1 how it works
Sam Bent edited this page 2026-07-17 15:15:52 -04:00

How it works

This page explains what i2pforge is doing under the hood, in plain terms. You do not need any of this to use it, but it helps to understand where your address comes from and why it is safe.

What an I2P address is

I2P is a network that sits on top of the normal internet and hides who is talking to whom. Services on I2P do not have IP addresses that the outside world can see. Instead they have a long address that ends in .b32.i2p, like naclle4rqyzzwi2q7brg46i64kzjvjmm7bbti2cczdrtym6d37wq.b32.i2p.

That address is not assigned to you by anyone. It is derived from a cryptographic key that your container generates. The key is called a destination. The address is just a fingerprint of that key: take the key, run it through a standard hash, and encode the result. Nobody else can produce your address without your key, and your key never leaves the folder you back up.

Because the address comes from the key and nothing else, i2pforge can print your address the moment the key exists. It does not have to ask any server or register anything. That is also why there is no account to lose and no way for anyone to take the address from you.

How your service becomes reachable

Inside the container runs a program called i2pd. It is a mature I2P router written in C++. i2pd builds encrypted paths through the I2P network, called tunnels, and tells the network "this destination is reachable through these tunnels." That announcement is called a leaseset. Once it is published, anyone who knows your .b32.i2p address can reach your service.

When a visitor connects to your address, their request travels through the I2P network and arrives at your container. i2pforge forwards it to the backend you configured, for example your web server on web:80. Your backend answers, the reply travels back the same way, and the visitor sees your page. Your backend never learns the visitor's real location, and the visitor never learns yours.

How you tell it what to publish

You describe your service with one environment variable. The simple form is:

I2P_EEPSITE = "host:port"

That sets up an HTTP service, called an eepsite in I2P terms, pointing at host:port. For a non-web service, such as SSH or an RPC port, use I2P_BACKEND instead, which forwards raw TCP.

For more than one service, or more than one port, use the full form:

<NAME>_I2P_SERVICE_HOSTS = "i2p_port:backend_host:backend_port, ..."

At startup, i2pforge reads these variables, checks them against strict rules, resolves the backend name to an IP address on the Docker network, and writes i2pd's tunnel configuration. Pinning the backend to an IP at startup means a later DNS change cannot quietly repoint your tunnel somewhere else.

Why the container is safe to run

i2pforge is deliberately small and deliberately boxed in.

  • It opens no management interface. I2P has optional bridges and control ports for apps that want to register tunnels while running. i2pforge turns all of them off. There is nothing to connect to and nothing to attack, because every tunnel is declared up front and the address is computed locally.
  • The router runs as an ordinary user with no special privileges. The one part that briefly needs to be root only sets up the key folder, then drops down.
  • The container's own filesystem is read-only. The few places it must write are small in-memory areas that cannot hold programs or survive a restart.
  • Your backend sits on a private network with no route to the internet, so even if the backend were compromised it could not phone home.

What is stored, and where

Only one thing is worth keeping: the destination key. It lives in the folder you mount at ./i2p-keys, as eepsite.dat, with the public address next to it as eepsite.b32.i2p. Everything else the router needs is temporary and is rebuilt on each start. Back up that one folder and you can move your service to any machine and keep the same address. See Backing up your key.