refactor(net): persistent endpoint/gossip/router (W7 B1)
The friends-only presence listener (W7) must answer pings while the app is open, whether or not we're in a call — but a node id has exactly one live endpoint instance (proven by the dual-endpoint spike: two endpoints sharing a SecretKey collide, all inbound connections land on one and the other's ALPN fails the QUIC handshake). So the listener can't get its own endpoint; the whole app must share one persistent endpoint. Today the core rebuilds the endpoint+gossip+router on every Join and tears them down on leave, so there's nothing alive between calls. B1 hoists those durable pieces to the app lifetime (no new behavior): - New persistent `NetStack` (endpoint + gossip + Router) built once at startup under the RelayNoDiscovery default (relay reachability, no DNS beacon); `online()` is backgrounded so launch isn't blocked. - New persistent `AudioRouter` (src/network/iroh_impl.rs) replaces the per-session `AudioProtocol`: it's registered once on the single Router and delegates each inbound audio connection to whatever session `Shared` is bound (`bind` on join, `clear` on leave), dropping links when idle. `IrohTransport:: new` now returns just `Self`. - Join reuses `net.endpoint`/`net.gossip` and only subscribes its gossip topic + binds the audio router; Leave clears the router but keeps the endpoint up. - `SetNetworkMode`/`RegenerateIdentity` rebuild the stack immediately when idle, else defer to the next Leave/Join (preserves "applies on next join"), and the existing session is always torn down before any rebuild closes the endpoint. Tests/loopback updated for the new transport API. 256 lib + 6 reconnect + 4 loopback + 2 ignored real-endpoint tests green, clippy --all-targets clean, release builds. NOT yet 2-machine field-verified — that regression is the gate before this merges to main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+7
-5
@@ -14,11 +14,13 @@
|
||||
//!
|
||||
//! **Deferred to a later slice (the hard integration):** spawning `serve` on a
|
||||
//! persistent endpoint that lives OUTSIDE the per-join room session, and the
|
||||
//! outbound ping scheduler. There's a real fork there — a second always-on
|
||||
//! endpoint would share our node id with the per-join room endpoint (possible
|
||||
//! relay/identity collision), vs. refactoring to a single persistent endpoint.
|
||||
//! That decision wants care + a 2-machine check, so it's intentionally NOT made
|
||||
//! here; this module works against whatever `Endpoint` it's handed.
|
||||
//! outbound ping scheduler. The endpoint fork was DECIDED 2026-06-15 by a
|
||||
//! throwaway spike: a **single persistent endpoint** (option b), NOT a second
|
||||
//! always-on endpoint sharing our id (option a). The spike proved two endpoints
|
||||
//! sharing one `SecretKey` can't coexist — inbound connections all land on one
|
||||
//! instance and the other's ALPN fails the QUIC handshake ("error 120"). So this
|
||||
//! `serve` will run as the FRIENDS_ALPN handler on the app's one persistent
|
||||
//! `Router`; it still works against whatever `Endpoint` it's handed.
|
||||
|
||||
use crate::presence::ControlMsg;
|
||||
use anyhow::{Context, Result, bail};
|
||||
|
||||
Reference in New Issue
Block a user