2008-01-31 20:38:37 +00:00
{% extends "_layout.html" %}
2008-02-10 14:17:56 +00:00
{% block title %}Application Development{% endblock %}
2008-01-31 20:38:37 +00:00
{% block content %}
2008-02-10 14:17:56 +00:00
< h1 > Application Development Guide< / h1 >
2004-07-06 20:39:18 +00:00
2010-08-30 14:01:34 +00:00
< h2 > Contents< / h2 >
< ul >
< li > < a href = "#why" > Why write I2P-specific code?< / a > < / li >
< li > < a href = "#concepts" > Important concepts< / a > < / li >
< li > < a href = "#options" > Development options< / a > < / li >
< li > < a href = "#start" > < b > Start developing - a simple guide< / b > < / a > < / li >
< / ul >
< h2 id = "why" > Why write I2P-specific code?< / h2 >
< p >
There are multiple ways to use applications in I2P.
Using < a href = "/i2ptunnel.html" > I2PTunnel< / a > ,
you can use regular applications without needing to program explicit I2P support.
This is very effective for client-server scenario's,
where you need to connect to a single website.
2010-08-30 14:27:05 +00:00
You can simply create a tunnel using I2PTunnel to connect to that website, as shown in < a href = "#tunnel.serverclient" > Figure 1< / a > .
2010-08-30 14:01:34 +00:00
< / p >
< p >
If your application is distributed, it will require connections to a large amount of peers.
2010-08-30 14:27:05 +00:00
Using I2PTunnel, you will need to create a new tunnel for each peer you want to contact,
as shown in < a href = "#tunnel.peertopeer" > Figure 2< / a > .
2010-08-30 14:01:34 +00:00
This process can of course be automated, but running a lot of I2PTunnel instances creates a large amount of overhead.
In addition, with many protocols you will need to force everyone to
use the same set of ports for all peers - e.g. if you want to reliably run DCC
chat, everyone needs to agree that port 10001 is Alice, port 10002 is Bob, port
10003 is Charlie, and so on, since the protocol includes TCP/IP specific information
(host and port).
< / p >
< p >
General network applications often send a lot of additional data that could be used to identify users.
Hostnames, port numbers, time zones, character sets, etc. are often sent without informing the user.
As such, designing the network protocol specifically with anonymity in mind
can avoid compromising user identities.
< / p >
< p >
There are also efficiency considerations to review when determining how to
interact on top of I2P. The streaming library and things built on top of it
operate with handshakes similar to TCP, while the core I2P protocols (I2NP and I2CP)
are strictly message based (like UDP or in some instances raw IP). The important
distinction is that with I2P, communication is operating over a long fat network -
each end to end message will have nontrivial latencies, but may contain payloads
of up to 32KB. An application that needs a simple request and response can get rid
of any state and drop the latency incurred by the startup and teardown handshakes
by using (best effort) datagrams without having to worry about MTU detection or
fragmentation of messages under 32KB.
< / p >
2010-08-30 14:27:05 +00:00
< center >
< div class = "box" id = "tunnel.serverclient" >
< img src = "_static/images/i2ptunnel_serverclient.png" alt = "Creating a server-client connection using I2PTunnel only requires creating a single tunnel." title = "Creating a server-client connection using I2PTunnel only requires creating a single tunnel." / >
< br / > < br / >
Figure 1: Creating a server-client connection using I2PTunnel only requires creating a single tunnel.
< / div >
< / center > < br / >
< center >
< div class = "box" id = "tunnel.peertopeer" >
< img src = "_static/images/i2ptunnel_peertopeer.png" alt = "Setting up connections for a peer-to-peer applications requires a very large amount of tunnels." title = "Setting up connections for a peer-to-peer applications requires a very large amount of tunnels." / >
< br / > < br / >
Figure 2: Setting up connections for a peer-to-peer applications requires a very large amount of tunnels.
< / div >
< / center > < br / >
2010-08-30 14:01:34 +00:00
< p >
In summary, a number of reasons to write I2P-specific code:
< ul >
< li >
Creating a large amount of I2PTunnel instances consumes a non-trivial amount of resources,
which is problematic for distributed applications (a new tunnel is required for each peer).
< / li >
< li >
General network protocols often send a lot of additional data that can be used to identify users.
Programming specifically for I2P allows the creation of a network protocol
that does not leak such information, keeping users anonymous and secure.
< / li >
< li >
Network protocols designed for use on the regular internet can be inefficient
on I2P, which is a network with a much higher latency.
< / li >
< / ul >
< / p >
< p >
Applications written in Java and accessible/runnable
using an HTML interface via the standard webapps/app.war
may be considered for inclusion in the i2p distribution.
< / p >
< h2 > Important concepts< / h2 >
2004-07-06 20:39:18 +00:00
2004-07-06 21:38:20 +00:00
< p > There are a few changes that require adjusting to when using I2P:< / p >
2004-07-06 20:39:18 +00:00
< h3 > Destination ~= host+port< / h3 >
2004-07-06 21:38:20 +00:00
< p > An application running on I2P sends messages from and receives messages to a
2004-07-06 20:39:18 +00:00
unique cryptographically secure end point - a "destination". In TCP or UDP
2010-03-21 19:41:10 +00:00
terms, a destination could (largely) be considered the equivalent of a hostname
2004-07-06 20:39:18 +00:00
plus port number pair, though there are a few differences. < / p >
< ul >
2004-07-06 21:38:20 +00:00
< li > An I2P destination itself is a cryptographic construct - all data sent to one is
2004-07-06 20:39:18 +00:00
encrypted as if there were universal deployment of IPsec with the (anonymized)
location of the end point signed as if there were universal deployment of DNSSEC. < / li >
2004-07-06 21:38:20 +00:00
< li > I2P destinations are mobile identifiers - they can be moved from one I2P router
2004-07-06 20:39:18 +00:00
to another (or with some special software, it can even operate on multiple routers at
once). This is quite different from the TCP or UDP world where a single end point (port)
must stay on a single host.< / li >
2010-08-30 14:01:34 +00:00
< li >
< p >
I2P destinations are ugly and large - behind the scenes, they contain a 2048bit ElGamal
2004-07-06 20:39:18 +00:00
public key for encryption, a 1024bit DSA public key for signing, and a variable size
2010-08-30 14:01:34 +00:00
certificate, which may contain proof of work or blinded data.
< / p >
< p >
There are existing ways to refer to these large and ugly destinations by short
2004-07-06 21:38:20 +00:00
and pretty names (e.g. "irc.duck.i2p"), but at the moment those techniques do not guarantee
2004-07-06 20:39:18 +00:00
globally uniqueness (since they're stored locally at each person's machine as "hosts.txt")
2010-08-30 14:01:34 +00:00
and the current mechanism is not especially scalable nor secure (updates to one host file are
2008-02-10 14:17:56 +00:00
manually managed within Monotone, and as such, anyone with commit rights on the repository can
2004-07-06 20:39:18 +00:00
change the destinations). There may be some secure, human readable, scalable, and globally
unique, naming system some day, but applications shouldn't depend upon it being in place,
2008-02-10 14:17:56 +00:00
since there are those who don't think such a beast is possible.
< a href = "naming.html" > Further information on the naming system< / a > is available.
2010-08-30 14:01:34 +00:00
< / p >
2008-02-10 14:17:56 +00:00
< / li >
2004-07-06 20:39:18 +00:00
< / ul >
< h3 > Anonymity and confidentiality< / h3 >
2004-07-06 21:38:20 +00:00
< p > A useful thing to remember is that I2P has transparent end to end encryption
2010-08-30 14:01:34 +00:00
and authentication for all data passed over the network - if Bob sends to Alice's destination,
2004-07-06 20:39:18 +00:00
only Alice's destination can receive it, and if Bob is using the datagrams or streaming
library, Alice knows for certain that Bob's destination is the one who sent the data. < / p >
2004-07-06 21:38:20 +00:00
< p > Of course, another useful thing to remember is that I2P transparently anonymizes the
2004-07-06 20:39:18 +00:00
data sent between Alice and Bob, but it does nothing to anonymize the content of what they
send. For instance, if Alice sends Bob a form with her full name, government IDs, and
2004-07-06 21:38:20 +00:00
credit card numbers, there is nothing I2P can do. As such, protocols and applications should
2004-07-06 20:39:18 +00:00
keep in mind what information they are trying to protect and what information they are willing
to expose.< / p >
2004-07-06 21:38:20 +00:00
< h3 > I2P datagrams can be up to 32KB< / h3 >
2004-07-06 20:39:18 +00:00
2004-07-06 21:38:20 +00:00
< p > Applications that use I2P datagrams (either raw or repliable ones) can essentially be thought
2004-07-06 20:39:18 +00:00
of in terms of UDP - the datagrams are unordered, best effort, and connectionless - but unlike
UDP, applications don't need to worry about MTU detection and can simply fire off 32KB datagrams
(31KB when using the repliable kind). For many applications, 32KB of data is sufficient for an
2004-07-06 21:38:20 +00:00
entire request or response, allowing them to transparently operate in I2P as a UDP-like
2004-07-06 20:39:18 +00:00
application without having to write fragmentation, resends, etc.< / p >
2010-08-30 14:01:34 +00:00
< h2 id = "options" > Development options< / h2 >
2004-07-06 20:39:18 +00:00
2008-02-10 14:17:56 +00:00
< p > There are several means of sending data over I2P, each with their own pros and cons.
The streaming lib is the recommended interface, used by the majority of I2P applications.
< / p >
< h3 > Streaming Lib< / h3 >
< p >
The < a href = "streaming.html" > full streaming library< / a > is now the standard
2010-08-30 14:01:34 +00:00
interface. It allows programming using TCP-like sockets, as explained in the < a href = "#start.streaming" > Streaming development guide< / a > .
2008-02-10 14:17:56 +00:00
< / p >
2004-07-06 20:39:18 +00:00
2009-05-06 19:46:57 +00:00
< h3 > SAM, SAM V2, SAM V3< / h3 >
2004-07-06 20:39:18 +00:00
2010-03-21 19:41:10 +00:00
< p > < i > SAM is not recommended. SAM V2 is okay, SAM V3 is beta.< / i > < / p >
2004-07-21 00:17:23 +00:00
< p > SAM is the < a href = "sam" > Simple Anonymous Messaging< / a > protocol, allowing an
2004-07-06 20:39:18 +00:00
application written in any language to talk to a SAM bridge through a plain TCP socket and have
2004-07-06 21:38:20 +00:00
that bridge multiplex all of its I2P traffic, transparently coordinating the encryption/decryption
2004-07-06 20:39:18 +00:00
and event based handling. SAM supports three styles of operation:< / p >
< ul >
< li > streams, for when Alice and Bob want to send data to each other reliably and in order< / li >
< li > repliable datagrams, for when Alice wants to send Bob a message that Bob can reply to< / li >
< li > raw datagrams, for when Alice wants to squeeze the most bandwidth and performance as possible,
and Bob doesn't care whether the data's sender is authenticated or not (e.g. the data transferred
is self authenticating)< / li >
< / ul >
2009-05-06 19:46:57 +00:00
< p > SAM V3< p > aims at the same goal as SAM and SAM V2, but does not require
multiplexing/demultiplexing. Each I2P stream is handled by its own socket between the application
and the SAM bride. Besides, datagrams can be sent and received by the application through datagram
communications with the SAM bridge.
2008-02-10 14:17:56 +00:00
< / p >
< p >
< a href = "samv2.html" > SAM V2< / a > is a new version used by imule
that fixes some of the problems in < a href = "sam.html" > SAM< / a > .
2009-05-06 19:46:57 +00:00
< br / >
< a href = "samv3.html" > SAM V3< / a > is used by imule since version 1.4.0.
2008-02-10 14:17:56 +00:00
< / p >
2004-07-06 20:39:18 +00:00
< h3 > I2PTunnel< / h3 >
< p > The I2PTunnel application allows applications to build specific TCP-like tunnels to peers
by creating either I2PTunnel 'client' applications (which listen on a specific port and connect
2004-07-06 21:38:20 +00:00
to a specific I2P destination whenever a socket to that port is opened) or I2PTunnel 'server'
applications (which listen to a specific I2P destination and whenever it gets a new I2P
2004-07-06 20:39:18 +00:00
connection it outproxies to a specific TCP host/port). These streams are 8bit clean and are
authenticated and secured through the same streaming library that SAM uses, but there is a
nontrivial overhead involved with creating multiple unique I2PTunnel instances, since each have
2004-07-06 21:38:20 +00:00
their own unique I2P destination and their own set of tunnels, keys, etc.< / p >
2004-07-06 20:39:18 +00:00
2008-02-10 14:17:56 +00:00
< h3 > Ministreaming< / h3 >
< p > < i > Not recommended< / i > < / p >
2010-08-30 14:01:34 +00:00
< p >
It was possible to write I2P applications in Java using the ministreaming library.
However, the Streaming library has superceded this, and provides better functionality.
< / p >
2004-07-06 20:39:18 +00:00
2008-02-10 14:17:56 +00:00
< h3 > Datagrams< / h3 >
< p > < i > Not recommended< / i > < / p >
2010-08-30 14:01:34 +00:00
The < a href = "datagrams" > Datagram library< / a > allows sending UDP-like packets.
It's possible to use:
< ul >
< li > Repliable datagrams< / li >
< li > Raw datagrams< / li >
< / ul >
2004-07-06 20:39:18 +00:00
< h3 > I2CP< / h3 >
2010-08-30 14:01:34 +00:00
< p > < i > Not recommended< / i > < / p >
2004-07-21 00:17:23 +00:00
< p > < a href = "i2cp" > I2CP< / a > itself is a language independent protocol, but to implement an I2CP library
in something other than Java there is a significant amount of code to be written (encryption routines,
object marshalling, asynchronous message handling, etc). While someone could write an I2CP library in
2008-02-10 14:17:56 +00:00
C or something else, it would most likely be more useful to use the C SAM library instead.
< / p >
< h3 > Web Applications< / h3 >
I2P comes with the Jetty webserver, and configuring to use the Apache server instead is straightforward.
Any standard web app technology should work.
2010-08-30 14:01:34 +00:00
< h2 id = "start" > Start developing - a simple guide< / h2 >
Developing using I2P requires a working I2P installation and a development environment of your own choice.
If you are using Java, you can start development with the < a href = "#start.streaming" > streaming library< / a > or datagram library.
Using another programming language, SAM or BOB can be used.
< h3 id = "start.streaming" > Developing with the streaming library< / h3 >
< p >
Development using the streaming library requires the following libraries in your classpath:
< ul >
< li > $I2P/lib/streaming.jar: the streaming library itself.< / li >
< li > $I2P/lib/mstreaming.jar: the ministreaming library is used as the base for the streaming library.< / li >
< li > $I2P/lib/i2p.jar: some standard I2P classes (like the Destination class) are very convenient when developing.< / li >
< / ul >
< / p >
< p >
Network communication requires the usage of I2P network sockets.
To demonstrate this, we will create an application where a client can send text messages to a server,
who will print the messages and send them back to the client. In other words, the server will function as an echo.
< / p >
< p >
We will start by initializing the server application. This requires getting an I2PSocketManager
and creating an I2PServerSocket.
In addition, we will ask the I2PSocketManager for an I2PSession, so we can find out the Destination we use.
< / p >
< div class = "box" >
< pre >
package i2p.echoserver;
import net.i2p.client.I2PSession;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
public class Main {
public static void main(String[] args) {
//Initialize application
I2PSocketManager manager = I2PSocketManagerFactory.createManager();
I2PServerSocket serverSocket = manager.getServerSocket();
I2PSession session = manager.getSession();
System.out.println(session.getMyDestination().toBase64()); //Print the base64 string, the regular string would look like garbage.
//The additional main method code comes here...
}
}
< / pre >
< br / > < br / >
< center > Code example 1: initializing the server application.< / center >
< / div >
< p >
Once we have an I2PServerSocket, we can create I2PSocket instances to accept connections from clients.
In this example, we will create a single I2PSocket instance, that can only handle one client at a time.
A real server would have to be able to handle multiple clients.
To do this, multiple I2PSocket instances would have to be created, each in separate threads.
Once we have created the I2PSocket instance, we read data, print it and send it back to the client.
The bold code is the new code we add.
< / p >
< div class = "box" >
< pre >
package i2p.echoserver;
< / pre >
< b > < pre >
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.util.I2PThread;
< / pre > < / b >
< pre >
import net.i2p.client.I2PSession;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
public class Main {
public static void main(String[] args) {
I2PSocketManager manager = I2PSocketManagerFactory.createManager();
I2PServerSocket serverSocket = manager.getServerSocket();
I2PSession session = manager.getSession();
System.out.println(session.getMyDestination().toBase64()); //Print the base64 string, the regular string would look like garbage.
< / pre >
< b > < pre >
//Create socket to handle clients
I2PThread t = new I2PThread(new ClientHandler(serverSocket));
t.setName("clienthandler1");
t.setDaemon(false);
t.start();
}
private static class ClientHandler implements Runnable {
public ClientHandler(I2PServerSocket socket) {
this.socket = socket;
}
public void run() {
while(true) {
try {
I2PSocket sock = this.socket.accept();
if(sock != null) {
BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream())); //Receive from clients
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream())); //Send to clients
String line = br.readLine();
if(line != null) {
System.out.println("Received from client: " + line);
bw.write(line);
bw.flush(); //Flush to make sure everything got sent
}
sock.close();
}
} catch (I2PException ex) {
System.out.println("General I2P exception!");
} catch (ConnectException ex) {
System.out.println("Error connecting!");
} catch (SocketTimeoutException ex) {
System.out.println("Timeout!");
} catch (IOException ex) {
System.out.println("General read/write-exception!");
}
}
}
private I2PServerSocket socket;
}
}
< / pre > < / b >
< br / > < br / >
< center > Code example 2: accepting connections from clients and handling messages.< / center >
< / div >
< p >
When you run the above server code, it should print something like this (but without the line endings, it should just be
one huge block of characters):
< pre id = "start.streaming.destination" >
y17s~L3H9q5xuIyyynyWahAuj6Jeg5VC~Klu9YPquQvD4vlgzmxn4yy~5Z0zVvKJiS2Lk
poPIcB3r9EbFYkz1mzzE3RYY~XFyPTaFQY8omDv49nltI2VCQ5cx7gAt~y4LdWqkyk3au
6HdfYSLr45zxzWRGZnTXQay9HPuYcHysZHJP1lY28QsPz36DHr6IZ0vwMENQsnQ5rhq20
jkB3iheYJeuO7MpL~1xrjgKzteirkCNHvXN8PjxNmxe-pj3QgOiow-R9rEYKyPAyGd2pe
qMD-J12CGfB6MlnmH5qPHGdZ13bUuebHiyZ1jqSprWL-SVIPcynAxD2Uu85ynxnx31Fth
nxFMk07vvggBrLM2Sw82pxNjKDbtO8reawe3cyksIXBBkuobOZdyOxp3NT~x6aLOxwkEq
BOF6kbxV7NPRPnivbNekd1E1GUq08ltDPVMO1pKJuGMsFyZC4Q~osZ8nI59ryouXgn97Q
5ZDEO8-Iazx50~yUQTRgLMOTC5hqnAAAA
< / pre >
This is the base64-representation of the server Destination. The client will need this string to reach the server.
< / p >
< p >
Now, we will create the client application. Again, a number of steps are required for initialization.
Again, we will need to start by getting an I2PSocketManager.
We won't use an I2PSession and an I2PServerSocket this time.
Instead, we will use the server Destination string to start our connection.
We will ask the user for the Destination string, and create an I2PSocket using this string.
Once we have an I2PSocket, we can start sending and receiving data to and from the server.
< / p >
< div class = "box" >
< pre >
package i2p.echoclient;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
public class Main {
public static void main(String[] args) {
I2PSocketManager manager = I2PSocketManagerFactory.createManager();
System.out.println("Please enter a Destination:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String destinationString = null;
try {
destinationString = br.readLine();
} catch (IOException ex) {
System.out.println("Failed to get a Destination string.");
return;
}
Destination destination = null;
try {
destination = new Destination(destinationString);
} catch (DataFormatException ex) {
System.out.println("Destination string incorrectly formatted.");
return;
}
I2PSocket socket = null;
try {
socket = manager.connect(destination);
} catch (I2PException ex) {
System.out.println("General I2P exception occurred!");
} catch (ConnectException ex) {
System.out.println("Failed to connect!");
} catch (NoRouteToHostException ex) {
System.out.println("Couldn't find host!");
} catch (InterruptedIOException ex) {
System.out.println("Sending/receiving was interrupted!");
}
try {
//Write to server
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("Hello I2P!\n");
bw.flush(); //Flush to make sure everything got sent
//Read from server
BufferedReader br2 = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = null;
while ((s = br2.readLine()) != null) {
System.out.println("Received from server: " + s);
}
socket.close();
} catch (IOException ex) {
System.out.println("Error occurred while sending/receiving!");
}
}
}
< / pre >
< br / > < br / >
< center > Code example 3: starting the client and connecting it to the server application.< / center >
< / div >
< p >
Finally, you can run both the server and the client application.
First, start the server application. It will print a Destination string (like shown < a href = "#start.streaming.destination" > above< / a > ).
Next, start the client application. When it requests a Destination string, you can enter the string printed by the server.
The client will then send 'Hello I2P!' (along with a newline) to the server, who will print the message and send it back to the client.
< / p >
< p >
Congratulations, you have successfully communicated over I2P!
< / p >
2008-02-10 14:17:56 +00:00
< h2 > Existing Applications in Development< / h2 >
Contact us if you would like to help.
< ul >
< li >
2011-04-05 22:30:16 +00:00
< a href = "http://i2pbote.i2p/" > I2P-Bote< / a > - contact HungryHobo
< / li > < li >
2011-04-05 22:36:15 +00:00
< a href = "http://syndie.i2p2.de/" > Syndie< / a >
< / li > < li >
2011-04-05 22:30:16 +00:00
< a href = "http://www.imule.i2p/" > IMule< / a >
2011-02-27 02:59:28 +00:00
< / li > < li >
2008-07-15 23:11:50 +00:00
< a href = "http://forum.i2p/viewforum.php?f=25" > I2Phex< / a > - contact Complication
2008-03-16 22:49:54 +00:00
< a href = "http://forum.i2p2.de/viewforum.php?f=25" > (outside I2P)< / a >
2011-02-27 02:59:28 +00:00
< / li > < li >
2008-02-10 14:17:56 +00:00
< a href = "http://www.imule.i2p/" > IMule< / a >
2011-02-27 02:59:28 +00:00
< / li > < li > I2PRufus - contact codevoid
< / li > < li > I2P-BT - contact sponge
< / li > < li > BOB - contact sponge
< / li > < / ul >
2008-02-10 14:17:56 +00:00
< h2 > Application Ideas< / h2 >
< ul >
< li > NNTP server - there have been some in the past, none at the moment
2011-04-05 22:30:16 +00:00
< / li > < li > Jabber server - there have been some in the past, and there is one at the moment, with access to the public internet
2011-02-27 02:59:28 +00:00
< / li > < li > PGP Key server and/or proxy
< / li > < li > Download manager / eepget scheduler -
2008-02-10 14:17:56 +00:00
We use eepget to fetch lots of things reliably over i2p, and there's already an
implementation of a sequential download manager (net.i2p.util.EepGetScheduler),
but there isn't any sort of user interface to it. A web based UI would be
great.
2011-02-27 02:59:28 +00:00
< / li > < li > Content Distribution / DHT applications - help out with < a href = "http://feedspace.i2p/" > feedspace< / a > ,
2008-04-26 21:41:15 +00:00
port dijjer, look for alternatives
2011-02-27 02:59:28 +00:00
< / li > < li > Help out with < a href = "http://syndie.i2p2.de/" > Syndie< / a > development
< / li > < li > Web-based applications - The sky is the limit for hosting web-server-based
2008-02-10 14:17:56 +00:00
applications such as blogs, pastebins, storage, tracking, feeds, etc.
2010-03-21 19:41:10 +00:00
Any web or CGI technology such as Perl, PHP, Python, or Ruby will work.
2011-02-27 02:59:28 +00:00
< / li > < li > Resurrect some old apps - in the i2p source package -
2008-07-15 23:11:50 +00:00
bogobot, pants, proxyscript, q, stasher, socks proxy, i2ping
2011-02-27 02:59:28 +00:00
< / li > < / ul >
2008-02-10 14:17:56 +00:00
{% endblock %}