40	/// way so the brain can retire it (B5 / KNOWN-HAZARDS 7.2).
41	pub const KIND_APPLIED: &str = "applied";
42	/// Broker→brain: a command failed (carries an operator-facing message).
43	pub const KIND_ERROR: &str = "error";
44	/// Brain→broker: report the broker-owned net endpoint's status (D4a). The brain
45	/// holds no endpoint of its own — the broker owns the node's network presence
46	/// (ADR-0004 §B), so status (node id, dialable address, conn count) is an IPC ask.
47	pub const KIND_NET_STATUS: &str = "net-status";
48	/// Broker→brain: the net endpoint status reply.
49	pub const KIND_NET_STATUS_REPLY: &str = "net-status-reply";
50	/// Brain→broker: dial a peer over the broker-owned endpoint (D4a). With an
51	/// `op_id` the dial is exactly-once (journaled like an idempotent `input`).
52	pub const KIND_NET_DIAL: &str = "net-dial";
53	/// Broker→brain: a dial durably landed — fresh or deduped (`applied_now`
54	/// distinguishes, mirroring [`KIND_APPLIED`]). Carries the broker-minted conn id.
55	pub const KIND_NET_DIALED: &str = "net-dialed";
56	/// Brain→broker: open a bidi QUIC stream on a held connection (D4b). With an
57	/// `op_id` the open is exactly-once (journaled like a dial).
58	pub const KIND_NET_STREAM_OPEN: &str = "net-stream-open";
59	/// Broker→brain: a stream open durably landed (fresh or deduped).
60	pub const KIND_NET_STREAM_OPENED: &str = "net-stream-opened";
61	/// Brain→broker: write bytes to a held stream's send side (D4b). With an
62	/// `op_id` the write is exactly-once (journaled; acked with [`KIND_NET_SENT`]).
63	pub const KIND_NET_STREAM_SEND: &str = "net-stream-send";
64	/// Broker→brain: a journaled stream send durably landed (fresh or deduped).
65	pub const KIND_NET_SENT: &str = "net-sent";
66	/// Brain→broker: list the streams the broker currently holds (D4b). The query
67	/// model a re-attaching brain uses to find streams that arrived while it was
68	/// down — no announce buffer to miss.
69	pub const KIND_NET_STREAMS: &str = "net-streams";
70	/// Broker→brain: the held-streams reply.
71	pub const KIND_NET_STREAMS_REPLY: &str = "net-streams-reply";
72	/// Brain→broker: (re)subscribe to a stream's buffered reads from a cursor —
73	/// the PTY `subscribe` discipline applied to a QUIC stream (gapless resume).
74	pub const KIND_NET_STREAM_SUBSCRIBE: &str = "net-stream-subscribe";
75	/// Broker→brain: a sequenced chunk of a stream's reads (live or replayed).
76	pub const KIND_NET_STREAM_DATA: &str = "net-stream-data";
77	/// Broker→brain: a stream's read side finished cleanly; `next_seq` tells the
78	/// brain the final cursor so it can verify it missed nothing.
79	pub const KIND_NET_STREAM_EOF: &str = "net-stream-eof";
80	/// Brain→broker: (re)subscribe to the broker's conn-liveness event log from a
81	/// cursor (D4c) — the PresenceChannel broker seam. Same gapless-resume
82	/// discipline as a stream subscribe.
83	// [impl->REQ-EP-4]
84	pub const KIND_NET_PRESENCE_SUBSCRIBE: &str = "net-presence-subscribe";
85	/// Broker→brain: one sequenced conn-liveness event (live or replayed).
86	pub const KIND_NET_PRESENCE_EVENT: &str = "net-presence-event";
87
88	/// [`NetPresenceEvent::kind`]: a connection joined the broker's conn table.
89	pub const PRESENCE_CONNECTED: &str = "connected";
90	/// [`NetPresenceEvent::kind`]: a connection ended and left the conn table.
91	pub const PRESENCE_DISCONNECTED: &str = "disconnected";
92
93	/// Brain→broker: list hosted sessions (id + owning endpoint). The inbound
94	/// dispatcher's session→endpoint resolution (D9-1).
95	pub const KIND_SESSIONS: &str = "sessions";
96	/// Broker→brain reply to [`KIND_SESSIONS`].
97	pub const KIND_SESSIONS_REPLY: &str = "sessions-reply";
98
99	/// `spawn` payload — the program + args + initial geometry. The broker spawns
100	/// it under a PTY (no shell interpretation; argv is passed through).
101	#[derive(Debug, Clone, Serialize, Deserialize)]
102	pub struct SpawnReq {
103	    /// The program to exec (resolved on `PATH` by the OS spawn).
104	    pub program: String,
105	    /// Its arguments, verbatim.
106	    #[serde(default)]
107	    pub args: Vec<String>,
108	    /// Initial PTY rows.
109	    pub rows: u16,

/// way so the brain can retire it (B5 / KNOWN-HAZARDS 7.2).
pub const KIND_APPLIED: &str = "applied";
/// Broker→brain: a command failed (carries an operator-facing message).
pub const KIND_ERROR: &str = "error";
/// Brain→broker: report the broker-owned net endpoint's status (D4a). The brain
/// holds no endpoint of its own — the broker owns the node's network presence
/// (ADR-0004 §B), so status (node id, dialable address, conn count) is an IPC ask.
pub const KIND_NET_STATUS: &str = "net-status";
/// Broker→brain: the net endpoint status reply.
pub const KIND_NET_STATUS_REPLY: &str = "net-status-reply";
/// Brain→broker: dial a peer over the broker-owned endpoint (D4a). With an
/// `op_id` the dial is exactly-once (journaled like an idempotent `input`).
pub const KIND_NET_DIAL: &str = "net-dial";
/// Broker→brain: a dial durably landed — fresh or deduped (`applied_now`
/// distinguishes, mirroring [`KIND_APPLIED`]). Carries the broker-minted conn id.
pub const KIND_NET_DIALED: &str = "net-dialed";
/// Brain→broker: open a bidi QUIC stream on a held connection (D4b). With an
/// `op_id` the open is exactly-once (journaled like a dial).
pub const KIND_NET_STREAM_OPEN: &str = "net-stream-open";
/// Broker→brain: a stream open durably landed (fresh or deduped).
pub const KIND_NET_STREAM_OPENED: &str = "net-stream-opened";
/// Brain→broker: write bytes to a held stream's send side (D4b). With an
/// `op_id` the write is exactly-once (journaled; acked with [`KIND_NET_SENT`]).
pub const KIND_NET_STREAM_SEND: &str = "net-stream-send";
/// Broker→brain: a journaled stream send durably landed (fresh or deduped).
pub const KIND_NET_SENT: &str = "net-sent";
/// Brain→broker: list the streams the broker currently holds (D4b). The query
/// model a re-attaching brain uses to find streams that arrived while it was
/// down — no announce buffer to miss.
pub const KIND_NET_STREAMS: &str = "net-streams";
/// Broker→brain: the held-streams reply.
pub const KIND_NET_STREAMS_REPLY: &str = "net-streams-reply";
/// Brain→broker: (re)subscribe to a stream's buffered reads from a cursor —
/// the PTY `subscribe` discipline applied to a QUIC stream (gapless resume).
pub const KIND_NET_STREAM_SUBSCRIBE: &str = "net-stream-subscribe";
/// Broker→brain: a sequenced chunk of a stream's reads (live or replayed).
pub const KIND_NET_STREAM_DATA: &str = "net-stream-data";
/// Broker→brain: a stream's read side finished cleanly; `next_seq` tells the
/// brain the final cursor so it can verify it missed nothing.
pub const KIND_NET_STREAM_EOF: &str = "net-stream-eof";
/// Brain→broker: (re)subscribe to the broker's conn-liveness event log from a
/// cursor (D4c) — the PresenceChannel broker seam. Same gapless-resume
/// discipline as a stream subscribe.
// [impl->REQ-EP-4]
pub const KIND_NET_PRESENCE_SUBSCRIBE: &str = "net-presence-subscribe";
/// Broker→brain: one sequenced conn-liveness event (live or replayed).
pub const KIND_NET_PRESENCE_EVENT: &str = "net-presence-event";

/// [`NetPresenceEvent::kind`]: a connection joined the broker's conn table.
pub const PRESENCE_CONNECTED: &str = "connected";
/// [`NetPresenceEvent::kind`]: a connection ended and left the conn table.
pub const PRESENCE_DISCONNECTED: &str = "disconnected";

/// Brain→broker: list hosted sessions (id + owning endpoint). The inbound
/// dispatcher's session→endpoint resolution (D9-1).
pub const KIND_SESSIONS: &str = "sessions";
/// Broker→brain reply to [`KIND_SESSIONS`].
pub const KIND_SESSIONS_REPLY: &str = "sessions-reply";

/// `spawn` payload — the program + args + initial geometry. The broker spawns
/// it under a PTY (no shell interpretation; argv is passed through).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpawnReq {
    /// The program to exec (resolved on `PATH` by the OS spawn).
    pub program: String,
    /// Its arguments, verbatim.
    #[serde(default)]
    pub args: Vec<String>,
    /// Initial PTY rows.
    pub rows: u16,


Grep

pub struct NetDialReq|pub struct NetDialed|pub struct NetStatusReply


227:pub struct NetStatusReply {
228-    /// Whether this broker hosts a net endpoint at all.
229-    pub enabled: bool,
230-    /// The node's stable name (lowercase-hex Ed25519 pubkey == iroh EndpointId).
231-    #[serde(default, skip_serializing_if = "Option::is_none")]
232-    pub node_id_hex: Option<String>,
233-    /// The endpoint's dialable address — a serde-serialized iroh `EndpointAddr`,
234-    /// opaque to this catalog (the net host owns its shape). `null` when disabled.
235-    #[serde(default)]
236-    pub addr: serde_json::Value,
237-    /// Live connections (accepted + dialed) in the broker's conn table.
238-    #[serde(default)]
239-    pub conn_count: u64,
240-}
241-
--
244:pub struct NetDialReq {
245-    /// The peer's dialable address — a serde-serialized iroh `EndpointAddr`
246-    /// (opaque JSON here; the broker's net host decodes it).
247-    pub addr: serde_json::Value,
248-    /// The durable op id for exactly-once dialing. `Some(id)` routes the dial
249-    \ through the broker's [`crate::effect::EffectJournal`] (keyed under the
250-    \ reserved [`crate::nethost::NET_EFFECT_SESSION`] namespace): a replay of
251-    /// the same id after a brain crash is deduped and answered with the
252-    /// already-open connection instead of dialing twice. `None` dials directly.
253-    #[serde(default, skip_serializing_if = "Option::is_none")]
254-    pub op_id: Option<u64>,
255-}
256-
257-/// `net-dialed` event — a dial durably landed (fresh or deduped).
258-#[derive(Debug, Clone, Serialize, Deserialize)]
259:pub struct NetDialed {
260-    /// The broker-minted id addressing this connection in later net commands.
261-    pub conn_id: u64,
262-    /// The remote node's stable name (lowercase-hex pubkey), proven by the QUIC
263-    /// handshake (the EndpointId *is* the key).
264-    pub remote_id_hex: String,
265-    /// Echoes the request's durable op id, when one was carried.
266-    #[serde(default, skip_serializing_if = "Option::is_none")]
267-    pub op_id: Option<u64>,
268-    /// Whether the dial ran on this delivery (`true`) or was deduped (`false`).
269-    \ Both mean "durably landed" — mirrors [`AppliedEvent::applied_now`].
270-    #[serde(default)]
271-    pub applied_now: bool,
272-}
273-


Add the pair-join kinds + payloads to msg.rs:


Edit

/// Brain→broker: list hosted sessions (id + owning endpoint). The inbound
/// dispatcher's session→endpoint resolution (D9-1).
pub const KIND_SESSIONS: &str = "sessions";

/// Brain→broker: run the joiner leg of the subnet pairing ceremony (M7 D3,
/// REQ-SUBNET-2). The ceremony must run broker-side: it dials via the
/// rendezvous and binds/pins the broker-owned endpoint's REAL node pubkey.
pub const KIND_PAIR_JOIN: &str = "pair-join";
/// Broker→brain: the join ceremony's outcome (success or classified failure).
pub const KIND_PAIR_JOINED: &str = "pair-joined";

/// Brain→broker: list hosted sessions (id + owning endpoint). The inbound
/// dispatcher's session→endpoint resolution (D9-1).
pub const KIND_SESSIONS: &str = "sessions";


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\msg.rs has been updated successfully. (file state is current in your context — no need to Read it back)

/// Brain→broker: run the joiner leg of the subnet pairing ceremony (M7 D3,
/// REQ-SUBNET-2). The ceremony must run broker-side: it dials via the
/// rendezvous and binds/pins the broker-owned endpoint's REAL node pubkey.
pub const KIND_PAIR_JOIN: &str = "pair-join";
/// Broker→brain: the join ceremony's outcome (success or classified failure).
pub const KIND_PAIR_JOINED: &str = "pair-joined";

/// Brain→broker: list hosted sessions (id + owning endpoint). The inbound
/// dispatcher's session→endpoint resolution (D9-1).
pub const KIND_SESSIONS: &str = "sessions";

/// [`NetPresenceEvent::kind`]: a connection ended and left the conn table.

pub const PRESENCE_DISCONNECTED: &str = "disconnected";

+/// Brain→broker: run the joiner leg of the subnet pairing ceremony (M7 D3,

+/// REQ-SUBNET-2). The ceremony must run broker-side: it dials via the

+/// rendezvous and binds/pins the broker-owned endpoint's REAL node pubkey.

+pub const KIND_PAIR_JOIN: &str = "pair-join";

+/// Broker→brain: the join ceremony's outcome (success or classified failure).

+pub const KIND_PAIR_JOINED: &str = "pair-joined";


/// `net-dialed` event — a dial durably landed (fresh or deduped).

/// `pair-join` payload — the two user-entered ceremony inputs (M7 D3).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PairJoinReq {
    /// The subnet to join (names the rendezvous + the seed).
    pub subnet: String,
    /// The current 6-digit TOTP code (the SPAKE2 password — consumed by the
    /// ceremony, never logged or journaled).
    pub code: String,
}

/// `pair-joined` reply — the ceremony's verdict, classified for the CLI's
/// guided UX (wrong code re-prompts; rate limits surface honestly; a missing
/// seed-holder gets actionable hints).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PairJoinReply {
    /// Whether the ceremony confirmed and the seed landed.
    pub ok: bool,
    /// On failure: the [`crate::pairhost::JoinFail`] class tag —
    /// `no-seed-holder` / `wrong-code` / `rate-limited` / `unknown-subnet` /
    /// `clock-skew` / `other`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub class: Option<String>,
    /// Rate-limit retry window (seconds), when the class is `rate-limited`
    /// with a known backoff.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub retry_after_secs: Option<u64>,
    /// Operator-facing detail (the rendered outcome on success, the rendered
    /// failure otherwise). Never carries secret material.
    #[serde(default)]
    pub detail: String,
}

/// `net-dialed` event — a dial durably landed (fresh or deduped).


/// `pair-join` payload — the two user-entered ceremony inputs (M7 D3).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PairJoinReq {
    /// The subnet to join (names the rendezvous + the seed).
    pub subnet: String,
    /// The current 6-digit TOTP code (the SPAKE2 password — consumed by the
    /// ceremony, never logged or journaled).
    pub code: String,
}

/// `pair-joined` reply — the ceremony's verdict, classified for the CLI's
/// guided UX (wrong code re-prompts; rate limits surface honestly; a missing
/// seed-holder gets actionable hints).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PairJoinReply {
    /// Whether the ceremony confirmed and the seed landed.
    pub ok: bool,
    /// On failure: the [`crate::pairhost::JoinFail`] class tag —
    /// `no-seed-holder` / `wrong-code` / `rate-limited` / `unknown-subnet` /
    /// `clock-skew` / `other`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub class: Option<String>,
    /// Rate-limit retry window (seconds), when the class is `rate-limited`
    /// with a known backoff.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub retry_after_secs: Option<u64>,
    /// Operator-facing detail (the rendered outcome on success, the rendered
    /// failure otherwise). Never carries secret material.
    #[serde(default)]
    pub detail: String,
}

/// `net-dialed` event — a dial durably landed (fresh or deduped).

+/// `pair-join` payload — the two user-entered ceremony inputs (M7 D3).

+#[derive(Debug, Clone, Serialize, Deserialize)]

+    /// The subnet to join (names the rendezvous + the seed).

+    /// The current 6-digit TOTP code (the SPAKE2 password — consumed by the

+    /// ceremony, never logged or journaled).

+/// `pair-joined` reply — the ceremony's verdict, classified for the CLI's

+/// guided UX (wrong code re-prompts; rate limits surface honestly; a missing

+/// seed-holder gets actionable hints).

+    /// Whether the ceremony confirmed and the seed landed.

+    /// On failure: the [`crate::pairhost::JoinFail`] class tag —

+    /// `no-seed-holder` / `wrong-code` / `rate-limited` / `unknown-subnet` /

+    #[serde(default, skip_serializing_if = "Option::is_none")]

+    /// Rate-limit retry window (seconds), when the class is `rate-limited`

+    /// Operator-facing detail (the rendered outcome on success, the rendered

+    /// failure otherwise). Never carries secret material.


540	    }
541
542	    /// Dial a peer over the broker-owned endpoint (D4a).
543	    ///
544	    /// With an `op_id` the dial is routed through the broker-owned
545	    /// [`EffectJournal`] under the reserved [`NET_EFFECT_SESSION`] namespace,
546	    /// exactly like an idempotent PTY write: a replay of the same id after a
547	    /// brain crash is deduped (no second connection) and answered with the
548	    /// connection the first apply opened. The `conn_id` is recorded *inside*
549	    /// the journaled closure, so a concurrent deduped replay always finds it.
550	    /// A deduped id whose connection this process no longer holds (a broker
551	    /// restart — the journal survives, connections don't) is a typed error
552	    /// telling the brain to dial under a fresh id.
553	    // [impl->REQ-HAZARD-RESTART-IDEMPOTENT]
554	    fn dispatch_net_dial(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
555	        let req: NetDialReq = serde_json::from_value(env.payload)
556	            .map_err(|e| format!("bad net-dial payload: {e}"))?;
557	        let host = self
558	            .net
559	            .as_ref()
560	            .ok_or("net disabled: this broker was bound without a network host")?;
561	        let addr: EndpointAddr = serde_json::from_value(req.addr)
562	            .map_err(|e| format!("bad net-dial addr: {e}"))?;
563	        match req.op_id {
564	            None => {
565	                // Direct (non-journaled) dial — no dedup, like a no-op_id input.
566	                let (conn_id, remote) = host.dial(addr).map_err(|e| e.to_string())?;
567	                send_frame(send, &net_dialed_envelope(conn_id, remote, None, true));
568	                Ok(())
569	            }
570	            Some(op) => {
571	                let outcome = self
572	                    .journal
573	                    .apply_once((NET_EFFECT_SESSION, op), EffectKind::NetDial, || {
574	                        let (conn_id, _) = host.dial(addr)?;
575	                        host.record_dial_op(op, conn_id);
576	                        Ok(())
577	                    })
578	                    .map_err(|e| format!("net-dial failed: {e}"))?;
579	                let conn_id = host.dial_op_conn(op).ok_or(
580	                    "net-dial op already applied but its connection is no longer held \
581	                     (broker restarted); retry with a fresh op_id",
582	                )?;
583	                let remote = host
584	                    .conn_remote_hex(conn_id)
585	                    .ok_or("dialed connection missing from the conn table")?;
586	                let applied_now = outcome == Outcome::Applied;
587	                send_frame(send, &net_dialed_envelope(conn_id, remote, Some(op), applied_now));
588	                Ok(())
589	            }
590	        }
591	    }
592
593	    /// Open a bidi stream on a held connection (D4b). Journaled exactly-once
594	    /// under the same discipline as [`Self::dispatch_net_dial`]: the minted
595	    /// `stream_id` is recorded inside the `apply_once` closure, and a deduped
596	    /// replay is answered with the stream the first apply opened.
597	    // [impl->REQ-HAZARD-RESTART-IDEMPOTENT]
598	    fn dispatch_net_stream_open(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
599	        let req: NetStreamOpenReq = serde_json::from_value(env.payload)
600	            .map_err(|e| format!("bad net-stream-open payload: {e}"))?;
601	        let host = self.net.as_ref().ok_or("net disabled")?;
602	        let build = |stream_id: u64, conn_id: u64, op_id: Option<u64>, applied_now: bool| {
603	            Envelope::new(
604	                KIND_NET_STREAM_OPENED,
605	                serde_json::to_value(NetStreamOpened { stream_id, conn_id, op_id, applied_now })
606	                    .expect("NetStreamOpened serializes"),
607	            )
608	        };
609	        match req.op_id {
610	            None => {
611	                let stream_id = host.open_stream(req.conn_id).map_err(|e| e.to_string())?;
612	                send_frame(send, &build(stream_id, req.conn_id, None, true));
613	                Ok(())
614	            }
615	            Some(op) => {
616	                let outcome = self
617	                    .journal
618	                    .apply_once((NET_EFFECT_SESSION, op), EffectKind::NetSend, || {
619	                        let stream_id = host.open_stream(req.conn_id)?;

    }

    /// Dial a peer over the broker-owned endpoint (D4a).
    ///
    /// With an `op_id` the dial is routed through the broker-owned
    /// [`EffectJournal`] under the reserved [`NET_EFFECT_SESSION`] namespace,
    /// exactly like an idempotent PTY write: a replay of the same id after a
    /// brain crash is deduped (no second connection) and answered with the
    /// connection the first apply opened. The `conn_id` is recorded *inside*
    /// the journaled closure, so a concurrent deduped replay always finds it.
    /// A deduped id whose connection this process no longer holds (a broker
    /// restart — the journal survives, connections don't) is a typed error
    /// telling the brain to dial under a fresh id.
    // [impl->REQ-HAZARD-RESTART-IDEMPOTENT]
    fn dispatch_net_dial(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
        let req: NetDialReq = serde_json::from_value(env.payload)
            .map_err(|e| format!("bad net-dial payload: {e}"))?;
        let host = self
            .net
            .as_ref()
            .ok_or("net disabled: this broker was bound without a network host")?;
        let addr: EndpointAddr = serde_json::from_value(req.addr)
            .map_err(|e| format!("bad net-dial addr: {e}"))?;
        match req.op_id {
            None => {
                // Direct (non-journaled) dial — no dedup, like a no-op_id input.
                let (conn_id, remote) = host.dial(addr).map_err(|e| e.to_string())?;
                send_frame(send, &net_dialed_envelope(conn_id, remote, None, true));
                Ok(())
            }
            Some(op) => {
                let outcome = self
                    .journal
                    .apply_once((NET_EFFECT_SESSION, op), EffectKind::NetDial, || {
                        let (conn_id, _) = host.dial(addr)?;
                        host.record_dial_op(op, conn_id);
                        Ok(())
                    })
                    .map_err(|e| format!("net-dial failed: {e}"))?;
                let conn_id = host.dial_op_conn(op).ok_or(
                    "net-dial op already applied but its connection is no longer held \
                     (broker restarted); retry with a fresh op_id",
                )?;
                let remote = host
                    .conn_remote_hex(conn_id)
                    .ok_or("dialed connection missing from the conn table")?;
                let applied_now = outcome == Outcome::Applied;
                send_frame(send, &net_dialed_envelope(conn_id, remote, Some(op), applied_now));
                Ok(())
            }
        }
    }

    /// Open a bidi stream on a held connection (D4b). Journaled exactly-once
    /// under the same discipline as [`Self::dispatch_net_dial`]: the minted
    /// `stream_id` is recorded inside the `apply_once` closure, and a deduped
    /// replay is answered with the stream the first apply opened.
    // [impl->REQ-HAZARD-RESTART-IDEMPOTENT]
    fn dispatch_net_stream_open(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
        let req: NetStreamOpenReq = serde_json::from_value(env.payload)
            .map_err(|e| format!("bad net-stream-open payload: {e}"))?;
        let host = self.net.as_ref().ok_or("net disabled")?;
        let build = |stream_id: u64, conn_id: u64, op_id: Option<u64>, applied_now: bool| {
            Envelope::new(
                KIND_NET_STREAM_OPENED,
                serde_json::to_value(NetStreamOpened { stream_id, conn_id, op_id, applied_now })
                    .expect("NetStreamOpened serializes"),
            )
        };
        match req.op_id {
            None => {
                let stream_id = host.open_stream(req.conn_id).map_err(|e| e.to_string())?;
                send_frame(send, &build(stream_id, req.conn_id, None, true));
                Ok(())
            }
            Some(op) => {
                let outcome = self
                    .journal
                    .apply_once((NET_EFFECT_SESSION, op), EffectKind::NetSend, || {
                        let stream_id = host.open_stream(req.conn_id)?;


/// Open a bidi stream on a held connection (D4b). Journaled exactly-once

    /// Run the joiner leg of the subnet pairing ceremony (M7 D3,
    /// REQ-SUBNET-2) on the broker-owned endpoint — it binds and pins the
    /// REAL node pubkey, so it cannot run anywhere else. Not journaled: a
    /// repeated join is idempotent at the stores (re-pair keeps the existing
    /// trust row + seed), and the TOTP code in the request must never land
    /// in a durable journal. Blocks this client's channel for the ceremony's
    /// (bounded) life; the reply is always a `pair-joined` frame, with
    /// failures classified for the CLI's guided UX.
    // [impl->REQ-SUBNET-2]
    fn dispatch_pair_join(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
        let req: PairJoinReq = serde_json::from_value(env.payload)
            .map_err(|e| format!("bad pair-join payload: {e}"))?;
        let host = self
            .net
            .as_ref()
            .ok_or("net disabled: this broker was bound without a network host")?;
        let reply = match host.pair_join(&req.subnet, &req.code) {
            Ok(outcome) => PairJoinReply {
                ok: true,
                class: None,
                retry_after_secs: None,
                detail: outcome,
            },
            Err(fail) => {
                use crate::pairhost::JoinFail;
                let (class, retry) = match &fail {
                    JoinFail::NoSeedHolder(_) => ("no-seed-holder", None),
                    JoinFail::WrongCode => ("wrong-code", None),
                    JoinFail::RateLimited(retry) => ("rate-limited", *retry),
                    JoinFail::UnknownSubnet(_) => ("unknown-subnet", None),
                    JoinFail::ClockSkew => ("clock-skew", None),
                    JoinFail::Other(_) => ("other", None),
                };
                PairJoinReply {
                    ok: false,
                    class: Some(class.to_string()),
                    retry_after_secs: retry,
                    detail: fail.to_string(),
                }
            }
        };
        let frame = Envelope::new(
            KIND_PAIR_JOINED,
            serde_json::to_value(reply).expect("PairJoinReply serializes"),
        );
        send_frame(send, &frame);
        Ok(())
    }

    /// Open a bidi stream on a held connection (D4b). Journaled exactly-once


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    /// Run the joiner leg of the subnet pairing ceremony (M7 D3,
    /// REQ-SUBNET-2) on the broker-owned endpoint — it binds and pins the
    /// REAL node pubkey, so it cannot run anywhere else. Not journaled: a
    /// repeated join is idempotent at the stores (re-pair keeps the existing
    /// trust row + seed), and the TOTP code in the request must never land
    /// in a durable journal. Blocks this client's channel for the ceremony's
    /// (bounded) life; the reply is always a `pair-joined` frame, with
    /// failures classified for the CLI's guided UX.
    // [impl->REQ-SUBNET-2]
    fn dispatch_pair_join(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
        let req: PairJoinReq = serde_json::from_value(env.payload)
            .map_err(|e| format!("bad pair-join payload: {e}"))?;
        let host = self
            .net
            .as_ref()
            .ok_or("net disabled: this broker was bound without a network host")?;
        let reply = match host.pair_join(&req.subnet, &req.code) {
            Ok(outcome) => PairJoinReply {
                ok: true,
                class: None,
                retry_after_secs: None,
                detail: outcome,
            },
            Err(fail) => {
                use crate::pairhost::JoinFail;
                let (class, retry) = match &fail {
                    JoinFail::NoSeedHolder(_) => ("no-seed-holder", None),
                    JoinFail::WrongCode => ("wrong-code", None),
                    JoinFail::RateLimited(retry) => ("rate-limited", *retry),
                    JoinFail::UnknownSubnet(_) => ("unknown-subnet", None),
                    JoinFail::ClockSkew => ("clock-skew", None),
                    JoinFail::Other(_) => ("other", None),
                };
                PairJoinReply {
                    ok: false,
                    class: Some(class.to_string()),
                    retry_after_secs: retry,
                    detail: fail.to_string(),
                }
            }
        };
        let frame = Envelope::new(
            KIND_PAIR_JOINED,
            serde_json::to_value(reply).expect("PairJoinReply serializes"),
        );
        send_frame(send, &frame);
        Ok(())
    }

    /// Open a bidi stream on a held connection (D4b). Journaled exactly-once

+    /// Run the joiner leg of the subnet pairing ceremony (M7 D3,

+    /// REQ-SUBNET-2) on the broker-owned endpoint — it binds and pins the

+    /// REAL node pubkey, so it cannot run anywhere else. Not journaled: a

+    /// repeated join is idempotent at the stores (re-pair keeps the existing

+    /// trust row + seed), and the TOTP code in the request must never land

+    /// in a durable journal. Blocks this client's channel for the ceremony's

+    /// (bounded) life; the reply is always a `pair-joined` frame, with

+    /// failures classified for the CLI's guided UX.

+    fn dispatch_pair_join(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {

+        let req: PairJoinReq = serde_json::from_value(env.payload)

+            .map_err(|e| format!("bad pair-join payload: {e}"))?;

+            .ok_or("net disabled: this broker was bound without a network host")?;

+        let reply = match host.pair_join(&req.subnet, &req.code) {

+            Ok(outcome) => PairJoinReply {

+                retry_after_secs: None,

+                use crate::pairhost::JoinFail;

+                let (class, retry) = match &fail {

+                    JoinFail::NoSeedHolder(_) => ("no-seed-holder", None),

+                    JoinFail::WrongCode => ("wrong-code", None),

+                    JoinFail::RateLimited(retry) => ("rate-limited", *retry),

+                    JoinFail::UnknownSubnet(_) => ("unknown-subnet", None),

+                    JoinFail::ClockSkew => ("clock-skew", None),

+                    JoinFail::Other(_) => ("other", None),

+                    class: Some(class.to_string()),

+                    retry_after_secs: retry,

+                    detail: fail.to_string(),

+            serde_json::to_value(reply).expect("PairJoinReply serializes"),

/// under the same discipline as [`Self::dispatch_net_dial`]: the minted

/// `stream_id` is recorded inside the `apply_once` closure, and a deduped


                KIND_NET_STATUS => self.dispatch_net_status(&send),
                KIND_NET_DIAL => {
                    if let Err(msg) = self.dispatch_net_dial(env, &send) {
                        send_error(&send, &msg);
                    }
                }

                KIND_NET_STATUS => self.dispatch_net_status(&send),
                KIND_NET_DIAL => {
                    if let Err(msg) = self.dispatch_net_dial(env, &send) {
                        send_error(&send, &msg);
                    }
                }
                KIND_PAIR_JOIN => {
                    if let Err(msg) = self.dispatch_pair_join(env, &send) {
                        send_error(&send, &msg);
                    }
                }


                KIND_NET_STATUS => self.dispatch_net_status(&send),
                KIND_NET_DIAL => {
                    if let Err(msg) = self.dispatch_net_dial(env, &send) {
                        send_error(&send, &msg);
                    }
                }
                KIND_PAIR_JOIN => {
                    if let Err(msg) = self.dispatch_pair_join(env, &send) {
                        send_error(&send, &msg);
                    }
                }

+                    if let Err(msg) = self.dispatch_pair_join(env, &send) {

+                        send_error(&send, &msg);

if let Err(msg) = self.dispatch_net_stream_open(env, &send) {


Bash

grep -n "use crate::msg::" -A12 crates/spt-daemon/src/broker.rs | head -20

Check broker msg imports


48:use crate::msg::{
49-    applied_envelope, decode_bytes, net_dialed_envelope, output_envelope, ErrorEvent, ExitEvent,
50-    InputReq, KillReq, NetDialReq, NetPresenceSubscribeReq, NetSent, NetStatusReply,
51-    NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq, NetStreamsReply,
52-    ResizeReq, SessionInfo, SessionsReply, SpawnReq, Spawned, SubscribeReq, KIND_ERROR, KIND_EXIT,
53-    KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT,
54-    KIND_NET_STATUS, KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY,
55-    KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
56-    KIND_NET_STREAM_SUBSCRIBE, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY, KIND_SPAWN,
57-    KIND_SPAWNED, KIND_SUBSCRIBE,
58-};
59-use crate::nethost::{NetHost, NET_EFFECT_SESSION};
60-use crate::transport::{recv_hello, DaemonTransport, LocalSocketTransport};


Edit

    InputReq, KillReq, NetDialReq, NetPresenceSubscribeReq, NetSent, NetStatusReply,
    NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq, NetStreamsReply,
    ResizeReq, SessionInfo, SessionsReply, SpawnReq, Spawned, SubscribeReq, KIND_ERROR, KIND_EXIT,
    KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT,
    KIND_NET_STATUS, KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY,
    KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
    KIND_NET_STREAM_SUBSCRIBE, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY, KIND_SPAWN,
    KIND_SPAWNED, KIND_SUBSCRIBE,
};

    InputReq, KillReq, NetDialReq, NetPresenceSubscribeReq, NetSent, NetStatusReply,
    NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq, NetStreamsReply,
    PairJoinReply, PairJoinReq, ResizeReq, SessionInfo, SessionsReply, SpawnReq, Spawned,
    SubscribeReq, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL,
    KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS, KIND_NET_STATUS_REPLY,
    KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED,
    KIND_NET_STREAM_SEND, KIND_NET_STREAM_SUBSCRIBE, KIND_PAIR_JOIN, KIND_PAIR_JOINED,
    KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY, KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};


    InputReq, KillReq, NetDialReq, NetPresenceSubscribeReq, NetSent, NetStatusReply,
    NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq, NetStreamsReply,
    ResizeReq, SessionInfo, SessionsReply, SpawnReq, Spawned, SubscribeReq, KIND_ERROR, KIND_EXIT,
    KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT,
    KIND_NET_STATUS, KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY,
    KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
    KIND_NET_STREAM_SUBSCRIBE, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY, KIND_SPAWN,
    KIND_SPAWNED, KIND_SUBSCRIBE,
};

    InputReq, KillReq, NetDialReq, NetPresenceSubscribeReq, NetSent, NetStatusReply,
    NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq, NetStreamsReply,
    PairJoinReply, PairJoinReq, ResizeReq, SessionInfo, SessionsReply, SpawnReq, Spawned,
    SubscribeReq, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL,
    KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS, KIND_NET_STATUS_REPLY,
    KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED,
    KIND_NET_STREAM_SEND, KIND_NET_STREAM_SUBSCRIBE, KIND_PAIR_JOIN, KIND_PAIR_JOINED,
    KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY, KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};

applied_envelope, decode_bytes, net_dialed_envelope, output_envelope, ErrorEvent, ExitEvent,

-    ResizeReq, SessionInfo, SessionsReply, SpawnReq, Spawned, SubscribeReq, KIND_ERROR, KIND_EXIT,

-    KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT,

-    KIND_NET_STATUS, KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY,

-    KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,

-    KIND_NET_STREAM_SUBSCRIBE, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY, KIND_SPAWN,

+    PairJoinReply, PairJoinReq, ResizeReq, SessionInfo, SessionsReply, SpawnReq, Spawned,

+    SubscribeReq, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL,

+    KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS, KIND_NET_STATUS_REPLY,

+    KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED,

+    KIND_NET_STREAM_SEND, KIND_NET_STREAM_SUBSCRIBE, KIND_PAIR_JOIN, KIND_PAIR_JOINED,

+    KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY, KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,

use crate::transport::{recv_hello, DaemonTransport, LocalSocketTransport};


D3a: pairhost module + nethost multi-ALPN routing + meet rotation

crates/spt-daemon/src/pairhost.rs: respond() handler (tokio Mutex limiter try_lock shed, 60s timeout aligned w/ CEREMONY_TIMEOUT_SECS), meet rotation loop per member subnet (re-bound per TOTP step, soft-fail binds, skip on BindScope::Port), join_ceremony() joiner leg. NetHost binds [SPT_NET_ALPN, SPT_PAIR_ALPN], accept loop routes by conn.alpn().

D3b: pair-join broker IPC op + brain client method

msg.rs KIND_PAIR_JOIN/JOINED + PairJoinReq/Reply (ok, class, detail). broker.rs dispatch arm → NetHost::pair_join. brain.rs client pair_join().

D3c: spt subnet join CLI + elevation gates + REQ-SUBNET-2/4 activation

join [NAME] [--code] — elevation gate (exit 3), interactive prompts when tty, bounded wrong-code re-prompt, honest rate-limit + no-seed-holder hints, home adoption on success. Status hint footer gains join line. Activate REQ-SUBNET-2 [impl] (+unit), REQ-SUBNET-4 [impl,unit]; tag create/join gates.

D3d: ADR-0005 amendment + full checks + commit

Record always-on responder + elevation gates amendment in docs/adr/0005. cargo tests, xtask gen/check, traceable-reqs check, commit.


Updated task #10 status


77:pub enum BrokerEvent {
78-    /// A contiguous chunk of a session's output (the cursor has advanced past it).
79-    Output { session_id: u64, seq: u64, bytes: Vec<u8> },
80-    /// A spawn succeeded.
81-    Spawned { session_id: u64, pid: Option<u32> },
82-    /// A session's child exited.
83-    Exit { session_id: u64, code: Option<i32> },
84-    /// An idempotent effect is durably applied (or was deduped — `applied_now`
85-    \ distinguishes). The exactly-once ack for a [`Brain::send_effect`] (B5).
86-    Applied { session_id: u64, op_id: u64, applied_now: bool },
87-    /// The broker-owned net endpoint's status (a `net-status` reply, D4a).
88-    NetStatus(NetStatusReply),
89-    /// A net dial durably landed — fresh or deduped (D4a).
90-    NetDialed(NetDialed),
91-    /// A stream open durably landed — fresh or deduped (D4b).
92-    NetStreamOpened(NetStreamOpened),
93-    /// A journaled stream send durably landed — fresh or deduped (D4b).
94-    NetSent(NetSent),
95-    /// The broker's held-streams listing (D4b).
96-    NetStreams(NetStreamsReply),
97-    \ The broker's hosted-sessions listing (D9-1: session → endpoint).
98-    Sessions(SessionsReply),
99-    /// A contiguous chunk of a stream's reads (the cursor has advanced past it).
100-    NetStreamData { stream_id: u64, seq: u64, bytes: Vec<u8> },
101-    /// A stream's read side finished cleanly; `next_seq` is the completeness
102-    /// check (the subscriber's cursor must equal it to have missed nothing).
103-    NetStreamEof { stream_id: u64, next_seq: u64 },
104-    /// A contiguous conn-liveness event (the presence cursor has advanced past
105-    /// it) — connected/disconnected on the broker's conn table (D4c).
106-    NetPresence(NetPresenceEvent),
107-    /// The broker reported a command error.
108-    Error { message: String },
109-    \ A frame of an unrecognized kind (forward-compat: tolerated, surfaced).
110-    Other(Envelope),
111-}
112-
113-/// A live brain attached to a broker.
114-pub struct Brain {
115-    conn: Stream,
116-    generation: u64,
117-    gen_start_ms: u64,


303-    \ Read and demux one broker event, enforcing exactly-once contiguous output:
304-    /// accept the next `seq` (advancing the cursor), drop an already-seen `seq`
305-    /// (replay duplicate), and reject a forward gap (lost chunk).
306:    pub fn read_event(&mut self) -> io::Result<BrokerEvent> {
307-        loop {
308-            let env = read_frame(&mut self.conn)?;
309-            match env.kind.as_str() {
310-                KIND_OUTPUT => {
311-                    let ev: OutputEvent = serde_json::from_value(env.payload)
312-                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
313-                    if ev.seq < self.next_seq {
314-                        continue; // already processed — a replay duplicate; dedup
315-                    }
316-                    if ev.seq > self.next_seq {
317-                        return Err(io::Error::new(
318-                            io::ErrorKind::InvalidData,
--
343-                        applied_now: ev.applied_now,
344-                    });
345-                }
346:                KIND_NET_STATUS_REPLY => {
347-                    let ev: NetStatusReply = serde_json::from_value(env.payload)
348-                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
349-                    return Ok(BrokerEvent::NetStatus(ev));
350-                }
351-                KIND_NET_DIALED => {
352-                    let ev: NetDialed = serde_json::from_value(env.payload)
353-                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
354-                    return Ok(BrokerEvent::NetDialed(ev));
355-                }
356-                KIND_NET_STREAM_OPENED => {
357-                    let ev: NetStreamOpened = serde_json::from_value(env.payload)
358-                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;


    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
    Sessions(SessionsReply),

    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
    Sessions(SessionsReply),
    /// The subnet-join ceremony's verdict (a `pair-joined` reply, M7 D3).
    PairJoined(PairJoinReply),


<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>

Error: File has not been read yet. Read it first before writing to it.


                KIND_NET_STATUS_REPLY => {
                    let ev: NetStatusReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::NetStatus(ev));
                }

                KIND_NET_STATUS_REPLY => {
                    let ev: NetStatusReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::NetStatus(ev));
                }
                KIND_PAIR_JOINED => {
                    let ev: PairJoinReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::PairJoined(ev));
                }


30
31	use crate::codec::{read_frame, write_frame};
32	use crate::digest::{ingest_chunk, DigestEngine};
33	use crate::digesthub::DigestHub;
34	use crate::frame::{Envelope, Role};
35	use crate::msg::{
36	    decode_bytes, encode_bytes, AppliedEvent, ErrorEvent, ExitEvent, InputReq, KillReq, NetDialReq,
37	    NetDialed, NetPresenceEvent, NetPresenceSubscribeReq, NetSent, NetStatusReply, NetStreamData,
38	    NetStreamEof, NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq,
39	    NetStreamsReply, OutputEvent, ResizeReq, SessionsReply, SpawnReq, Spawned, SubscribeReq,
40	    KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,
41	    KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS,
42	    KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA,
43	    KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
44	    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,
45	    KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
46	};
47	use crate::transport::{send_hello, LocalSocketTransport};
48
49	/// Wall-clock now in epoch milliseconds — the source for `gen_start` (matches
50	/// the epoch-ms stamping used elsewhere in the tree; no date dependency).
51	pub fn now_ms() -> u64 {
52	    SystemTime::now()
53	        .duration_since(UNIX_EPOCH)
54	        .map(|d| d.as_millis() as u64)
55	        .unwrap_or(0)
56	}
57
58	/// The brain's durable state across a handoff: enough to re-attach gaplessly and
59	/// advance the generation. `gen_start_ms` is recorded for observability but is
60	/// **deliberately not authoritative on restart** — [`Brain::handoff`] sets a
61	/// fresh `gen_start` from `now()` (2.4).
62	#[derive(Debug, Clone)]
63	pub struct BrainState {
64	    /// The session the brain was driving.
65	    pub session_id: u64,
66	    /// The generation counter (incremented on each handoff).
67	    pub generation: u64,
68	    /// The next output sequence still needed (the resume cursor).
69	    pub next_seq: u64,
70	    /// The prior generation's wall-clock start (ignored as authoritative on
71	    /// handoff — kept only to demonstrate it is *not* carried forward).
72	    pub gen_start_ms: u64,
73	}
74
75	/// A demuxed broker→brain event.
76	#[derive(Debug)]
77	pub enum BrokerEvent {
78	    /// A contiguous chunk of a session's output (the cursor has advanced past it).
79	    Output { session_id: u64, seq: u64, bytes: Vec<u8> },
80	    /// A spawn succeeded.
81	    Spawned { session_id: u64, pid: Option<u32> },
82	    /// A session's child exited.
83	    Exit { session_id: u64, code: Option<i32> },
84	    /// An idempotent effect is durably applied (or was deduped — `applied_now`
85	    /// distinguishes). The exactly-once ack for a [`Brain::send_effect`] (B5).
86	    Applied { session_id: u64, op_id: u64, applied_now: bool },
87	    /// The broker-owned net endpoint's status (a `net-status` reply, D4a).
88	    NetStatus(NetStatusReply),
89	    /// A net dial durably landed — fresh or deduped (D4a).
90	    NetDialed(NetDialed),
91	    /// A stream open durably landed — fresh or deduped (D4b).
92	    NetStreamOpened(NetStreamOpened),
93	    /// A journaled stream send durably landed — fresh or deduped (D4b).
94	    NetSent(NetSent),
95	    /// The broker's held-streams listing (D4b).
96	    NetStreams(NetStreamsReply),
97	    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
98	    Sessions(SessionsReply),
99	    /// A contiguous chunk of a stream's reads (the cursor has advanced past it).
100	    NetStreamData { stream_id: u64, seq: u64, bytes: Vec<u8> },
101	    /// A stream's read side finished cleanly; `next_seq` is the completeness
102	    /// check (the subscriber's cursor must equal it to have missed nothing).
103	    NetStreamEof { stream_id: u64, next_seq: u64 },
104	    /// A contiguous conn-liveness event (the presence cursor has advanced past
105	    /// it) — connected/disconnected on the broker's conn table (D4c).
106	    NetPresence(NetPresenceEvent),
107	    /// The broker reported a command error.
108	    Error { message: String },
109	    /// A frame of an unrecognized kind (forward-compat: tolerated, surfaced).
110	    Other(Envelope),
111	}
112
113	/// A live brain attached to a broker.
114	pub struct Brain {
115	    conn: Stream,
116	    generation: u64,
117	    gen_start_ms: u64,
118	    session_id: Option<u64>,
119	    /// The next output sequence still needed (resume cursor / dedup floor).

use crate::codec::{read_frame, write_frame};
use crate::digest::{ingest_chunk, DigestEngine};
use crate::digesthub::DigestHub;
use crate::frame::{Envelope, Role};
use crate::msg::{
    decode_bytes, encode_bytes, AppliedEvent, ErrorEvent, ExitEvent, InputReq, KillReq, NetDialReq,
    NetDialed, NetPresenceEvent, NetPresenceSubscribeReq, NetSent, NetStatusReply, NetStreamData,
    NetStreamEof, NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq,
    NetStreamsReply, OutputEvent, ResizeReq, SessionsReply, SpawnReq, Spawned, SubscribeReq,
    KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,
    KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS,
    KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA,
    KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,
    KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};
use crate::transport::{send_hello, LocalSocketTransport};

/// Wall-clock now in epoch milliseconds — the source for `gen_start` (matches
/// the epoch-ms stamping used elsewhere in the tree; no date dependency).
pub fn now_ms() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_millis() as u64)
        .unwrap_or(0)
}

/// The brain's durable state across a handoff: enough to re-attach gaplessly and
/// advance the generation. `gen_start_ms` is recorded for observability but is
/// **deliberately not authoritative on restart** — [`Brain::handoff`] sets a
/// fresh `gen_start` from `now()` (2.4).
#[derive(Debug, Clone)]
pub struct BrainState {
    /// The session the brain was driving.
    pub session_id: u64,
    /// The generation counter (incremented on each handoff).
    pub generation: u64,
    /// The next output sequence still needed (the resume cursor).
    pub next_seq: u64,
    /// The prior generation's wall-clock start (ignored as authoritative on
    /// handoff — kept only to demonstrate it is *not* carried forward).
    pub gen_start_ms: u64,
}

/// A demuxed broker→brain event.
#[derive(Debug)]
pub enum BrokerEvent {
    /// A contiguous chunk of a session's output (the cursor has advanced past it).
    Output { session_id: u64, seq: u64, bytes: Vec<u8> },
    /// A spawn succeeded.
    Spawned { session_id: u64, pid: Option<u32> },
    /// A session's child exited.
    Exit { session_id: u64, code: Option<i32> },
    /// An idempotent effect is durably applied (or was deduped — `applied_now`
    /// distinguishes). The exactly-once ack for a [`Brain::send_effect`] (B5).
    Applied { session_id: u64, op_id: u64, applied_now: bool },
    /// The broker-owned net endpoint's status (a `net-status` reply, D4a).
    NetStatus(NetStatusReply),
    /// A net dial durably landed — fresh or deduped (D4a).
    NetDialed(NetDialed),
    /// A stream open durably landed — fresh or deduped (D4b).
    NetStreamOpened(NetStreamOpened),
    /// A journaled stream send durably landed — fresh or deduped (D4b).
    NetSent(NetSent),
    /// The broker's held-streams listing (D4b).
    NetStreams(NetStreamsReply),
    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
    Sessions(SessionsReply),
    /// A contiguous chunk of a stream's reads (the cursor has advanced past it).
    NetStreamData { stream_id: u64, seq: u64, bytes: Vec<u8> },
    /// A stream's read side finished cleanly; `next_seq` is the completeness
    /// check (the subscriber's cursor must equal it to have missed nothing).
    NetStreamEof { stream_id: u64, next_seq: u64 },
    /// A contiguous conn-liveness event (the presence cursor has advanced past
    /// it) — connected/disconnected on the broker's conn table (D4c).
    NetPresence(NetPresenceEvent),
    /// The broker reported a command error.
    Error { message: String },
    /// A frame of an unrecognized kind (forward-compat: tolerated, surfaced).
    Other(Envelope),
}

/// A live brain attached to a broker.
pub struct Brain {
    conn: Stream,
    generation: u64,
    gen_start_ms: u64,
    session_id: Option<u64>,
    /// The next output sequence still needed (resume cursor / dedup floor).


    NetStreamsReply, OutputEvent, ResizeReq, SessionsReply, SpawnReq, Spawned, SubscribeReq,
    KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,
    KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS,
    KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA,
    KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,
    KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};

    NetStreamsReply, OutputEvent, PairJoinReply, PairJoinReq, ResizeReq, SessionsReply, SpawnReq,
    Spawned, SubscribeReq, KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL,
    KIND_NET_DIAL, KIND_NET_DIALED, KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE,
    KIND_NET_SENT, KIND_NET_STATUS, KIND_NET_STATUS_REPLY, KIND_NET_STREAMS,
    KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA, KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN,
    KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND, KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT,
    KIND_PAIR_JOIN, KIND_PAIR_JOINED, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,
    KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    NetStreamsReply, OutputEvent, ResizeReq, SessionsReply, SpawnReq, Spawned, SubscribeReq,
    KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,
    KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS,
    KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA,
    KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,
    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,
    KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};

    NetStreamsReply, OutputEvent, PairJoinReply, PairJoinReq, ResizeReq, SessionsReply, SpawnReq,
    Spawned, SubscribeReq, KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL,
    KIND_NET_DIAL, KIND_NET_DIALED, KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE,
    KIND_NET_SENT, KIND_NET_STATUS, KIND_NET_STATUS_REPLY, KIND_NET_STREAMS,
    KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA, KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN,
    KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND, KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT,
    KIND_PAIR_JOIN, KIND_PAIR_JOINED, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,
    KIND_SPAWN, KIND_SPAWNED, KIND_SUBSCRIBE,
};

decode_bytes, encode_bytes, AppliedEvent, ErrorEvent, ExitEvent, InputReq, KillReq, NetDialReq,

NetDialed, NetPresenceEvent, NetPresenceSubscribeReq, NetSent, NetStatusReply, NetStreamData,

NetStreamEof, NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq,

-    NetStreamsReply, OutputEvent, ResizeReq, SessionsReply, SpawnReq, Spawned, SubscribeReq,

-    KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIALED,

-    KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE, KIND_NET_SENT, KIND_NET_STATUS,

-    KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA,

-    KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND,

-    KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,

+    NetStreamsReply, OutputEvent, PairJoinReply, PairJoinReq, ResizeReq, SessionsReply, SpawnReq,

+    Spawned, SubscribeReq, KIND_APPLIED, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL,

+    KIND_NET_DIAL, KIND_NET_DIALED, KIND_NET_PRESENCE_EVENT, KIND_NET_PRESENCE_SUBSCRIBE,

+    KIND_NET_SENT, KIND_NET_STATUS, KIND_NET_STATUS_REPLY, KIND_NET_STREAMS,

+    KIND_NET_STREAMS_REPLY, KIND_NET_STREAM_DATA, KIND_NET_STREAM_EOF, KIND_NET_STREAM_OPEN,

+    KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND, KIND_NET_STREAM_SUBSCRIBE, KIND_OUTPUT,

+    KIND_PAIR_JOIN, KIND_PAIR_JOINED, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY,


    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
    Sessions(SessionsReply),

    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
    Sessions(SessionsReply),
    /// The subnet-join ceremony's verdict (a `pair-joined` reply, M7 D3).
    PairJoined(PairJoinReply),


    /// The broker's hosted-sessions listing (D9-1: session → endpoint).
    Sessions(SessionsReply),
    /// The subnet-join ceremony's verdict (a `pair-joined` reply, M7 D3).
    PairJoined(PairJoinReply),

+    /// The subnet-join ceremony's verdict (a `pair-joined` reply, M7 D3).

/// A contiguous chunk of a stream's reads (the cursor has advanced past it).

NetStreamData { stream_id: u64, seq: u64, bytes: Vec<u8> },

/// A stream's read side finished cleanly; `next_seq` is the completeness


Bash

grep -n "KIND_SESSIONS_REPLY =>" crates/spt-daemon/src/brain.rs

Find sessions reply arm location


374:                KIND_SESSIONS_REPLY => {

374:                KIND_SESSIONS_REPLY => {


374	                KIND_SESSIONS_REPLY => {
375	                    let ev: SessionsReply = serde_json::from_value(env.payload)
376	                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
377	                    return Ok(BrokerEvent::Sessions(ev));
378	                }
379	                KIND_NET_STREAM_DATA => {
380	                    let ev: NetStreamData = serde_json::from_value(env.payload)
381	                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
382	                    // Same exactly-once discipline as PTY output, per stream:
383	                    // accept the contiguous next seq, drop a replay duplicate,
384	                    // reject a forward gap (a lost chunk must not pass silently).
385	                    let cursor = self.net_cursors.entry(ev.stream_id).or_insert(ev.seq);
386	                    if ev.seq < *cursor {
387	                        continue; // replay duplicate — dedup

                KIND_SESSIONS_REPLY => {
                    let ev: SessionsReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::Sessions(ev));
                }
                KIND_NET_STREAM_DATA => {
                    let ev: NetStreamData = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    // Same exactly-once discipline as PTY output, per stream:
                    // accept the contiguous next seq, drop a replay duplicate,
                    // reject a forward gap (a lost chunk must not pass silently).
                    let cursor = self.net_cursors.entry(ev.stream_id).or_insert(ev.seq);
                    if ev.seq < *cursor {
                        continue; // replay duplicate — dedup


Edit

                KIND_SESSIONS_REPLY => {
                    let ev: SessionsReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::Sessions(ev));
                }

                KIND_SESSIONS_REPLY => {
                    let ev: SessionsReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::Sessions(ev));
                }
                KIND_PAIR_JOINED => {
                    let ev: PairJoinReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::PairJoined(ev));
                }


                KIND_SESSIONS_REPLY => {
                    let ev: SessionsReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::Sessions(ev));
                }
                KIND_PAIR_JOINED => {
                    let ev: PairJoinReply = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::PairJoined(ev));
                }

+                    let ev: PairJoinReply = serde_json::from_value(env.payload)

+                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

+                    return Ok(BrokerEvent::PairJoined(ev));

let ev: NetStreamData = serde_json::from_value(env.payload)


500	        loop {
501	            match self.read_event()? {
502	                BrokerEvent::NetStatus(s) => return Ok(s),
503	                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
504	                _ => continue,
505	            }
506	        }
507	    }
508
509	    /// Dial a peer over the broker-owned endpoint (D4a). `addr` is the peer's
510	    /// serialized `EndpointAddr` (as another node's `net-status` reply carries).
511	    /// With `op_id` the dial is exactly-once: a replay of the same durable id
512	    /// after a brain crash is deduped at the broker and answered with the
513	    /// already-open connection ([`NetDialed::applied_now`] distinguishes).
514	    // [impl->REQ-HAZARD-RESTART-IDEMPOTENT]
515	    pub fn net_dial(
516	        &mut self,
517	        addr: serde_json::Value,
518	        op_id: Option<u64>,
519	    ) -> io::Result<NetDialed> {
520	        self.send(
521	            KIND_NET_DIAL,
522	            serde_json::to_value(NetDialReq { addr, op_id }).expect("NetDialReq serializes"),
523	        )?;
524	        loop {
525	            match self.read_event()? {
526	                BrokerEvent::NetDialed(d) => return Ok(d),
527	                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
528	                _ => continue,
529	            }
530	        }
531	    }
532
533	    /// Open a bidi stream on a held connection (D4b). With `op_id` the open is
534	    /// exactly-once (a replay is answered with the original stream). NOTE all

        loop {
            match self.read_event()? {
                BrokerEvent::NetStatus(s) => return Ok(s),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }

    /// Dial a peer over the broker-owned endpoint (D4a). `addr` is the peer's
    /// serialized `EndpointAddr` (as another node's `net-status` reply carries).
    /// With `op_id` the dial is exactly-once: a replay of the same durable id
    /// after a brain crash is deduped at the broker and answered with the
    /// already-open connection ([`NetDialed::applied_now`] distinguishes).
    // [impl->REQ-HAZARD-RESTART-IDEMPOTENT]
    pub fn net_dial(
        &mut self,
        addr: serde_json::Value,
        op_id: Option<u64>,
    ) -> io::Result<NetDialed> {
        self.send(
            KIND_NET_DIAL,
            serde_json::to_value(NetDialReq { addr, op_id }).expect("NetDialReq serializes"),
        )?;
        loop {
            match self.read_event()? {
                BrokerEvent::NetDialed(d) => return Ok(d),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }

    /// Open a bidi stream on a held connection (D4b). With `op_id` the open is
    /// exactly-once (a replay is answered with the original stream). NOTE all


    /// Dial a peer over the broker-owned endpoint (D4a). `addr` is the peer's
    /// serialized `EndpointAddr` (as another node's `net-status` reply carries).

    /// Run the joiner leg of the subnet pairing ceremony on the broker (M7
    /// D3, REQ-SUBNET-2): the broker owns the node-identity endpoint the
    /// ceremony binds and pins. Blocks for the ceremony's bounded life; the
    /// reply classifies failures for the CLI's guided UX. Not journaled —
    /// the code must never persist, and a re-join is store-idempotent.
    // [impl->REQ-SUBNET-2]
    pub fn pair_join(&mut self, subnet: &str, code: &str) -> io::Result<PairJoinReply> {
        self.send(
            KIND_PAIR_JOIN,
            serde_json::to_value(PairJoinReq { subnet: subnet.to_string(), code: code.to_string() })
                .expect("PairJoinReq serializes"),
        )?;
        loop {
            match self.read_event()? {
                BrokerEvent::PairJoined(r) => return Ok(r),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }

    /// Dial a peer over the broker-owned endpoint (D4a). `addr` is the peer's
    /// serialized `EndpointAddr` (as another node's `net-status` reply carries).


    /// Run the joiner leg of the subnet pairing ceremony on the broker (M7
    /// D3, REQ-SUBNET-2): the broker owns the node-identity endpoint the
    /// ceremony binds and pins. Blocks for the ceremony's bounded life; the
    /// reply classifies failures for the CLI's guided UX. Not journaled —
    /// the code must never persist, and a re-join is store-idempotent.
    // [impl->REQ-SUBNET-2]
    pub fn pair_join(&mut self, subnet: &str, code: &str) -> io::Result<PairJoinReply> {
        self.send(
            KIND_PAIR_JOIN,
            serde_json::to_value(PairJoinReq { subnet: subnet.to_string(), code: code.to_string() })
                .expect("PairJoinReq serializes"),
        )?;
        loop {
            match self.read_event()? {
                BrokerEvent::PairJoined(r) => return Ok(r),
                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
                _ => continue,
            }
        }
    }

    /// Dial a peer over the broker-owned endpoint (D4a). `addr` is the peer's
    /// serialized `EndpointAddr` (as another node's `net-status` reply carries).

+    /// Run the joiner leg of the subnet pairing ceremony on the broker (M7

+    /// D3, REQ-SUBNET-2): the broker owns the node-identity endpoint the

+    /// ceremony binds and pins. Blocks for the ceremony's bounded life; the

+    /// reply classifies failures for the CLI's guided UX. Not journaled —

+    /// the code must never persist, and a re-join is store-idempotent.

+    pub fn pair_join(&mut self, subnet: &str, code: &str) -> io::Result<PairJoinReply> {

+            serde_json::to_value(PairJoinReq { subnet: subnet.to_string(), code: code.to_string() })

+                .expect("PairJoinReq serializes"),

+                BrokerEvent::PairJoined(r) => return Ok(r),

+                BrokerEvent::Error { message } => return Err(io::Error::other(message)),

/// With `op_id` the dial is exactly-once: a replay of the same durable id


Bash

cargo check -p spt-daemon --all-targets 2>&1 | grep -E "^error" -A6 | head -30; cargo check -p spt-daemon --all-targets 2>&1 | tail -1

Check daemon compiles with IPC additions


Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.46s


D3b done. CLI join (task 11):


Updated task #11 status


Edit

    /// Show a subnet's current 6-digit pairing code + `otpauth://` URI.
    ///
    /// Gated behind OS elevation (or read the code from your authenticator
    /// app). With no name the node's sole subnet is used; if it holds
    /// several, the name is required (never guessed).
    ShowTotp {
        /// Which subnet's code to show. Required only when the node holds several.
        name: Option<String>,
    },
}

    /// Show a subnet's current 6-digit pairing code + `otpauth://` URI.
    ///
    /// Gated behind OS elevation (or read the code from your authenticator
    /// app). With no name the node's sole subnet is used; if it holds
    /// several, the name is required (never guessed).
    ShowTotp {
        /// Which subnet's code to show. Required only when the node holds several.
        name: Option<String>,
    },
    /// Pair this machine into an existing subnet (guided).
    ///
    /// Finds a member machine over LAN + relay rendezvous and runs the
    /// code-authenticated pairing ceremony against it. Prompts for the
    /// name and code when omitted (interactive terminals). Gated behind
    /// OS elevation — joining enrolls this whole machine.
    Join {
        /// The subnet to join (as named on the member machine).
        name: Option<String>,
        /// The current 6-digit code (`spt subnet show-totp` on a member
        /// machine, or your authenticator app).
        #[arg(long)]
        code: Option<String>,
    },
}


The file C:\Users\decid\Documents\projects\spt-core\crates\spt\src\cli.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    /// Show a subnet's current 6-digit pairing code + `otpauth://` URI.
    ///
    /// Gated behind OS elevation (or read the code from your authenticator
    /// app). With no name the node's sole subnet is used; if it holds
    /// several, the name is required (never guessed).
    ShowTotp {
        /// Which subnet's code to show. Required only when the node holds several.
        name: Option<String>,
    },
}

    /// Show a subnet's current 6-digit pairing code + `otpauth://` URI.
    ///
    /// Gated behind OS elevation (or read the code from your authenticator
    /// app). With no name the node's sole subnet is used; if it holds
    /// several, the name is required (never guessed).
    ShowTotp {
        /// Which subnet's code to show. Required only when the node holds several.
        name: Option<String>,
    },
    /// Pair this machine into an existing subnet (guided).
    ///
    /// Finds a member machine over LAN + relay rendezvous and runs the
    /// code-authenticated pairing ceremony against it. Prompts for the
    /// name and code when omitted (interactive terminals). Gated behind
    /// OS elevation — joining enrolls this whole machine.
    Join {
        /// The subnet to join (as named on the member machine).
        name: Option<String>,
        /// The current 6-digit code (`spt subnet show-totp` on a member
        /// machine, or your authenticator app).
        #[arg(long)]
        code: Option<String>,
    },
}

+    /// Pair this machine into an existing subnet (guided).

+    /// Finds a member machine over LAN + relay rendezvous and runs the

+    /// code-authenticated pairing ceremony against it. Prompts for the

+    /// name and code when omitted (interactive terminals). Gated behind

+    /// OS elevation — joining enrolls this whole machine.

+        /// The subnet to join (as named on the member machine).

+        /// The current 6-digit code (`spt subnet show-totp` on a member

+        /// machine, or your authenticator app).

/// Top-level commands an agent is meant to run from its own session; every


            match action.unwrap_or(SubnetCmd::Status { name: None, nodes: false }) {
                SubnetCmd::Status { name, nodes } => cmd_subnet_status(name, nodes),
                SubnetCmd::Create { name } => cmd_subnet_create(name),
                SubnetCmd::ShowTotp { name } => cmd_subnet_show_totp(name),
            }

            match action.unwrap_or(SubnetCmd::Status { name: None, nodes: false }) {
                SubnetCmd::Status { name, nodes } => cmd_subnet_status(name, nodes),
                SubnetCmd::Create { name } => cmd_subnet_create(name),
                SubnetCmd::ShowTotp { name } => cmd_subnet_show_totp(name),
                SubnetCmd::Join { name, code } => cmd_subnet_join(name, code),
            }


            match action.unwrap_or(SubnetCmd::Status { name: None, nodes: false }) {
                SubnetCmd::Status { name, nodes } => cmd_subnet_status(name, nodes),
                SubnetCmd::Create { name } => cmd_subnet_create(name),
                SubnetCmd::ShowTotp { name } => cmd_subnet_show_totp(name),
            }

            match action.unwrap_or(SubnetCmd::Status { name: None, nodes: false }) {
                SubnetCmd::Status { name, nodes } => cmd_subnet_status(name, nodes),
                SubnetCmd::Create { name } => cmd_subnet_create(name),
                SubnetCmd::ShowTotp { name } => cmd_subnet_show_totp(name),
                SubnetCmd::Join { name, code } => cmd_subnet_join(name, code),
            }

+                SubnetCmd::Join { name, code } => cmd_subnet_join(name, code),

Cmd::HowTo { topic } => cmd_how_to(topic.as_deref()),


    out.push_str(
        "\nhint: `spt subnet create <name>` mints a new subnet on this machine\n\
         hint: `spt subnet show-totp [name]` shows a subnet's joining code (elevated)\n",
    );
    out
}

    out.push_str(HINT_FOOTER);
    out
}

/// The status views' standing hint footer (M7 plan decision 5: always
/// printed — the next verb is never a doc lookup away).
const HINT_FOOTER: &str = "\nhint: `spt subnet create <name>` mints a new subnet on this machine\n\
     hint: `spt subnet join <name>` pairs this machine into one (elevated)\n\
     hint: `spt subnet show-totp [name]` shows a subnet's joining code (elevated)\n";


    out.push_str(
        "\nhint: `spt subnet create <name>` mints a new subnet on this machine\n\
         hint: `spt subnet show-totp [name]` shows a subnet's joining code (elevated)\n",
    );
    out
}

    out.push_str(HINT_FOOTER);
    out
}

/// The status views' standing hint footer (M7 plan decision 5: always
/// printed — the next verb is never a doc lookup away).
const HINT_FOOTER: &str = "\nhint: `spt subnet create <name>` mints a new subnet on this machine\n\
     hint: `spt subnet join <name>` pairs this machine into one (elevated)\n\
     hint: `spt subnet show-totp [name]` shows a subnet's joining code (elevated)\n";

out.push_str(&format!("{:<pad$}  {:<5}  {}\n", r.name, r.nodes, r.endpoints));

-        "\nhint: `spt subnet create <name>` mints a new subnet on this machine\n\

-         hint: `spt subnet show-totp [name]` shows a subnet's joining code (elevated)\n",

+/// The status views' standing hint footer (M7 plan decision 5: always

+/// printed — the next verb is never a doc lookup away).

+const HINT_FOOTER: &str = "\nhint: `spt subnet create <name>` mints a new subnet on this machine\n\

+     hint: `spt subnet join <name>` pairs this machine into one (elevated)\n\

+     hint: `spt subnet show-totp [name]` shows a subnet's joining code (elevated)\n";

/// One node's `status --nodes` row (M7 plan decision 6): identity, advertised

/// label, endpoint counts, and the gossip-recency liveness verdict.


    if out.is_empty() {
        out.push_str(
            "This node belongs to no subnet — a standalone node. Local messaging \
             works without one; a subnet links your machines into one fabric.\n",
        );
    }
    out.push_str(
        "\nhint: `spt subnet create <name>` mints a new subnet on this machine\n\
         hint: `spt subnet show-totp [name]` shows a subnet's joining code (elevated)\n",
    );
    print!("{out}");
    let _ = std::io::stdout().flush();
    0
}

    if out.is_empty() {
        out.push_str(
            "This node belongs to no subnet — a standalone node. Local messaging \
             works without one; a subnet links your machines into one fabric.\n",
        );
    }
    out.push_str(HINT_FOOTER);
    print!("{out}");
    let _ = std::io::stdout().flush();
    0
}

/// The join command's elevation verdict — pure over the probed level
/// (REQ-SUBNET-4): joining enrolls the MACHINE into a subnet's trust
/// fabric, a mutation an unprivileged process must not perform (an
/// attacker-run joiner would enroll the box into the attacker's subnet).
/// Same fail-safe posture as the seed-reveal gates: only confirmed
/// elevation proceeds.
// [impl->REQ-SUBNET-4]
fn join_elevation_refusal(elevation: Elevation) -> Option<String> {
    match elevation::gate_for(elevation) {
        Gate::Show => None,
        Gate::Fallback => Some(
            "ELEVATION_REQUIRED: joining a subnet enrolls this machine into its trust \
             fabric — re-run elevated (run as administrator / root)"
                .to_string(),
        ),
    }
}

/// Read one interactive line from the terminal (prompts on stderr so stdout
/// stays clean). `None` when stdin is not a terminal or the line is empty —
/// the caller falls back to a usage error instead of hanging a pipe.
fn prompt_line(label: &str) -> Option<String> {
    use std::io::IsTerminal;
    if !std::io::stdin().is_terminal() {
        return None;
    }
    eprint!("{label}");
    let _ = std::io::stderr().flush();
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok()?;
    let s = s.trim().to_string();
    (!s.is_empty()).then_some(s)
}

/// `spt subnet join [NAME] [--code <CODE>]` — the guided join flow (M7 plan
/// decision 10, REQ-SUBNET-2): elevation gate → prompt the missing inputs →
/// the daemon runs the rendezvous + ceremony on the node-identity endpoint →
/// wrong codes re-prompt (codes rotate; bounded attempts), rate limits
/// surface honestly, and a missing seed-holder gets actionable hints.
// [impl->REQ-SUBNET-2] [impl->REQ-SUBNET-4]
fn cmd_subnet_join(name: Option<String>, code: Option<String>) -> i32 {
    // The gate runs FIRST — before any prompt or network syllable
    // (REQ-SUBNET-4: refused = nothing typed, nothing dialed, nothing saved).
    if let Some(msg) = join_elevation_refusal(elevation::current()) {
        eprintln!("{msg}");
        return EXIT_NOT_ELEVATED;
    }
    let Some(name) = name.or_else(|| prompt_line("Subnet name: ")) else {
        eprintln!("USAGE: a subnet name is required — `spt subnet join <name>` (or run interactively)");
        return 2;
    };
    if SubnetStore::load().find(&name).is_some() {
        eprintln!("ALREADY_MEMBER:{name} — this machine already holds the subnet");
        return 0;
    }
    let interactive = code.is_none();
    let Some(mut code) = code.or_else(|| prompt_line("6-digit code: ")) else {
        eprintln!("USAGE: the current 6-digit code is required — `--code <CODE>` (or run interactively)");
        return 2;
    };

    if spt_daemon::ensure_running().is_err() {
        eprintln!("DAEMON_START_FAIL: the join ceremony runs on the daemon's endpoint");
        return 1;
    }
    let mut brain =
        match spt_daemon::brain::Brain::cold_start(&spt_daemon::broker_socket_name(), now_ms()) {
            Ok(b) => b,
            Err(e) => {
                eprintln!("BROKER_CONNECT_FAIL: {e}");
                return 1;
            }
        };

    const MAX_ATTEMPTS: usize = 3;
    for attempt in 1..=MAX_ATTEMPTS {
        eprintln!("Searching for a \"{name}\" member (LAN + relay)…");
        let reply = match brain.pair_join(&name, &code) {
            Ok(r) => r,
            Err(e) => {
                eprintln!("JOIN_FAIL:{name}: {e}");
                return 1;
            }
        };
        if reply.ok {
            println!("JOINED:{name}");
            // First-join home adoption (REQ-INST-15): fires on join exactly
            // as on create — the daemon saved the seed; local-only Self
            // perches adopt the fresh sole subnet now rather than waiting
            // one advertisement cadence.
            let store = SubnetStore::load();
            let mut vis = spt_store::visibility::VisibilityStore::load();
            let adopted = spt_store::home::adopt_for_unset(&perch::owlery_dir(), &store, &mut vis);
            if !adopted.is_empty() {
                let _ = vis.save();
                eprintln!("HOME_ADOPTED:{}", adopted.join(","));
            }
            eprintln!(
                "Joined \"{name}\" ({}). `spt subnet status --nodes` shows the fleet \
                 as gossip lands.",
                reply.detail
            );
            return 0;
        }
        match reply.class.as_deref() {
            // Wrong code: re-prompt against a FRESH window (codes rotate
            // every 30s) — bounded so a typo can't become a guessing loop.
            Some("wrong-code") if interactive && attempt < MAX_ATTEMPTS => {
                match prompt_line(
                    "Wrong code (codes rotate every 30s) — enter the CURRENT code: ",
                ) {
                    Some(next) => code = next,
                    None => {
                        eprintln!("JOIN_FAIL:{name}: wrong code");
                        return 1;
                    }
                }
            }
            Some("wrong-code") => {
                eprintln!(
                    "JOIN_FAIL:{name}: wrong code — read the CURRENT code off the member \
                     machine (`spt subnet show-totp`) and retry"
                );
                return 1;
            }
            // The rate limiter speaks for itself — relay it honestly, never
            // retry into it automatically.
            Some("rate-limited") => {
                match reply.retry_after_secs {
                    Some(s) => eprintln!(
                        "JOIN_RATE_LIMITED:{name}: too many failed attempts — retry in {s}s"
                    ),
                    None => eprintln!(
                        "JOIN_BUSY:{name}: the member is mid-ceremony with another machine — \
                         retry in a moment"
                    ),
                }
                return 1;
            }
            Some("no-seed-holder") => {
                eprintln!(
                    "NO_SEED_HOLDER:{name}: no member answered the rendezvous.\n\
                     - Is a member machine online (its spt daemon running)?\n\
                     - Same subnet name on both sides? (names are exact)\n\
                     - Are both clocks within ±30s? (the rendezvous derives from the time)"
                );
                return 1;
            }
            Some("clock-skew") => {
                eprintln!(
                    "JOIN_FAIL:{name}: the member rejected this machine's clock — sync time \
                     (within ±30s) and retry"
                );
                return 1;
            }
            Some("unknown-subnet") => {
                eprintln!("JOIN_FAIL:{name}: the answering machine holds no subnet by that name");
                return 1;
            }
            _ => {
                eprintln!("JOIN_FAIL:{name}: {}", reply.detail);
                return 1;
            }
        }
    }
    eprintln!("JOIN_FAIL:{name}: attempts exhausted");
    1
}


    if out.is_empty() {
        out.push_str(
            "This node belongs to no subnet — a standalone node. Local messaging \
             works without one; a subnet links your machines into one fabric.\n",
        );
    }
    out.push_str(
        "\nhint: `spt subnet create <name>` mints a new subnet on this machine\n\
         hint: `spt subnet show-totp [name]` shows a subnet's joining code (elevated)\n",
    );
    print!("{out}");
    let _ = std::io::stdout().flush();
    0
}

    if out.is_empty() {
        out.push_str(
            "This node belongs to no subnet — a standalone node. Local messaging \
             works without one; a subnet links your machines into one fabric.\n",
        );
    }
    out.push_str(HINT_FOOTER);
    print!("{out}");
    let _ = std::io::stdout().flush();
    0
}

/// The join command's elevation verdict — pure over the probed level
/// (REQ-SUBNET-4): joining enrolls the MACHINE into a subnet's trust
/// fabric, a mutation an unprivileged process must not perform (an
/// attacker-run joiner would enroll the box into the attacker's subnet).
/// Same fail-safe posture as the seed-reveal gates: only confirmed
/// elevation proceeds.
// [impl->REQ-SUBNET-4]
fn join_elevation_refusal(elevation: Elevation) -> Option<String> {
    match elevation::gate_for(elevation) {
        Gate::Show => None,
        Gate::Fallback => Some(
            "ELEVATION_REQUIRED: joining a subnet enrolls this machine into its trust \
             fabric — re-run elevated (run as administrator / root)"
                .to_string(),
        ),
    }
}

/// Read one interactive line from the terminal (prompts on stderr so stdout
/// stays clean). `None` when stdin is not a terminal or the line is empty —
/// the caller falls back to a usage error instead of hanging a pipe.
fn prompt_line(label: &str) -> Option<String> {
    use std::io::IsTerminal;
    if !std::io::stdin().is_terminal() {
        return None;
    }
    eprint!("{label}");
    let _ = std::io::stderr().flush();
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok()?;
    let s = s.trim().to_string();
    (!s.is_empty()).then_some(s)
}

/// `spt subnet join [NAME] [--code <CODE>]` — the guided join flow (M7 plan
/// decision 10, REQ-SUBNET-2): elevation gate → prompt the missing inputs →
/// the daemon runs the rendezvous + ceremony on the node-identity endpoint →
/// wrong codes re-prompt (codes rotate; bounded attempts), rate limits
/// surface honestly, and a missing seed-holder gets actionable hints.
// [impl->REQ-SUBNET-2] [impl->REQ-SUBNET-4]
fn cmd_subnet_join(name: Option<String>, code: Option<String>) -> i32 {
    // The gate runs FIRST — before any prompt or network syllable
    // (REQ-SUBNET-4: refused = nothing typed, nothing dialed, nothing saved).
    if let Some(msg) = join_elevation_refusal(elevation::current()) {
        eprintln!("{msg}");
        return EXIT_NOT_ELEVATED;
    }
    let Some(name) = name.or_else(|| prompt_line("Subnet name: ")) else {
        eprintln!("USAGE: a subnet name is required — `spt subnet join <name>` (or run interactively)");
        return 2;
    };
    if SubnetStore::load().find(&name).is_some() {
        eprintln!("ALREADY_MEMBER:{name} — this machine already holds the subnet");
        return 0;
    }
    let interactive = code.is_none();
    let Some(mut code) = code.or_else(|| prompt_line("6-digit code: ")) else {
        eprintln!("USAGE: the current 6-digit code is required — `--code <CODE>` (or run interactively)");
        return 2;
    };

    if spt_daemon::ensure_running().is_err() {
        eprintln!("DAEMON_START_FAIL: the join ceremony runs on the daemon's endpoint");
        return 1;
    }
    let mut brain =
        match spt_daemon::brain::Brain::cold_start(&spt_daemon::broker_socket_name(), now_ms()) {
            Ok(b) => b,
            Err(e) => {
                eprintln!("BROKER_CONNECT_FAIL: {e}");
                return 1;
            }
        };

    const MAX_ATTEMPTS: usize = 3;
    for attempt in 1..=MAX_ATTEMPTS {
        eprintln!("Searching for a \"{name}\" member (LAN + relay)…");
        let reply = match brain.pair_join(&name, &code) {
            Ok(r) => r,
            Err(e) => {
                eprintln!("JOIN_FAIL:{name}: {e}");
                return 1;
            }
        };
        if reply.ok {
            println!("JOINED:{name}");
            // First-join home adoption (REQ-INST-15): fires on join exactly
            // as on create — the daemon saved the seed; local-only Self
            // perches adopt the fresh sole subnet now rather than waiting
            // one advertisement cadence.
            let store = SubnetStore::load();
            let mut vis = spt_store::visibility::VisibilityStore::load();
            let adopted = spt_store::home::adopt_for_unset(&perch::owlery_dir(), &store, &mut vis);
            if !adopted.is_empty() {
                let _ = vis.save();
                eprintln!("HOME_ADOPTED:{}", adopted.join(","));
            }
            eprintln!(
                "Joined \"{name}\" ({}). `spt subnet status --nodes` shows the fleet \
                 as gossip lands.",
                reply.detail
            );
            return 0;
        }
        match reply.class.as_deref() {
            // Wrong code: re-prompt against a FRESH window (codes rotate
            // every 30s) — bounded so a typo can't become a guessing loop.
            Some("wrong-code") if interactive && attempt < MAX_ATTEMPTS => {
                match prompt_line(
                    "Wrong code (codes rotate every 30s) — enter the CURRENT code: ",
                ) {
                    Some(next) => code = next,
                    None => {
                        eprintln!("JOIN_FAIL:{name}: wrong code");
                        return 1;
                    }
                }
            }
            Some("wrong-code") => {
                eprintln!(
                    "JOIN_FAIL:{name}: wrong code — read the CURRENT code off the member \
                     machine (`spt subnet show-totp`) and retry"
                );
                return 1;
            }
            // The rate limiter speaks for itself — relay it honestly, never
            // retry into it automatically.
            Some("rate-limited") => {
                match reply.retry_after_secs {
                    Some(s) => eprintln!(
                        "JOIN_RATE_LIMITED:{name}: too many failed attempts — retry in {s}s"
                    ),
                    None => eprintln!(
                        "JOIN_BUSY:{name}: the member is mid-ceremony with another machine — \
                         retry in a moment"
                    ),
                }
                return 1;
            }
            Some("no-seed-holder") => {
                eprintln!(
                    "NO_SEED_HOLDER:{name}: no member answered the rendezvous.\n\
                     - Is a member machine online (its spt daemon running)?\n\
                     - Same subnet name on both sides? (names are exact)\n\
                     - Are both clocks within ±30s? (the rendezvous derives from the time)"
                );
                return 1;
            }
            Some("clock-skew") => {
                eprintln!(
                    "JOIN_FAIL:{name}: the member rejected this machine's clock — sync time \
                     (within ±30s) and retry"
                );
                return 1;
            }
            Some("unknown-subnet") => {
                eprintln!("JOIN_FAIL:{name}: the answering machine holds no subnet by that name");
                return 1;
            }
            _ => {
                eprintln!("JOIN_FAIL:{name}: {}", reply.detail);
                return 1;
            }
        }
    }
    eprintln!("JOIN_FAIL:{name}: attempts exhausted");
    1
}

+/// The join command's elevation verdict — pure over the probed level

+/// (REQ-SUBNET-4): joining enrolls the MACHINE into a subnet's trust

+/// fabric, a mutation an unprivileged process must not perform (an

+/// attacker-run joiner would enroll the box into the attacker's subnet).

+/// Same fail-safe posture as the seed-reveal gates: only confirmed

+fn join_elevation_refusal(elevation: Elevation) -> Option<String> {

+    match elevation::gate_for(elevation) {

+            "ELEVATION_REQUIRED: joining a subnet enrolls this machine into its trust \

+             fabric — re-run elevated (run as administrator / root)"

+/// Read one interactive line from the terminal (prompts on stderr so stdout

+/// stays clean). `None` when stdin is not a terminal or the line is empty —

+/// the caller falls back to a usage error instead of hanging a pipe.

+fn prompt_line(label: &str) -> Option<String> {

+    if !std::io::stdin().is_terminal() {

+    std::io::stdin().read_line(&mut s).ok()?;

+/// `spt subnet join [NAME] [--code <CODE>]` — the guided join flow (M7 plan

+/// decision 10, REQ-SUBNET-2): elevation gate → prompt the missing inputs →

+/// the daemon runs the rendezvous + ceremony on the node-identity endpoint →

+/// wrong codes re-prompt (codes rotate; bounded attempts), rate limits

+/// surface honestly, and a missing seed-holder gets actionable hints.

+fn cmd_subnet_join(name: Option<String>, code: Option<String>) -> i32 {

+    // The gate runs FIRST — before any prompt or network syllable

+    // (REQ-SUBNET-4: refused = nothing typed, nothing dialed, nothing saved).

+    if let Some(msg) = join_elevation_refusal(elevation::current()) {

+    let Some(name) = name.or_else(|| prompt_line("Subnet name: ")) else {

+        eprintln!("USAGE: a subnet name is required — `spt subnet join <name>` (or run interactively)");

+    if SubnetStore::load().find(&name).is_some() {

+        eprintln!("ALREADY_MEMBER:{name} — this machine already holds the subnet");

+    let Some(mut code) = code.or_else(|| prompt_line("6-digit code: ")) else {

+        eprintln!("USAGE: the current 6-digit code is required — `--code <CODE>` (or run interactively)");

+    if spt_daemon::ensure_running().is_err() {

+        eprintln!("DAEMON_START_FAIL: the join ceremony runs on the daemon's endpoint");

+        match spt_daemon::brain::Brain::cold_start(&spt_daemon::broker_socket_name(), now_ms()) {

+                eprintln!("BROKER_CONNECT_FAIL: {e}");

+        eprintln!("Searching for a \"{name}\" member (LAN + relay)…");

+        let reply = match brain.pair_join(&name, &code) {

+                eprintln!("JOIN_FAIL:{name}: {e}");

+            // First-join home adoption (REQ-INST-15): fires on join exactly

+            // as on create — the daemon saved the seed; local-only Self

+            // perches adopt the fresh sole subnet now rather than waiting

+            // one advertisement cadence.

+            let store = SubnetStore::load();

+            let mut vis = spt_store::visibility::VisibilityStore::load();

+            let adopted = spt_store::home::adopt_for_unset(&perch::owlery_dir(), &store, &mut vis);

+                eprintln!("HOME_ADOPTED:{}", adopted.join(","));

+                "Joined \"{name}\" ({}). `spt subnet status --nodes` shows the fleet \

+            // Wrong code: re-prompt against a FRESH window (codes rotate

+            // every 30s) — bounded so a typo can't become a guessing loop.

+            Some("wrong-code") if interactive && attempt < MAX_ATTEMPTS => {

+                    "Wrong code (codes rotate every 30s) — enter the CURRENT code: ",

+                    Some(next) => code = next,

+                        eprintln!("JOIN_FAIL:{name}: wrong code");

+                    "JOIN_FAIL:{name}: wrong code — read the CURRENT code off the member \

+                     machine (`spt subnet show-totp`) and retry"

+            // The rate limiter speaks for itself — relay it honestly, never

+            // retry into it automatically.

+                match reply.retry_after_secs {

+                    Some(s) => eprintln!(

+                        "JOIN_RATE_LIMITED:{name}: too many failed attempts — retry in {s}s"

+                        "JOIN_BUSY:{name}: the member is mid-ceremony with another machine — \

+                         retry in a moment"

+            Some("no-seed-holder") => {

+                    "NO_SEED_HOLDER:{name}: no member answered the rendezvous.\n\

+                     - Is a member machine online (its spt daemon running)?\n\

+                     - Same subnet name on both sides? (names are exact)\n\

+                     - Are both clocks within ±30s? (the rendezvous derives from the time)"

+                    "JOIN_FAIL:{name}: the member rejected this machine's clock — sync time \

+                     (within ±30s) and retry"

+            Some("unknown-subnet") => {

+                eprintln!("JOIN_FAIL:{name}: the answering machine holds no subnet by that name");

+                eprintln!("JOIN_FAIL:{name}: {}", reply.detail);

+    eprintln!("JOIN_FAIL:{name}: attempts exhausted");

/// `spt how-to ready` — listener guidance for agents.


Add join tests + retag create gate test:


        assert!(matches!(
            subnet_cmd(&["spt", "subnet", "show-totp", "home"]),
            Some(SubnetCmd::ShowTotp { name: Some(n) }) if n == "home"
        ));
    }

        assert!(matches!(
            subnet_cmd(&["spt", "subnet", "show-totp", "home"]),
            Some(SubnetCmd::ShowTotp { name: Some(n) }) if n == "home"
        ));
        // [unit->REQ-SUBNET-2] the join shapes: fully interactive (bare),
        // name-only (prompts the code), and fully non-interactive.
        assert!(matches!(
            subnet_cmd(&["spt", "subnet", "join"]),
            Some(SubnetCmd::Join { name: None, code: None })
        ));
        assert!(matches!(
            subnet_cmd(&["spt", "subnet", "join", "home"]),
            Some(SubnetCmd::Join { name: Some(n), code: None }) if n == "home"
        ));
        assert!(matches!(
            subnet_cmd(&["spt", "subnet", "join", "home", "--code", "123456"]),
            Some(SubnetCmd::Join { name: Some(_), code: Some(c) }) if c == "123456"
        ));
    }

    // [unit->REQ-SUBNET-4] join is elevation-gated with the same fail-safe
    // posture as the seed-reveal paths: only CONFIRMED elevation proceeds;
    // NotElevated and the undeterminable Unknown both refuse (before any
    // prompt or network work — the command checks this gate first).
    #[test]
    fn join_gate_refuses_without_confirmed_elevation() {
        assert!(join_elevation_refusal(Elevation::Elevated).is_none());
        for level in [Elevation::NotElevated, Elevation::Unknown] {
            let msg = join_elevation_refusal(level).expect("refused");
            assert!(msg.contains("ELEVATION_REQUIRED"));
            assert!(msg.contains("administrator"), "actionable fix named: {msg}");
        }
    }