You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
initiate_session (the command called when a user pastes an ech0:// link) makes a single connection attempt to the peer. If the I2P tunnels are degraded — which is common, especially in the first few minutes after bootstrap — the attempt fails and the user gets a generic error with no option to retry automatically.
I2P connections are inherently unreliable on the first attempt. Tunnels may be in the process of being rebuilt, the peer's lease set might not have propagated yet, or intermediate hops may reject the tunnel build (reason=30 "bandwidth" as seen in logs). A single-shot approach is not appropriate for this network.
Current behavior
In session.rs, initiate_session (line ~326) does:
// 1. Single connection attempt — no retrylet tunnel = session.connect_to_peer(&peer.dest).await?;// 2. Single handshake attempt — no retrywrite_framed(&mut writer,&init_msg).await?;let ack_frame = read_framed(&mut reader).await?;
If any step fails, the error propagates to the frontend and the user must manually try again.
What needs to change
1. Add retry loop with exponential backoff around the full dial+handshake
Wrap the entire sequence (SAM STREAM CONNECT + write_framed(INIT) + read_framed(ACK)) in a retry loop:
Max attempts: 3–4 (I2P latency means more than that is wasteful)
Backoff: start at ~5s, increase to ~10s, then ~15s — giving tunnels time to rebuild between attempts
Each retry should open a fresh SAM STREAM CONNECT since the previous tunnel may be dead
The ephemeral key (ek_a) must be regenerated on each attempt — reusing it after a failed attempt where the peer may have partially processed the handshake would be a security issue
2. Emit progress events to the frontend
The user should know what's happening during retries. Add a new event:
app.emit("connection_progress", serde_json::json!({"attempt": attempt,"max_attempts":MAX_ATTEMPTS,"status":"retrying"// or "connecting", "failed"}))?;
The frontend should listen for connection_progress and show a status like:
"Connecting to peer... (attempt 2/4)"
3. Frontend: show progress in SessionSetup
SessionSetup currently calls invoke("initiate_session") and only shows a spinner or error. It should:
Listen for connection_progress events while connecting
Show which attempt is in progress
Show a meaningful error on final failure (e.g., "Could not reach peer after 4 attempts — their node may be offline or still bootstrapping")
4. Distinguish error types for the user
Not all failures are the same. Map errors to user-friendly messages:
STREAM CONNECT failed: RESULT=CANT_REACH_PEER → "Peer is unreachable — they may be offline"
STREAM CONNECT failed: RESULT=TIMEOUT → "Connection timed out — I2P tunnels may be building"
Handshake timeout → "Connected but handshake failed — retrying"
Description
initiate_session(the command called when a user pastes anech0://link) makes a single connection attempt to the peer. If the I2P tunnels are degraded — which is common, especially in the first few minutes after bootstrap — the attempt fails and the user gets a generic error with no option to retry automatically.I2P connections are inherently unreliable on the first attempt. Tunnels may be in the process of being rebuilt, the peer's lease set might not have propagated yet, or intermediate hops may reject the tunnel build (reason=30 "bandwidth" as seen in logs). A single-shot approach is not appropriate for this network.
Current behavior
In
session.rs,initiate_session(line ~326) does:If any step fails, the error propagates to the frontend and the user must manually try again.
What needs to change
1. Add retry loop with exponential backoff around the full dial+handshake
Wrap the entire sequence (SAM
STREAM CONNECT+write_framed(INIT)+read_framed(ACK)) in a retry loop:ek_a) must be regenerated on each attempt — reusing it after a failed attempt where the peer may have partially processed the handshake would be a security issue2. Emit progress events to the frontend
The user should know what's happening during retries. Add a new event:
The frontend should listen for
connection_progressand show a status like:3. Frontend: show progress in SessionSetup
SessionSetupcurrently callsinvoke("initiate_session")and only shows a spinner or error. It should:connection_progressevents while connecting4. Distinguish error types for the user
Not all failures are the same. Map errors to user-friendly messages:
STREAM CONNECT failed: RESULT=CANT_REACH_PEER→ "Peer is unreachable — they may be offline"STREAM CONNECT failed: RESULT=TIMEOUT→ "Connection timed out — I2P tunnels may be building"Notes
accept_loop) does NOT need retry logic — it just loops and waits for the next connection. Its equivalent fix is the timeout in issue Peer connection hangs indefinitely: missing timeouts on handshake I/O over degraded I2P tunnels #11.initiate_sessiononce.