fix(net): room creator can rejoin a room they left (A8)
The gossip bootstrap list was derived solely from the ticket: a client dialed the host, but "we are the host" produced an EMPTY list. So when the room CREATOR rejoined their own room (their ticket names themselves as host) they dialed nobody and never re-entered the swarm — the remaining peer stayed stuck until it too left and rejoined. (First 2-human field test, 2026-06-14; user-confirmed call-breaking, P1.) Fix: retain the peers seen in the current room across leave (core `known_peers`, updated by the event task; reset only when the joined ticket changes) and pass them to `RoomState::join` as extra bootstrap targets. The new pure `compute_bootstrap` seam unions the ticket host + retained peers, drops self, and de-dups; address resolution rides the persistent lookup. - `RoomState::join` gains `extra_bootstrap: Vec<EndpointAddr>` (test_net callers pass vec![]). - +4 unit tests on `compute_bootstrap`, incl. the host-rejoin regression case. Tests-green (208 lib + 6 + 4 integration), clippy clean. NOT yet 2-machine field-verified — needs a live host leave→rejoin call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+13
-2
@@ -1,4 +1,4 @@
|
||||
use iroh::EndpointId;
|
||||
use iroh::{EndpointId, EndpointAddr};
|
||||
use bytes::Bytes;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
@@ -133,7 +133,18 @@ pub trait NetworkTransport: Send + Sync {
|
||||
#[async_trait]
|
||||
pub trait RoomState: Send + Sync {
|
||||
/// Joins a room using a gossip ticket string and announces our state.
|
||||
async fn join(&self, ticket: &str, self_state: PeerState) -> Result<(), NetError>;
|
||||
///
|
||||
/// `extra_bootstrap` are additional peer addresses to dial when entering the
|
||||
/// gossip swarm, on top of the ticket's host. This is what lets the ROOM
|
||||
/// CREATOR rejoin a room they left: their own ticket lists only themselves as
|
||||
/// host, so without retained peers they'd have no dial target and never
|
||||
/// re-enter the swarm (bug A8). Callers with no prior peers pass `vec![]`.
|
||||
async fn join(
|
||||
&self,
|
||||
ticket: &str,
|
||||
self_state: PeerState,
|
||||
extra_bootstrap: Vec<EndpointAddr>,
|
||||
) -> Result<(), NetError>;
|
||||
|
||||
/// Updates our local state (e.g. when user mutes/unmutes) and broadcasts it.
|
||||
async fn update_self_state(&self, self_state: PeerState) -> Result<(), NetError>;
|
||||
|
||||
Reference in New Issue
Block a user