Overview
I2CP is the low-level control protocol between an I2P router and any client process. It defines a strict separation of responsibilities:
- Router: Manages routing, cryptography, tunnel lifecycles, and network database operations
- Client: Selects anonymity properties, configures tunnels, and submits/receives messages
All communication flows over a single TCP socket (optionally TLS-wrapped), enabling asynchronous, full-duplex operations.
Protocol Version: I2CP uses a protocol version byte 0x2A (42 decimal) sent during initial connection establishment. This version byte has remained stable since the protocol’s inception.
Current Status: This specification is accurate for router version 0.9.67 (API version 0.9.67), released 2025-09.
Implementation Context
Java Implementation
The reference implementation is in Java I2P:
- Client SDK:
i2p.jarpackage - Router implementation:
router.jarpackage - Javadocs
When client and router run in the same JVM, I2CP messages are passed as Java objects without serialization. External clients use the serialized protocol over TCP.
C++ Implementation
i2pd (the C++ I2P router) also implements I2CP externally for client connections.
Non-Java Clients
There are no known non-Java implementations of a complete I2CP client library. Non-Java applications should use higher-level protocols instead:
- SAM (Simple Anonymous Messaging) v3: Socket-based interface with libraries in multiple languages
- BOB (Basic Open Bridge): Simpler alternative to SAM
These higher-level protocols handle I2CP complexity internally and also provide the streaming library (for TCP-like connections) and datagram library (for UDP-like connections).
Connection Establishment
1. TCP Connection
Connect to the router’s I2CP port:
- Default:
127.0.0.1:7654 - Configurable via router settings
- Optional TLS wrapper (strongly recommended for remote connections)
2. Protocol Handshake
Step 1: Send protocol version byte 0x2A
Step 2: Clock Synchronization
Client → Router: GetDateMessage
Router → Client: SetDateMessage
The router returns its current timestamp and I2CP API version string (since 0.8.7).
Step 3: Authentication (if enabled)
As of 0.9.11, authentication may be included in GetDateMessage via a Mapping containing:
i2cp.usernamei2cp.password
As of 0.9.16, when authentication is enabled, it must be completed via GetDateMessage before any other messages are sent.
Step 4: Session Creation
Client → Router: CreateSessionMessage (contains SessionConfig)
Router → Client: SessionStatusMessage (status=Created)
Step 5: Tunnel Ready Signal
Router → Client: RequestVariableLeaseSetMessage
This message signals that inbound tunnels have been built. The router will NOT send this until at least one inbound AND one outbound tunnel exist.
Step 6: LeaseSet Publication
Client → Router: CreateLeaseSet2Message
At this point, the session is fully operational for sending and receiving messages.
Message Flow Patterns
Outgoing Message (Client sends to remote destination)
With i2cp.messageReliability=none:
Client → Router: SendMessageMessage (nonce=0)
[No acknowledgments]
With i2cp.messageReliability=BestEffort:
Client → Router: SendMessageMessage (nonce>0)
Router → Client: MessageStatusMessage (status=Accepted)
Router → Client: MessageStatusMessage (status=Success or Failure)
Incoming Message (Router delivers to client)
With i2cp.fastReceive=true (default since 0.9.4):
Router → Client: MessagePayloadMessage
[No acknowledgment required]
With i2cp.fastReceive=false (DEPRECATED):
Router → Client: MessageStatusMessage (status=Available)
Client → Router: ReceiveMessageBeginMessage
Router → Client: MessagePayloadMessage
Client → Router: ReceiveMessageEndMessage
Modern clients should always use fast receive mode.
Common Data Structures
I2CP Message Header
All I2CP messages use this common header:
+----+----+----+----+----+----+----+----+
| Body Length (4 bytes) |
+----+----+----+----+----+----+----+----+
|Type| Message Body (variable) |
+----+----+----+----+----+----+----+----+
- Body Length: 4-byte integer, length of message body only (excludes header)
- Type: 1-byte integer, message type identifier
- Message Body: 0+ bytes, format varies by message type
Message Size Limit: Approximately 64 KB maximum.
Session ID
2-byte integer uniquely identifying a session on a router.
Special Value: 0xFFFF indicates “no session” (used for hostname lookups without an established session).
Message ID
4-byte integer generated by the router to uniquely identify a message within a session.
Important: Message IDs are not globally unique, only unique within a session. They are also distinct from the nonce generated by the client.
Payload Format
Message payloads are gzip-compressed with a standard 10-byte gzip header:
- Starts with:
0x1F 0x8B 0x08(RFC 1952) - Since 0.7.1: Unused portions of gzip header contain protocol, from-port, and to-port information
- This enables streaming and datagrams on the same destination
Compression Control: Set i2cp.gzip=false to disable compression (sets gzip effort to 0). The gzip header is still included, but with minimal compression overhead.
SessionConfig Structure
Defines configuration for a client session:
+----------------------------------+
| Destination |
+----------------------------------+
| Mapping (configuration options) |
+----------------------------------+
| Creation Date |
+----------------------------------+
| Signature |
+----------------------------------+
Critical Requirements:
- Mapping must be sorted by key for signature validation
- Creation Date must be within ±30 seconds of router’s current time
- Signature is created by the SigningPrivateKey of the Destination
Offline Signatures (as of 0.9.38):
If using offline signing, the Mapping must contain:
i2cp.leaseSetOfflineExpirationi2cp.leaseSetTransientPublicKeyi2cp.leaseSetOfflineSignature
The Signature is then generated by the transient SigningPrivateKey.
Core Configuration Options
Tunnel Configuration
| Option | Default | Description |
|---|---|---|
inbound.length | 3 | Number of hops for inbound tunnels |
outbound.length | 3 | Number of hops for outbound tunnels |
inbound.lengthVariance | 0 | Random variance in hop count (since 0.7.6) |
outbound.lengthVariance | 0 | Random variance in hop count (since 0.7.6) |
inbound.quantity | 2 | Number of concurrent inbound tunnels |
outbound.quantity | 2 | Number of concurrent outbound tunnels |
inbound.backupQuantity | 0 | Standby inbound tunnels (hot spares) |
outbound.backupQuantity | 0 | Standby outbound tunnels (hot spares) |
inbound.allowZeroHop | true | Allow 0-hop tunnels (disable for full anonymity) |
outbound.allowZeroHop | true | Allow 0-hop tunnels (disable for full anonymity) |
Notes:
- Values for
quantity> 6 require peers running 0.9.0+ and significantly increase resource usage - Set
backupQuantityto 1-2 for high-availability services - Zero-hop tunnels sacrifice anonymity for latency but are useful for testing
Message Handling
| Option | Default | Description |
|---|---|---|
clientMessageTimeout | 60000 ms | Legacy timeout for message delivery |
i2cp.messageReliability | BestEffort | None, BestEffort, or Guaranteed |
i2cp.fastReceive | true | Skip ReceiveMessageBegin/End handshake (default since 0.9.4) |
i2cp.gzip | true | Enable gzip compression of message payloads |
outbound.priority | 0 | Priority for outbound scheduling (-25 to +25) |
Message Reliability:
None: No router acknowledgments (streaming library default since 0.8.1)BestEffort: Router sends acceptance + success/failure notificationsGuaranteed: Unimplemented (currently behaves like BestEffort)
Per-Message Override (since 0.9.14):
- In a session with
messageReliability=none, setting a nonzero nonce requests delivery notification for that specific message - Setting nonce=0 in a
BestEffortsession disables notifications for that message
LeaseSet Configuration
| Option | Default | Description |
|---|---|---|
i2cp.dontPublishLeaseSet | false | Disable automatic LeaseSet publication (for client-only destinations) |
i2cp.leaseSetType | 1 | LeaseSet variant: 1 = standard, 3 = LS2, 5 = encrypted, 7 = meta |
i2cp.leaseSetEncType | 0 | Comma-separated encryption type codes (see below) |
Legacy ElGamal/AES Session Tags
These options are relevant only for legacy ElGamal encryption:
| Option | Default | Description |
|---|---|---|
crypto.lowTagThreshold | 30 | Minimum session tags before replenishing |
crypto.tagsToSend | 40 | Number of tags to send in a batch |
Note: ECIES-X25519 clients use a different ratchet mechanism and ignore these options.
Encryption Types
I2CP supports multiple end-to-end encryption schemes via the i2cp.leaseSetEncType option. Multiple types can be specified (comma-separated) to support both modern and legacy peers.
Supported Encryption Types
| Type | Algorithm | Key Size | Since | Status |
|---|---|---|---|---|
| 0 | ElGamal/AES+SessionTags | 2048-bit ElGamal | Original | Legacy |
| 1-3 | Reserved | - | - | Unused |
| 4 | ECIES-X25519-AEAD-Ratchet | 32-byte X25519 | 0.9.46 | Current Standard |
| 5 | ECIES-X25519-AEAD-Ratchet + ML-KEM-768 hybrid | 32 + PQ | 0.9.67 | Beta |
| 6 | ECIES-X25519-AEAD-Ratchet + ML-KEM-1024 hybrid | 32 + PQ | 0.9.67 | Beta |
| 7 | Reserved (likely ML-KEM-512 hybrid) | 32 + PQ | Future | Planned |
Recommended Configuration:
i2cp.leaseSetEncType=4,0
This provides X25519 (preferred) with ElGamal fallback for compatibility.
Encryption Type Details
Type 0 - ElGamal/AES+SessionTags:
- 2048-bit ElGamal public keys (256 bytes)
- AES-256 symmetric encryption
- 32-byte session tags sent in batches
- High CPU, bandwidth, and memory overhead
- Being phased out network-wide
Type 4 - ECIES-X25519-AEAD-Ratchet:
- X25519 key exchange (32-byte keys)
- ChaCha20/Poly1305 AEAD
- Signal-style double ratchet
- 8-byte session tags (vs 32-byte for ElGamal)
- Tags generated via synchronized PRNG (not sent in advance)
- ~92% overhead reduction vs ElGamal
- Standard for modern I2P (most routers use this)
Types 5-6 - Post-Quantum Hybrid:
- Combines X25519 with ML-KEM (NIST FIPS 203)
- Provides quantum-resistant security
- ML-KEM-768 for balanced security/performance
- ML-KEM-1024 for maximum security
- Larger message sizes due to PQ key material
- Network support still being deployed
Migration Strategy
The I2P network is actively migrating from ElGamal (type 0) to X25519 (type 4):
- NTCP → NTCP2 (complete)
- SSU → SSU2 (complete)
- ElGamal tunnels → X25519 tunnels (complete)
- ElGamal end-to-end → ECIES-X25519 (majority complete)
LeaseSet2 and Advanced Features
LeaseSet2 Options (since 0.9.38)
| Option | Since | Purpose |
|---|---|---|
i2cp.leaseSetType | 0.9.38 | Specifies LeaseSet variant (1, 3, 5, 7) |
i2cp.leaseSetEncType | 0.9.38 | Encryption types supported (comma-separated) |
i2cp.leaseSetAuthType | 0.9.41 | Per-client authentication: 0 = none, 1 = DH, 2 = PSK |
i2cp.leaseSetPrivKey | 0.9.41 | X25519 private key for decrypting LS2 with auth |
i2cp.leaseSetSecret | 0.9.39 | Base64 secret for blinded addresses |
i2cp.leaseSetTransientPublicKey | 0.9.38 | Transient signing key for offline signatures |
i2cp.leaseSetPrivateKey | 0.9.18 | Persistent LeaseSet encryption keys (type:key pairs) |
i2cp.leaseSetOption.nnn | 0.9.66 | Service records (proposal 167) |
i2cp.leaseSetClient.dh.nnn | 0.9.41 | DH client auth material (indexed from 0) |
i2cp.leaseSetClient.psk.nnn | 0.9.41 | PSK client auth material (indexed from 0) |
Blinded Addresses
As of 0.9.39, destinations can use “blinded” addresses (b33 format) that change periodically:
- Requires
i2cp.leaseSetSecretfor password protection - Optional per-client authentication
- See proposals 123 and 149 for details
Service Records (since 0.9.66)
LeaseSet2 supports service record options (proposal 167):
i2cp.leaseSetOption.0=_smtp._tcp=1 86400 0 0 25 mail.example.b32.i2p
Format follows DNS SRV record style but adapted for I2P.
Multiple Sessions (since 0.9.21)
A single I2CP connection can maintain multiple sessions:
Primary Session: The first session created on a connection Subsessions: Additional sessions sharing the primary’s tunnel pool
Subsession Characteristics
- Shared Tunnels: Use the same inbound/outbound tunnel pools as primary
- Shared Encryption Keys: Must use identical LeaseSet encryption keys
- Different Signing Keys: Must use distinct Destination signing keys
- No Anonymity Guarantee: Clearly linked to primary session (same router, same tunnels)
Subsession Use Case
Enable communication with destinations using different signature types:
- Primary: EdDSA signature (modern)
- Subsession: DSA signature (legacy compatibility)
Subsession Lifecycle
Creation:
Client → Router: CreateSessionMessage
Router → Client: SessionStatusMessage (unique Session ID)
Router → Client: RequestVariableLeaseSetMessage (separate for each destination)
Client → Router: CreateLeaseSet2Message (separate for each destination)
Destruction:
- Destroying a subsession: Leaves primary session intact
- Destroying primary session: Destroys all subsessions and closes connection
- DisconnectMessage: Destroys all sessions
Session ID Handling
Most I2CP messages contain a Session ID field. Exceptions:
- DestLookup / DestReply (deprecated, use HostLookup / HostReply)
- GetBandwidthLimits / BandwidthLimits (response not session-specific)
Important: Clients should not have multiple CreateSession messages outstanding simultaneously, as responses cannot be definitively correlated to requests.
Message Catalog
Message Type Summary
| Type | Name | Direction | Since | Status |
|---|---|---|---|---|
| 1 | CreateSession | C → R | Original | Current |
| 2 | ReconfigureSession | C → R | 0.7.1 | Current |
| 3 | DestroySession | C → R | Original | Current |
| 4 | CreateLeaseSet | C → R | Original | Deprecated |
| 5 | SendMessage | C → R | Original | Current |
| 6 | ReceiveMessageBegin | C → R | Original | Deprecated |
| 7 | ReceiveMessageEnd | C → R | Original | Deprecated |
| 8 | GetBandwidthLimits | C → R | 0.7.2 | Current |
| 20 | SessionStatus | R → C | Original | Current |
| 21 | RequestLeaseSet | R → C | Original | Deprecated |
| 22 | MessageStatus | R → C | Original | Current |
| 23 | BandwidthLimits | R → C | 0.7.2 | Current |
| 29 | ReportAbuse | Bidirectional | Original | Unused |
| 30 | Disconnect | Bidirectional | Original | Current |
| 31 | MessagePayload | R → C | Original | Current |
| 32 | GetDate | C → R | Original | Current |
| 33 | SetDate | R → C | Original | Current |
| 34 | DestLookup | C → R | 0.7 | Deprecated |
| 35 | DestReply | R → C | 0.7 | Deprecated |
| 36 | SendMessageExpires | C → R | 0.7.1 | Current |
| 37 | RequestVariableLeaseSet | R → C | 0.9.7 | Current |
| 38 | HostLookup | C → R | 0.9.11 | Current |
| 39 | HostReply | R → C | 0.9.11 | Current |
| 41 | CreateLeaseSet2 | C → R | 0.9.39 | Current |
| 42 | BlindingInfo | C → R | 0.9.43 | Current |
Legend: C = Client, R = Router
Key Message Details
CreateSessionMessage (Type 1)
Purpose: Initiate a new I2CP session
Content: SessionConfig structure
Response: SessionStatusMessage (status=Created or Invalid)
Requirements:
- Date in SessionConfig must be within ±30 seconds of router time
- Mapping must be sorted by key for signature validation
- Destination must not already have an active session
RequestVariableLeaseSetMessage (Type 37)
Purpose: Router requests client authorization for inbound tunnels
Content:
- Session ID
- Number of leases
- Array of Lease structures (each with individual expiration)
Response: CreateLeaseSet2Message
Significance: This is the signal that the session is operational. The router sends this only after:
- At least one inbound tunnel is built
- At least one outbound tunnel is built
Timeout Recommendation: Clients should destroy the session if this message is not received within 5+ minutes of session creation.
CreateLeaseSet2Message (Type 41)
Purpose: Client publishes LeaseSet to network database
Content:
- Session ID
- LeaseSet type byte (1, 3, 5, or 7)
- LeaseSet or LeaseSet2 or EncryptedLeaseSet or MetaLeaseSet
- Number of private keys
- Private key list (one per public key in LeaseSet, same order)
Private Keys: Required for decrypting incoming garlic messages. Format:
Encryption type (2 bytes)
Key length (2 bytes)
Private key data (variable)
Note: Replaces deprecated CreateLeaseSetMessage (type 4), which cannot handle:
- LeaseSet2 variants
- Non-ElGamal encryption
- Multiple encryption types
- Encrypted LeaseSets
- Offline signing keys
SendMessageExpiresMessage (Type 36)
Purpose: Send message to destination with expiration and advanced options
Content:
- Session ID
- Destination
- Payload (gzipped)
- Nonce (4 bytes)
- Flags (2 bytes) - see below
- Expiration Date (6 bytes, truncated from 8)
Flags Field (2 bytes, bit order 15…0):
Bits 15-11: Unused, must be 0
Bits 10-9: Message Reliability Override (unused, use nonce instead)
Bit 8: Don’t bundle LeaseSet
- 0: Router may bundle LeaseSet in garlic
- 1: Don’t bundle LeaseSet
Bits 7-4: Low tag threshold (ElGamal only, ignored for ECIES)
0000 = Use session settings
0001 = 2 tags
0010 = 3 tags
...
1111 = 192 tags
Bits 3-0: Tags to send if needed (ElGamal only, ignored for ECIES)
0000 = Use session settings
0001 = 2 tags
0010 = 4 tags
...
1111 = 160 tags
MessageStatusMessage (Type 22)
Purpose: Notify client of message delivery status
Content:
- Session ID
- Message ID (router-generated)
- Status code (1 byte)
- Size (4 bytes, only relevant for status=0)
- Nonce (4 bytes, matches client’s SendMessage nonce)
Status Codes (Outgoing Messages):
| Code | Name | Meaning | Result |
|---|---|---|---|
| 1 | Accepted | Router accepted message | Success |
| 2 | Best Effort Success | Probable delivery | Success |
| 4 | Guaranteed Success | Probable delivery | Success |
| 6 | Local Success | Delivered to local client | Success |
| 3 | Best Effort Failure | Probable failure | Failure |
| 5 | Guaranteed Failure | Generic failure | Failure |
| 7 | Local Failure | Local delivery failed | Failure |
| 8 | Router Failure | Router shutdown/error | Failure |
| 9 | Network Failure | No network connectivity | Failure |
| 10 | Bad Session | Invalid/closed session | Failure |
| 11 | Bad Message | Invalid payload | Failure |
| 12 | Bad Options | Invalid options/expiration | Failure |
| 13 | Overflow Failure | Queue/buffer full | Failure |
| 14 | Message Expired | Expired before send | Failure |
| 15 | Bad Local LeaseSet | Local LeaseSet problem | Failure |
| 16 | No Local Tunnels | No tunnels available | Failure |
| 17 | Unsupported Encryption | Incompatible encryption | Failure |
| 18 | Bad Destination | Invalid remote destination | Failure |
| 19 | Bad Leaseset | Invalid remote LeaseSet | Failure |
| 20 | Expired Leaseset | Remote LeaseSet expired | Failure |
| 21 | No Leaseset | Remote LeaseSet not found | Failure |
| 22 | Meta Leaseset | Cannot send to meta LS | Failure |
| 23 | Loopback Denied | Same source and destination | Failure |
Success codes: 1, 2, 4, 6 Failure codes: All others
Status Code 0 (DEPRECATED): Available message (incoming, fast receive disabled)
HostLookupMessage (Type 38)
Purpose: Lookup destination by hostname or hash (replaces DestLookup)
Content:
- Session ID (or 0xFFFF for no session)
- Request ID (4 bytes)
- Timeout in milliseconds (4 bytes, min recommended: 10000)
- Request type (1 byte)
- Lookup key (Hash, hostname String, or Destination)
Request Types:
| Type | Lookup Key | Returns | Since |
|---|---|---|---|
| 0 | Hash | Destination | Original |
| 1 | Hostname String | Destination | Original |
| 2 | Hash | Destination + Options | 0.9.66 |
| 3 | Hostname String | Destination + Options | 0.9.66 |
| 4 | Destination | Destination + Options | 0.9.66 |
Types 2-4 return LeaseSet options (proposal 167) if available.
Response: HostReplyMessage
HostReplyMessage (Type 39)
Purpose: Response to HostLookupMessage
Content:
- Session ID
- Request ID
- Result code (1 byte)
- Destination (present on success, sometimes on specific failures)
- Mapping (only for lookup types 2-4, may be empty)
Result Codes:
| Code | Name | Meaning |
|---|---|---|
| 0 | Success | Lookup succeeded |
| 1 | Failure | Generic failure |
| 2 | Lookup Password Required | Blinded address requires password |
| 3 | Private Key Required | Blinded address requires private key |
| 4 | Password and Key Required | Blinded address requires both |
| 5 | LeaseSet Decryption Failure | Cannot decrypt LeaseSet |
| 6 | LeaseSet Lookup Failure | LeaseSet not found in netdb |
| 7 | Lookup Type Unsupported | Router doesn't support this type |
BlindingInfoMessage (Type 42)
Purpose: Inform router about blinded destination authentication requirements (since 0.9.43)
Content:
- Session ID
- Flags (1 byte)
- Endpoint type (1 byte): 0=Hash, 1=hostname, 2=Destination, 3=SigType+Key
- Blinded signature type (2 bytes)
- Expiration (4 bytes, seconds since epoch)
- Endpoint data (varies by type)
- Private key (32 bytes, only if flag bit 0 set)
- Lookup password (String, only if flag bit 4 set)
Flags (bit order 76543210):
- Bit 0: 0=everybody, 1=per-client
- Bits 3-1: Auth scheme (if bit 0=1): 000=DH, 001=PSK
- Bit 4: 1=secret required
- Bits 7-5: Unused, set to 0
No Response: Router processes silently
Use Case: Before sending to a blinded destination (b33 address), client must either:
- Lookup the b33 via HostLookup, OR
- Send BlindingInfo message
If the destination requires authentication, BlindingInfo is mandatory.
ReconfigureSessionMessage (Type 2)
Purpose: Update session configuration after creation
Content:
- Session ID
- SessionConfig (only changed options needed)
Response: SessionStatusMessage (status=Updated or Invalid)
Notes:
- Router merges new config with existing config
- Tunnel options (
inbound.*,outbound.*) are always applied - Some options may be immutable after session creation
- Date must be within ±30 seconds of router time
- Mapping must be sorted by key
DestroySessionMessage (Type 3)
Purpose: Terminate a session
Content: Session ID
Expected Response: SessionStatusMessage (status=Destroyed)
Actual Behavior (Java I2P through 0.9.66):
- Router never sends SessionStatus(Destroyed)
- If no sessions remain: Sends DisconnectMessage
- If subsessions remain: No reply
Important: Java I2P behavior deviates from specification. Implementations should be cautious when destroying individual subsessions.
DisconnectMessage (Type 30)
Purpose: Notify that connection is about to be terminated
Content: Reason String
Effect: All sessions on the connection are destroyed, socket closes
Implementation: Primarily router → client in Java I2P
Protocol Version History
Version Detection
The I2CP protocol version is exchanged in Get/SetDate messages (since 0.8.7). For older routers, version information is unavailable.
Version String: Indicates “core” API version, not necessarily router version.
Feature Timeline
| Version | Key Features |
|---|---|
| 0.9.67 | PQ Hybrid ML-KEM (enc types 5-7) in LeaseSet |
| 0.9.66 | Host lookup/reply extensions (proposal 167), service records |
| 0.9.62 | MessageStatus loopback error code |
| 0.9.46 | X25519 (enc type 4) in LeaseSet, ECIES end-to-end |
| 0.9.43 | BlindingInfo message, extended HostReply failure codes |
| 0.9.41 | EncryptedLeaseSet options, Meta LS error code |
| 0.9.39 | CreateLeaseSet2 message, RedDSA Ed25519 support |
| 0.9.38 | Preliminary LS2 support (format changed in 0.9.39) |
| 0.9.21 | Multiple sessions on single connection |
| 0.9.20 | Additional SetDate messages for clock shifts |
| 0.9.16 | Authentication required before other messages (when enabled) |
| 0.9.15 | EdDSA Ed25519 signature type |
| 0.9.14 | Per-message reliability override with nonzero nonce |
| 0.9.12 | ECDSA P-256/384/521 signature types, RSA support |
| 0.9.11 | HostLookup/HostReply messages, auth in GetDate |
| 0.9.7 | RequestVariableLeaseSet message |
| 0.9.5 | Additional MessageStatus codes |
| 0.9.4 | Fast receive mode default, nonce=0 allowed |
| 0.9.2 | SendMessageExpires flag tag bits |
| 0.9 | 16 leases per LeaseSet (up from 6) |
| 0.8.7 | Version strings in Get/SetDate |
| 0.8.4 | SendMessageExpires flag bits |
| 0.8.3 | DestLookup in standard session, concurrent lookups |
| 0.8.1 | messageReliability=none |
| 0.7.2 | GetBandwidthLimits, BandwidthLimits |
| 0.7.1 | SendMessageExpires, ReconfigureSession, ports in gzip header |
| 0.7 | DestLookup, DestReply |
| 0.6.5- | Original protocol features |
Security Considerations
Authentication
Default: No authentication required Optional: Username/password authentication (since 0.9.11) Required: When enabled, auth must complete before other messages (since 0.9.16)
Remote Connections: Always use TLS (i2cp.SSL=true) to protect credentials and private keys.
Clock Skew
SessionConfig Date must be within ±30 seconds of router time, or session will be rejected. Use Get/SetDate to synchronize.
Private Key Handling
CreateLeaseSet2Message contains private keys for decrypting incoming messages. These keys must be:
- Transmitted securely (TLS for remote connections)
- Stored securely by the router
- Rotated when compromised
Message Expiration
Always use SendMessageExpires (not SendMessage) to set explicit expiration. This:
- Prevents messages from being queued indefinitely
- Reduces resource consumption
- Improves reliability
Session Tag Management
ElGamal (deprecated):
- Tags must be transmitted in batches
- Lost tags cause decryption failures
- High memory overhead
ECIES-X25519 (current):
- Tags generated via synchronized PRNG
- No advance transmission needed
- Resilient to message loss
- Significantly lower overhead
Best Practices
For Client Developers
Use Fast Receive Mode: Always set
i2cp.fastReceive=true(or rely on default)Prefer ECIES-X25519: Configure
i2cp.leaseSetEncType=4,0for best performance with compatibilitySet Explicit Expiration: Use SendMessageExpires, not SendMessage
Handle Subsessions Carefully: Be aware that subsessions offer no anonymity between destinations
Timeout Session Creation: Destroy session if RequestVariableLeaseSet not received within 5 minutes
Sort Configuration Mappings: Always sort Mapping keys before signing SessionConfig
Use Appropriate Tunnel Counts: Don’t set
quantity> 6 unless necessaryConsider SAM/BOB for Non-Java: Implement SAM rather than I2CP directly
For Router Developers
Validate Dates: Enforce ±30 second window on SessionConfig dates
Limit Message Size: Enforce ~64 KB maximum message size
Support Multiple Sessions: Implement subsession support per 0.9.21 spec
Send RequestVariableLeaseSet Promptly: Only after both inbound and outbound tunnels exist
Handle Deprecated Messages: Accept but discourage ReceiveMessageBegin/End
Support ECIES-X25519: Prioritize type 4 encryption for new deployments
Debugging and Troubleshooting
Common Issues
Session Rejected (Invalid):
- Check clock skew (must be within ±30 seconds)
- Verify Mapping is sorted by key
- Ensure Destination not already in use
No RequestVariableLeaseSet:
- Router may be building tunnels (wait up to 5 minutes)
- Check for network connectivity issues
- Verify sufficient peer connections
Message Delivery Failures:
- Check MessageStatus codes for specific failure reason
- Verify remote LeaseSet is published and current
- Ensure compatible encryption types
Subsession Problems:
- Verify primary session created first
- Confirm same encryption keys
- Check for distinct signing keys
Diagnostic Messages
GetBandwidthLimits: Query router capacity HostLookup: Test name resolution and LeaseSet availability MessageStatus: Track message delivery end-to-end
Related Specifications
- Common Structures: /docs/specs/common-structures/
- I2NP (Network Protocol): /docs/specs/i2np/
- ECIES-X25519: /docs/specs/ecies/
- Tunnel Creation: /docs/specs/implementation/
- Streaming Library: /docs/specs/streaming/
- Datagram Library: /docs/api/datagrams/
- SAM v3: /docs/api/samv3/
Proposals Referenced
- Proposal 123: Encrypted LeaseSets and authentication
- Proposal 144: ECIES-X25519-AEAD-Ratchet
- Proposal 149: Blinded address format (b33)
- Proposal 152: X25519 tunnel creation
- Proposal 154: Database lookups from ECIES destinations
- Proposal 156: Router migration to ECIES-X25519
- Proposal 161: Destination padding compression
- Proposal 167: LeaseSet service records
- Proposal 169: Post-quantum hybrid cryptography (ML-KEM)
Javadocs Reference
Deprecation Summary
Deprecated Messages (Do Not Use)
- CreateLeaseSetMessage (type 4): Use CreateLeaseSet2Message
- RequestLeaseSetMessage (type 21): Use RequestVariableLeaseSetMessage
- ReceiveMessageBeginMessage (type 6): Use fast receive mode
- ReceiveMessageEndMessage (type 7): Use fast receive mode
- DestLookupMessage (type 34): Use HostLookupMessage
- DestReplyMessage (type 35): Use HostReplyMessage
- ReportAbuseMessage (type 29): Never implemented
Deprecated Options
- ElGamal encryption (type 0): Migrate to ECIES-X25519 (type 4)
- DSA signatures: Migrate to EdDSA or ECDSA
i2cp.fastReceive=false: Always use fast receive mode