/// Bind the tunnel-control channel at `name` and serve it forever against `hub`.
/// Thread-per-connection (mirroring the digest / drive control channels).
pub fn serve_tunnel_control(name: &str, hub: Arc<TunnelHub>) -> io::Result<()> {
    let listener = LocalSocketTransport::bind(name)?;
    loop {
        let conn = listener.accept()?;
        let hub = Arc::clone(&hub);
        thread::spawn(move || {
            let _ = handle_conn(conn, &hub);
        });
    }
}

/// Drive one control connection: handshake, then service open / resolve / clear
/// requests until the client disconnects.
fn handle_conn<C: Read + Write>(mut conn: C, hub: &TunnelHub) -> io::Result<()> {
    recv_hello(&mut conn, Role::Brain)?;
    loop {
        let env = match read_frame(&mut conn) {
            Ok(e) => e,
            Err(_) => return Ok(()), // peer hung up
        };
        match env.kind.as_str() {
            KIND_TUNNEL_OPEN => {
                let req: TunnelOpenReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                hub.open(
                    &req.owner,
                    &req.shell_id,
                    &req.token,
                    req.owner_stream,
                    req.shell_stream,
                );
                // No reply — fire-and-forget (the open already succeeded on the wire).
            }
            KIND_TUNNEL_RESOLVE => {
                let req: TunnelResolveReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                let stream_id = hub.resolve(&req.owner, &req.shell_id, req.end, &req.token);
                let res = TunnelResult { stream_id };
                write_frame(
                    &mut conn,
                    &Envelope::new(KIND_TUNNEL_RESULT, serde_json::to_value(res).unwrap()),
                )?;
            }
            KIND_TUNNEL_CLEAR => {
                let req: TunnelClearReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                hub.clear(&req.owner, &req.shell_id);
                // No reply.
            }
            // Forward-compat: an unknown control kind is tolerated, not fatal.
            _ => {}
        }
    }
}

/// Bind the tunnel-control channel at `name` and serve it forever against `hub`,
/// using `broker` to reach the broker-owned [`NetHost`](crate::nethost::NetHost)
/// for the opaque byte relay (doyle gate A: the tunnel relay is broker-homed — no
/// dispatch change, no brain serve loop). Thread-per-connection (mirroring the
/// digest / drive control channels).
// [impl->REQ-SHELL-4]
pub fn serve_tunnel_control(name: &str, hub: Arc<TunnelHub>, broker: Arc<Broker>) -> io::Result<()> {
    let listener = LocalSocketTransport::bind(name)?;
    loop {
        let conn = listener.accept()?;
        let hub = Arc::clone(&hub);
        let broker = Arc::clone(&broker);
        thread::spawn(move || {
            let _ = handle_conn(conn, &hub, &broker);
        });
    }
}

/// Drive one control connection: handshake, then service ensure / resolve / send /
/// recv / clear requests until the client disconnects. The byte ops (ensure / send
/// / recv) operate the broker-owned NetHost directly; registry ops (resolve /
/// clear) touch only the hub.
fn handle_conn<C: Read + Write>(mut conn: C, hub: &TunnelHub, broker: &Broker) -> io::Result<()> {
    recv_hello(&mut conn, Role::Brain)?;
    loop {
        let env = match read_frame(&mut conn) {
            Ok(e) => e,
            Err(_) => return Ok(()), // peer hung up
        };
        match env.kind.as_str() {
            KIND_TUNNEL_ENSURE => {
                let req: TunnelEnsureReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                let res = match broker.net() {
                    None => TunnelEnsured {
                        streams: None,
                        error: Some("net disabled".into()),
                    },
                    Some(host) => match host.open_loopback_pair() {
                        Ok((owner_stream, shell_stream)) => {
                            host.mark_stream_retentive(owner_stream, TUNNEL_RING_CHUNKS);
                            host.mark_stream_retentive(shell_stream, TUNNEL_RING_CHUNKS);
                            hub.open(
                                &req.owner,
                                &req.shell_id,
                                &req.token,
                                owner_stream,
                                shell_stream,
                            );
                            TunnelEnsured {
                                streams: Some((owner_stream, shell_stream)),
                                error: None,
                            }
                        }
                        Err(e) => TunnelEnsured {
                            streams: None,
                            error: Some(e.to_string()),
                        },
                    },
                };
                write_frame(
                    &mut conn,
                    &Envelope::new(KIND_TUNNEL_ENSURED, serde_json::to_value(res).unwrap()),
                )?;
            }
            KIND_TUNNEL_RESOLVE => {
                let req: TunnelResolveReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                let stream_id = hub.resolve(&req.owner, &req.shell_id, req.end, &req.token);
                let res = TunnelResult { stream_id };
                write_frame(
                    &mut conn,
                    &Envelope::new(KIND_TUNNEL_RESULT, serde_json::to_value(res).unwrap()),
                )?;
            }
            KIND_TUNNEL_SEND => {
                let req: TunnelSendReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                let bytes = decode_bytes(&req.data_b64)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                if let Some(host) = broker.net() {
                    let _ = host.send_stream(req.stream_id, &bytes, req.finish);
                }
                // No reply — fire-and-forget (the owner gated liveness client-side).
            }
            KIND_TUNNEL_RECV => {
                let req: TunnelRecvReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                let (bytes, finished) = broker
                    .net()
                    .and_then(|h| h.drain_stream(req.stream_id))
                    .unwrap_or_default();
                let res = TunnelRecvResult {
                    data_b64: encode_bytes(&bytes),
                    finished,
                };
                write_frame(
                    &mut conn,
                    &Envelope::new(KIND_TUNNEL_RECV_RESULT, serde_json::to_value(res).unwrap()),
                )?;
            }
            KIND_TUNNEL_CLEAR => {
                let req: TunnelClearReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                hub.clear(&req.owner, &req.shell_id);
                // No reply.
            }
            // Forward-compat: an unknown control kind is tolerated, not fatal.
            _ => {}
        }
    }
}

-/// Bind the tunnel-control channel at `name` and serve it forever against `hub`.

-/// Thread-per-connection (mirroring the digest / drive control channels).

-pub fn serve_tunnel_control(name: &str, hub: Arc<TunnelHub>) -> io::Result<()> {

+/// Bind the tunnel-control channel at `name` and serve it forever against `hub`,

+/// using `broker` to reach the broker-owned [`NetHost`](crate::nethost::NetHost)

+/// for the opaque byte relay (doyle gate A: the tunnel relay is broker-homed — no

+/// dispatch change, no brain serve loop). Thread-per-connection (mirroring the

+pub fn serve_tunnel_control(name: &str, hub: Arc<TunnelHub>, broker: Arc<Broker>) -> io::Result<()> {

+        let broker = Arc::clone(&broker);

-            let _ = handle_conn(conn, &hub);

+            let _ = handle_conn(conn, &hub, &broker);

-/// Drive one control connection: handshake, then service open / resolve / clear

-/// requests until the client disconnects.

-fn handle_conn<C: Read + Write>(mut conn: C, hub: &TunnelHub) -> io::Result<()> {

+/// Drive one control connection: handshake, then service ensure / resolve / send /

+/// recv / clear requests until the client disconnects. The byte ops (ensure / send

+/// / recv) operate the broker-owned NetHost directly; registry ops (resolve /

+fn handle_conn<C: Read + Write>(mut conn: C, hub: &TunnelHub, broker: &Broker) -> io::Result<()> {

-                let req: TunnelOpenReq = serde_json::from_value(env.payload)

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

-                // No reply — fire-and-forget (the open already succeeded on the wire).

+                let res = match broker.net() {

+                    None => TunnelEnsured {

+                        error: Some("net disabled".into()),

+                    Some(host) => match host.open_loopback_pair() {

+                        Ok((owner_stream, shell_stream)) => {

+                            host.mark_stream_retentive(owner_stream, TUNNEL_RING_CHUNKS);

+                            host.mark_stream_retentive(shell_stream, TUNNEL_RING_CHUNKS);

+                                &req.owner,

+                                &req.shell_id,

+                                &req.token,

+                                owner_stream,

+                                shell_stream,

+                            TunnelEnsured {

+                                streams: Some((owner_stream, shell_stream)),

+                                error: None,

+                        Err(e) => TunnelEnsured {

+                            streams: None,

+                            error: Some(e.to_string()),

+                    &Envelope::new(KIND_TUNNEL_ENSURED, serde_json::to_value(res).unwrap()),

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

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

+                let bytes = decode_bytes(&req.data_b64)

+                if let Some(host) = broker.net() {

+                    let _ = host.send_stream(req.stream_id, &bytes, req.finish);

+                // No reply — fire-and-forget (the owner gated liveness client-side).

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

+                let (bytes, finished) = broker

+                    .and_then(|h| h.drain_stream(req.stream_id))

+                    .unwrap_or_default();

+                let res = TunnelRecvResult {

+                    data_b64: encode_bytes(&bytes),

+                    &Envelope::new(KIND_TUNNEL_RECV_RESULT, serde_json::to_value(res).unwrap()),


Replace the now-dead `tunnel_open` client fn with `tunnel_ensure`/`tunnel_send`/`tunnel_recv`. View the client fn block:


355	                let res = TunnelResult { stream_id };
356	                write_frame(
357	                    &mut conn,
358	                    &Envelope::new(KIND_TUNNEL_RESULT, serde_json::to_value(res).unwrap()),
359	                )?;
360	            }
361	            KIND_TUNNEL_SEND => {
362	                let req: TunnelSendReq = serde_json::from_value(env.payload)
363	                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
364	                let bytes = decode_bytes(&req.data_b64)
365	                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
366	                if let Some(host) = broker.net() {
367	                    let _ = host.send_stream(req.stream_id, &bytes, req.finish);
368	                }
369	                // No reply — fire-and-forget (the owner gated liveness client-side).
370	            }
371	            KIND_TUNNEL_RECV => {
372	                let req: TunnelRecvReq = serde_json::from_value(env.payload)
373	                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
374	                let (bytes, finished) = broker
375	                    .net()
376	                    .and_then(|h| h.drain_stream(req.stream_id))
377	                    .unwrap_or_default();
378	                let res = TunnelRecvResult {
379	                    data_b64: encode_bytes(&bytes),
380	                    finished,
381	                };
382	                write_frame(
383	                    &mut conn,
384	                    &Envelope::new(KIND_TUNNEL_RECV_RESULT, serde_json::to_value(res).unwrap()),
385	                )?;
386	            }
387	            KIND_TUNNEL_CLEAR => {
388	                let req: TunnelClearReq = serde_json::from_value(env.payload)
389	                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
390	                hub.clear(&req.owner, &req.shell_id);
391	                // No reply.
392	            }
393	            // Forward-compat: an unknown control kind is tolerated, not fatal.
394	            _ => {}
395	        }
396	    }
397	}
398
399	/// Connect to the tunnel-control channel `name`, announcing ourselves as a brain
400	/// (the `spt` CLI is a transient logic client of the daemon).
401	fn connect(name: &str) -> io::Result<interprocess::local_socket::Stream> {
402	    let mut conn = LocalSocketTransport::connect(name)?;
403	    send_hello(&mut conn, Role::Brain)?;
404	    Ok(conn)
405	}
406
407	/// Record `shell_id`'s freshly-opened tunnel stream pair on the daemon over `name`,
408	/// stamping it with `token`. Fire-and-forget.
409	// [impl->REQ-SHELL-4]
410	pub fn tunnel_open(
411	    name: &str,
412	    owner: &str,
413	    shell_id: &str,
414	    token: &str,

                let res = TunnelResult { stream_id };
                write_frame(
                    &mut conn,
                    &Envelope::new(KIND_TUNNEL_RESULT, serde_json::to_value(res).unwrap()),
                )?;
            }
            KIND_TUNNEL_SEND => {
                let req: TunnelSendReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                let bytes = decode_bytes(&req.data_b64)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                if let Some(host) = broker.net() {
                    let _ = host.send_stream(req.stream_id, &bytes, req.finish);
                }
                // No reply — fire-and-forget (the owner gated liveness client-side).
            }
            KIND_TUNNEL_RECV => {
                let req: TunnelRecvReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                let (bytes, finished) = broker
                    .net()
                    .and_then(|h| h.drain_stream(req.stream_id))
                    .unwrap_or_default();
                let res = TunnelRecvResult {
                    data_b64: encode_bytes(&bytes),
                    finished,
                };
                write_frame(
                    &mut conn,
                    &Envelope::new(KIND_TUNNEL_RECV_RESULT, serde_json::to_value(res).unwrap()),
                )?;
            }
            KIND_TUNNEL_CLEAR => {
                let req: TunnelClearReq = serde_json::from_value(env.payload)
                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                hub.clear(&req.owner, &req.shell_id);
                // No reply.
            }
            // Forward-compat: an unknown control kind is tolerated, not fatal.
            _ => {}
        }
    }
}

/// Connect to the tunnel-control channel `name`, announcing ourselves as a brain
/// (the `spt` CLI is a transient logic client of the daemon).
fn connect(name: &str) -> io::Result<interprocess::local_socket::Stream> {
    let mut conn = LocalSocketTransport::connect(name)?;
    send_hello(&mut conn, Role::Brain)?;
    Ok(conn)
}

/// Record `shell_id`'s freshly-opened tunnel stream pair on the daemon over `name`,
/// stamping it with `token`. Fire-and-forget.
// [impl->REQ-SHELL-4]
pub fn tunnel_open(
    name: &str,
    owner: &str,
    shell_id: &str,
    token: &str,


407	/// Record `shell_id`'s freshly-opened tunnel stream pair on the daemon over `name`,
408	/// stamping it with `token`. Fire-and-forget.
409	// [impl->REQ-SHELL-4]
410	pub fn tunnel_open(
411	    name: &str,
412	    owner: &str,
413	    shell_id: &str,
414	    token: &str,
415	    owner_stream: u64,
416	    shell_stream: u64,
417	) -> io::Result<()> {
418	    let mut conn = connect(name)?;
419	    let req = TunnelOpenReq {
420	        owner: owner.to_string(),
421	        shell_id: shell_id.to_string(),
422	        token: token.to_string(),
423	        owner_stream,
424	        shell_stream,
425	    };
426	    write_frame(
427	        &mut conn,
428	        &Envelope::new(KIND_TUNNEL_OPEN, serde_json::to_value(req).unwrap()),
429	    )?;
430	    Ok(())
431	}
432
433	/// Resolve one `end` of `shell_id`'s tunnel stream on the daemon over `name`,
434	/// presenting `token`. `Ok(None)` ⇒ no live tunnel OR the entry was a dead-generation
435	/// stamp (never surfaced).
436	// [impl->REQ-SHELL-4]
437	pub fn tunnel_resolve(
438	    name: &str,
439	    owner: &str,
440	    shell_id: &str,
441	    end: TunnelEnd,
442	    token: &str,
443	) -> io::Result<Option<u64>> {
444	    let mut conn = connect(name)?;
445	    let req = TunnelResolveReq {
446	        owner: owner.to_string(),
447	        shell_id: shell_id.to_string(),
448	        end,
449	        token: token.to_string(),
450	    };
451	    write_frame(
452	        &mut conn,
453	        &Envelope::new(KIND_TUNNEL_RESOLVE, serde_json::to_value(req).unwrap()),
454	    )?;
455	    let env = read_frame(&mut conn)?;
456	    let res: TunnelResult = serde_json::from_value(env.payload)
457	        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
458	    Ok(res.stream_id)
459	}
460
461	/// Clear `shell_id`'s tunnel entry on the daemon over `name`. Fire-and-forget +
462	/// best-effort: a daemon that isn't running holds no entry, so a failed connect is
463	/// harmless (the entry is already gone with the process).
464	// [impl->REQ-SHELL-4]
465	pub fn tunnel_clear(name: &str, owner: &str, shell_id: &str) -> io::Result<()> {
466	    let mut conn = connect(name)?;
467	    let req = TunnelClearReq {
468	        owner: owner.to_string(),
469	        shell_id: shell_id.to_string(),
470	    };
471	    write_frame(
472	        &mut conn,
473	        &Envelope::new(KIND_TUNNEL_CLEAR, serde_json::to_value(req).unwrap()),
474	    )?;
475	    Ok(())
476	}
477
478	#[cfg(test)]
479	mod tests {
480	    use super::*;
481
482	    // [unit->REQ-SHELL-4] a resolve serves the held stream id for the requested
483	    // end only under the opening token; the two ends are distinct ids.
484	    #[test]
485	    fn resolve_serves_each_end_under_the_opening_token() {
486	        let hub = TunnelHub::new();
487	        hub.open("doyle", "mock-shell-0", "tok", 7, 8);
488	        assert_eq!(hub.owner_stream("doyle", "mock-shell-0", "tok"), Some(7));
489	        assert_eq!(hub.shell_stream("doyle", "mock-shell-0", "tok"), Some(8));
490	    }
491
492	    // [unit->REQ-SHELL-4] no-stale-stream-across-relink (R1): a stream id opened
493	    // under one link token is NEVER surfaced to a resolve presenting a different
494	    // (relinked) token — even though the entry is NOT evicted by the mismatch (the
495	    // tunnel is long-lived, unlike the drive slot's take-and-clear). This is the
496	    // crash-proof guarantee that does not depend on the explicit clear-on-close.
497	    #[test]
498	    fn dead_generation_stream_is_never_surfaced() {
499	        let hub = TunnelHub::new();
500	        hub.open("doyle", "mock-shell-0", "old-token", 7, 8);
501	        // The shell crashed/relinked; the surfaces now present the fresh token.

/// Record `shell_id`'s freshly-opened tunnel stream pair on the daemon over `name`,
/// stamping it with `token`. Fire-and-forget.
// [impl->REQ-SHELL-4]
pub fn tunnel_open(
    name: &str,
    owner: &str,
    shell_id: &str,
    token: &str,
    owner_stream: u64,
    shell_stream: u64,
) -> io::Result<()> {
    let mut conn = connect(name)?;
    let req = TunnelOpenReq {
        owner: owner.to_string(),
        shell_id: shell_id.to_string(),
        token: token.to_string(),
        owner_stream,
        shell_stream,
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_OPEN, serde_json::to_value(req).unwrap()),
    )?;
    Ok(())
}

/// Resolve one `end` of `shell_id`'s tunnel stream on the daemon over `name`,
/// presenting `token`. `Ok(None)` ⇒ no live tunnel OR the entry was a dead-generation
/// stamp (never surfaced).
// [impl->REQ-SHELL-4]
pub fn tunnel_resolve(
    name: &str,
    owner: &str,
    shell_id: &str,
    end: TunnelEnd,
    token: &str,
) -> io::Result<Option<u64>> {
    let mut conn = connect(name)?;
    let req = TunnelResolveReq {
        owner: owner.to_string(),
        shell_id: shell_id.to_string(),
        end,
        token: token.to_string(),
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_RESOLVE, serde_json::to_value(req).unwrap()),
    )?;
    let env = read_frame(&mut conn)?;
    let res: TunnelResult = serde_json::from_value(env.payload)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    Ok(res.stream_id)
}

/// Clear `shell_id`'s tunnel entry on the daemon over `name`. Fire-and-forget +
/// best-effort: a daemon that isn't running holds no entry, so a failed connect is
/// harmless (the entry is already gone with the process).
// [impl->REQ-SHELL-4]
pub fn tunnel_clear(name: &str, owner: &str, shell_id: &str) -> io::Result<()> {
    let mut conn = connect(name)?;
    let req = TunnelClearReq {
        owner: owner.to_string(),
        shell_id: shell_id.to_string(),
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_CLEAR, serde_json::to_value(req).unwrap()),
    )?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    // [unit->REQ-SHELL-4] a resolve serves the held stream id for the requested
    // end only under the opening token; the two ends are distinct ids.
    #[test]
    fn resolve_serves_each_end_under_the_opening_token() {
        let hub = TunnelHub::new();
        hub.open("doyle", "mock-shell-0", "tok", 7, 8);
        assert_eq!(hub.owner_stream("doyle", "mock-shell-0", "tok"), Some(7));
        assert_eq!(hub.shell_stream("doyle", "mock-shell-0", "tok"), Some(8));
    }

    // [unit->REQ-SHELL-4] no-stale-stream-across-relink (R1): a stream id opened
    // under one link token is NEVER surfaced to a resolve presenting a different
    // (relinked) token — even though the entry is NOT evicted by the mismatch (the
    // tunnel is long-lived, unlike the drive slot's take-and-clear). This is the
    // crash-proof guarantee that does not depend on the explicit clear-on-close.
    #[test]
    fn dead_generation_stream_is_never_surfaced() {
        let hub = TunnelHub::new();
        hub.open("doyle", "mock-shell-0", "old-token", 7, 8);
        // The shell crashed/relinked; the surfaces now present the fresh token.


Replace the `tunnel_open` client fn with `tunnel_ensure` + `tunnel_send` + `tunnel_recv`, and remove the now-dead `KIND_TUNNEL_OPEN`/`TunnelOpenReq`:


Edit

/// Record `shell_id`'s freshly-opened tunnel stream pair on the daemon over `name`,
/// stamping it with `token`. Fire-and-forget.
// [impl->REQ-SHELL-4]
pub fn tunnel_open(
    name: &str,
    owner: &str,
    shell_id: &str,
    token: &str,
    owner_stream: u64,
    shell_stream: u64,
) -> io::Result<()> {
    let mut conn = connect(name)?;
    let req = TunnelOpenReq {
        owner: owner.to_string(),
        shell_id: shell_id.to_string(),
        token: token.to_string(),
        owner_stream,
        shell_stream,
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_OPEN, serde_json::to_value(req).unwrap()),
    )?;
    Ok(())
}

/// Ensure `shell_id`'s tunnel is open on the daemon over `name`, stamped with
/// `token` (fired at shell-online when the manifest enables the tunnel). The
/// broker opens the same-node loopback pair + registers it. Returns the opened
/// `(owner_stream, shell_stream)` ids, or an error string from the broker.
// [impl->REQ-SHELL-4]
pub fn tunnel_ensure(
    name: &str,
    owner: &str,
    shell_id: &str,
    token: &str,
) -> io::Result<Result<(u64, u64), String>> {
    let mut conn = connect(name)?;
    let req = TunnelEnsureReq {
        owner: owner.to_string(),
        shell_id: shell_id.to_string(),
        token: token.to_string(),
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_ENSURE, serde_json::to_value(req).unwrap()),
    )?;
    let env = read_frame(&mut conn)?;
    let res: TunnelEnsured = serde_json::from_value(env.payload)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    Ok(match (res.streams, res.error) {
        (Some(s), _) => Ok(s),
        (None, Some(e)) => Err(e),
        (None, None) => Err("tunnel ensure: empty reply".into()),
    })
}

/// Write opaque `bytes` to a held tunnel `stream_id` on the daemon over `name`;
/// `finish` closes the send side (the peer sees EOF). Fire-and-forget.
// [impl->REQ-SHELL-4]
pub fn tunnel_send(name: &str, stream_id: u64, bytes: &[u8], finish: bool) -> io::Result<()> {
    let mut conn = connect(name)?;
    let req = TunnelSendReq {
        stream_id,
        data_b64: encode_bytes(bytes),
        finish,
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_SEND, serde_json::to_value(req).unwrap()),
    )?;
    Ok(())
}

/// Drain opaque bytes from a held tunnel `stream_id` on the daemon over `name`
/// (the recv cursor). Returns `(bytes, finished)`; empty bytes + `false` ⇒ nothing
/// pending yet.
// [impl->REQ-SHELL-4]
pub fn tunnel_recv(name: &str, stream_id: u64) -> io::Result<(Vec<u8>, bool)> {
    let mut conn = connect(name)?;
    let req = TunnelRecvReq { stream_id };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_RECV, serde_json::to_value(req).unwrap()),
    )?;
    let env = read_frame(&mut conn)?;
    let res: TunnelRecvResult = serde_json::from_value(env.payload)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    let bytes = decode_bytes(&res.data_b64)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    Ok((bytes, res.finished))
}


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

/// Record `shell_id`'s freshly-opened tunnel stream pair on the daemon over `name`,
/// stamping it with `token`. Fire-and-forget.
// [impl->REQ-SHELL-4]
pub fn tunnel_open(
    name: &str,
    owner: &str,
    shell_id: &str,
    token: &str,
    owner_stream: u64,
    shell_stream: u64,
) -> io::Result<()> {
    let mut conn = connect(name)?;
    let req = TunnelOpenReq {
        owner: owner.to_string(),
        shell_id: shell_id.to_string(),
        token: token.to_string(),
        owner_stream,
        shell_stream,
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_OPEN, serde_json::to_value(req).unwrap()),
    )?;
    Ok(())
}

/// Ensure `shell_id`'s tunnel is open on the daemon over `name`, stamped with
/// `token` (fired at shell-online when the manifest enables the tunnel). The
/// broker opens the same-node loopback pair + registers it. Returns the opened
/// `(owner_stream, shell_stream)` ids, or an error string from the broker.
// [impl->REQ-SHELL-4]
pub fn tunnel_ensure(
    name: &str,
    owner: &str,
    shell_id: &str,
    token: &str,
) -> io::Result<Result<(u64, u64), String>> {
    let mut conn = connect(name)?;
    let req = TunnelEnsureReq {
        owner: owner.to_string(),
        shell_id: shell_id.to_string(),
        token: token.to_string(),
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_ENSURE, serde_json::to_value(req).unwrap()),
    )?;
    let env = read_frame(&mut conn)?;
    let res: TunnelEnsured = serde_json::from_value(env.payload)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    Ok(match (res.streams, res.error) {
        (Some(s), _) => Ok(s),
        (None, Some(e)) => Err(e),
        (None, None) => Err("tunnel ensure: empty reply".into()),
    })
}

/// Write opaque `bytes` to a held tunnel `stream_id` on the daemon over `name`;
/// `finish` closes the send side (the peer sees EOF). Fire-and-forget.
// [impl->REQ-SHELL-4]
pub fn tunnel_send(name: &str, stream_id: u64, bytes: &[u8], finish: bool) -> io::Result<()> {
    let mut conn = connect(name)?;
    let req = TunnelSendReq {
        stream_id,
        data_b64: encode_bytes(bytes),
        finish,
    };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_SEND, serde_json::to_value(req).unwrap()),
    )?;
    Ok(())
}

/// Drain opaque bytes from a held tunnel `stream_id` on the daemon over `name`
/// (the recv cursor). Returns `(bytes, finished)`; empty bytes + `false` ⇒ nothing
/// pending yet.
// [impl->REQ-SHELL-4]
pub fn tunnel_recv(name: &str, stream_id: u64) -> io::Result<(Vec<u8>, bool)> {
    let mut conn = connect(name)?;
    let req = TunnelRecvReq { stream_id };
    write_frame(
        &mut conn,
        &Envelope::new(KIND_TUNNEL_RECV, serde_json::to_value(req).unwrap()),
    )?;
    let env = read_frame(&mut conn)?;
    let res: TunnelRecvResult = serde_json::from_value(env.payload)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    let bytes = decode_bytes(&res.data_b64)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    Ok((bytes, res.finished))
}

-/// Record `shell_id`'s freshly-opened tunnel stream pair on the daemon over `name`,

-/// stamping it with `token`. Fire-and-forget.

+/// Ensure `shell_id`'s tunnel is open on the daemon over `name`, stamped with

+/// `token` (fired at shell-online when the manifest enables the tunnel). The

+/// broker opens the same-node loopback pair + registers it. Returns the opened

+/// `(owner_stream, shell_stream)` ids, or an error string from the broker.

+) -> io::Result<Result<(u64, u64), String>> {

-        &Envelope::new(KIND_TUNNEL_OPEN, serde_json::to_value(req).unwrap()),

+        &Envelope::new(KIND_TUNNEL_ENSURE, serde_json::to_value(req).unwrap()),

+    let res: TunnelEnsured = serde_json::from_value(env.payload)

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

+    Ok(match (res.streams, res.error) {

+        (None, None) => Err("tunnel ensure: empty reply".into()),

+/// Write opaque `bytes` to a held tunnel `stream_id` on the daemon over `name`;

+/// `finish` closes the send side (the peer sees EOF). Fire-and-forget.

+pub fn tunnel_send(name: &str, stream_id: u64, bytes: &[u8], finish: bool) -> io::Result<()> {

+        &Envelope::new(KIND_TUNNEL_SEND, serde_json::to_value(req).unwrap()),

+/// Drain opaque bytes from a held tunnel `stream_id` on the daemon over `name`

+/// (the recv cursor). Returns `(bytes, finished)`; empty bytes + `false` ⇒ nothing

+pub fn tunnel_recv(name: &str, stream_id: u64) -> io::Result<(Vec<u8>, bool)> {

+    let req = TunnelRecvReq { stream_id };

+        &Envelope::new(KIND_TUNNEL_RECV, serde_json::to_value(req).unwrap()),

+    let res: TunnelRecvResult = serde_json::from_value(env.payload)

+    let bytes = decode_bytes(&res.data_b64)

/// Resolve one `end` of `shell_id`'s tunnel stream on the daemon over `name`,

/// presenting `token`. `Ok(None)` ⇒ no live tunnel OR the entry was a dead-generation


Remove the dead `KIND_TUNNEL_OPEN` + `TunnelOpenReq`:


/// Daemon→registry: record a shell's freshly-opened tunnel stream pair, stamped
/// with the opening link token. Fired at shell-online (manifest tunnel-enabled).
pub const KIND_TUNNEL_OPEN: &str = "tunnel_open";
/// Surface→registry: resolve the held stream id for one end of a shell's tunnel,

/// Surface→registry: resolve the held stream id for one end of a shell's tunnel,


/// Daemon→registry: record a shell's freshly-opened tunnel stream pair, stamped
/// with the opening link token. Fired at shell-online (manifest tunnel-enabled).
pub const KIND_TUNNEL_OPEN: &str = "tunnel_open";
/// Surface→registry: resolve the held stream id for one end of a shell's tunnel,

/// loss, no unbounded growth — see [`crate::nethost::NetHost::mark_stream_retentive`]).

pub const TUNNEL_RING_CHUNKS: usize = 256;

-/// Daemon→registry: record a shell's freshly-opened tunnel stream pair, stamped

-/// with the opening link token. Fired at shell-online (manifest tunnel-enabled).

-pub const KIND_TUNNEL_OPEN: &str = "tunnel_open";

/// gated on the presented link token (the no-stale-stream serve gate). Replies


/// `tunnel_open` payload: the slot key, the opening link token (the no-stale stamp),
/// and the two cross-wired stream ids.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TunnelOpenReq {
    /// Owning endpoint id (shell ids are per-owner, so the key needs both).
    pub owner: String,
    /// Canonical shell instance id.
    pub shell_id: String,
    /// The link token current at open time — the no-stale-stream generation stamp.
    pub token: String,
    /// The owner-side (operator-row) held stream id.
    pub owner_stream: u64,
    /// The shell-side (peer-row) held stream id.
    pub shell_stream: u64,
}

/// `tunnel_resolve` payload: the slot key, which end, and the presenting token.

/// `tunnel_resolve` payload: the slot key, which end, and the presenting token.


/// `tunnel_open` payload: the slot key, the opening link token (the no-stale stamp),
/// and the two cross-wired stream ids.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TunnelOpenReq {
    /// Owning endpoint id (shell ids are per-owner, so the key needs both).
    pub owner: String,
    /// Canonical shell instance id.
    pub shell_id: String,
    /// The link token current at open time — the no-stale-stream generation stamp.
    pub token: String,
    /// The owner-side (operator-row) held stream id.
    pub owner_stream: u64,
    /// The shell-side (peer-row) held stream id.
    pub shell_stream: u64,
}

/// `tunnel_resolve` payload: the slot key, which end, and the presenting token.

-/// `tunnel_open` payload: the slot key, the opening link token (the no-stale stamp),

-/// and the two cross-wired stream ids.

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

-    /// Owning endpoint id (shell ids are per-owner, so the key needs both).

-    /// The link token current at open time — the no-stale-stream generation stamp.

-    /// The owner-side (operator-row) held stream id.

-    /// The shell-side (peer-row) held stream id.


Update the lib.rs re-export + the daemon.rs spawn (gate on `Some(broker)`, pass it):


pub use tunnelhub::{
    serve_tunnel_control, tunnel_clear, tunnel_open, tunnel_resolve, TunnelEnd, TunnelHub,
};

pub use tunnelhub::{
    serve_tunnel_control, tunnel_clear, tunnel_ensure, tunnel_recv, tunnel_resolve, tunnel_send,
    TunnelEnd, TunnelHub,
};


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

pub use tunnelhub::{
    serve_tunnel_control, tunnel_clear, tunnel_open, tunnel_resolve, TunnelEnd, TunnelHub,
};

pub use tunnelhub::{
    serve_tunnel_control, tunnel_clear, tunnel_ensure, tunnel_recv, tunnel_resolve, tunnel_send,
    TunnelEnd, TunnelHub,
};

drive_clear, drive_take, drive_write, serve_drive_control, DriveHub,

-    serve_tunnel_control, tunnel_clear, tunnel_open, tunnel_resolve, TunnelEnd, TunnelHub,

+    serve_tunnel_control, tunnel_clear, tunnel_ensure, tunnel_recv, tunnel_resolve, tunnel_send,

broker_socket_name, daemon_pid_path, digest_socket_name, drive_socket_name,


        let tunnel_hub = Arc::new(crate::tunnelhub::TunnelHub::new());
        {
            let hub = Arc::clone(&tunnel_hub);
            thread::spawn(move || {
                let _ = crate::tunnelhub::serve_tunnel_control(
                    &crate::endpoint::tunnel_socket_name(),
                    hub,
                );
            });
        }

        let tunnel_hub = Arc::new(crate::tunnelhub::TunnelHub::new());
        // The relay needs the broker-owned NetHost (doyle gate A: broker-homed),
        // so it serves only when the broker bound; a net-less daemon has no tunnel.
        if let Some(broker) = &broker_handle {
            let hub = Arc::clone(&tunnel_hub);
            let broker = Arc::clone(broker);
            thread::spawn(move || {
                let _ = crate::tunnelhub::serve_tunnel_control(
                    &crate::endpoint::tunnel_socket_name(),
                    hub,
                    broker,
                );
            });
        }


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

        let tunnel_hub = Arc::new(crate::tunnelhub::TunnelHub::new());
        {
            let hub = Arc::clone(&tunnel_hub);
            thread::spawn(move || {
                let _ = crate::tunnelhub::serve_tunnel_control(
                    &crate::endpoint::tunnel_socket_name(),
                    hub,
                );
            });
        }

        let tunnel_hub = Arc::new(crate::tunnelhub::TunnelHub::new());
        // The relay needs the broker-owned NetHost (doyle gate A: broker-homed),
        // so it serves only when the broker bound; a net-less daemon has no tunnel.
        if let Some(broker) = &broker_handle {
            let hub = Arc::clone(&tunnel_hub);
            let broker = Arc::clone(broker);
            thread::spawn(move || {
                let _ = crate::tunnelhub::serve_tunnel_control(
                    &crate::endpoint::tunnel_socket_name(),
                    hub,
                    broker,
                );
            });
        }

// the shell binary's `api tunnel` resolve their stream end through it.

+        // The relay needs the broker-owned NetHost (doyle gate A: broker-homed),

+        // so it serves only when the broker bound; a net-less daemon has no tunnel.

+        if let Some(broker) = &broker_handle {

+            let broker = Arc::clone(broker);


Bash

cargo build -q -p spt-daemon 2>&1 | tail -25; echo "BUILD=$?"

Build spt-daemon with tunnel-control byte ops


Builds. Verify hub + nethost tests still green:


cargo test -q -p spt-daemon --lib 'tunnelhub::tests::' 'nethost::tests::' 2>&1 | grep 'test result'

Run tunnelhub + nethost unit tests


(Bash completed with no output)


cargo test -q -p spt-daemon --lib tunnelhub:: 2>&1 | grep 'test result'; cargo test -q -p spt-daemon --lib nethost:: 2>&1 | grep 'test result'

Run tunnelhub + nethost unit tests separately


test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 304 filtered out; finished in 0.00s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 304 filtered out; finished in 1.12s


Green. Commit the broker byte-bridge (T3.2d), then the open-hook + surfaces:


cargo clippy --workspace --all-targets -- -D warnings 2>&1 | tail -4; traceable-reqs check >/dev/null 2>&1; echo "TRACEABLE=$?"

Clippy + traceable pre-flight


    Checking spt-daemon v0.7.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon)
    Checking xtask v0.7.1 (C:\Users\decid\Documents\projects\spt-core\crates\xtask)
    Checking spt v0.7.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 5.51s
TRACEABLE=0


git add -A && git commit -q -m "$(cat <<'EOF'
M11-W3 T3.2d: broker-homed tunnel byte bridge (REQ-SHELL-4)

The opaque relay (doyle gate A): the tunnel-control channel gains
Arc<Broker> so it operates the broker-owned NetHost directly — no
dispatch change, no brain serve loop. New control ops:
- tunnel_ensure: opens the same-node loopback stream pair, marks both
  retentive (backpressured, lossless), registers them in the hub under
  the link token.
- tunnel_send / tunnel_recv: write / drain opaque bytes (base64 on the
  control wire) to a held stream via NetHost::send_stream / drain_stream.
Registry ops (resolve / clear) unchanged. The dead tunnel_open record
op is removed (ensure subsumes it). Daemon serves the channel only when
the broker bound (a net-less daemon has no tunnel).

Surfaces (spt shell tunnel / api tunnel) + the open-at-bind hook land next.

Co-authored by: todlando
EOF
)" && git log --oneline -1

Commit T3.2d byte bridge


warning: in the working copy of 'crates/spt-daemon/src/tunnelhub.rs', LF will be replaced by CRLF the next time Git touches it
6f3065a M11-W3 T3.2d: broker-homed tunnel byte bridge (REQ-SHELL-4)


T3.2d committed. Now the open-hook at `api bind-shell` — needs the shell's manifest (to check `tunnel.enable`). Check how the shell manifest resolves by owner+id (`resolve_link_target`):


190	pub fn resolve_link_target(
191	    owlery: &Path,
192	    owner: &str,
193	    shell_ref: &str,
194	) -> Result<(String, ShellInfo, Shell), ShellLinkError> {
195	    let Some((id, info)) = shellinfo::resolve_shell_ref(owlery, owner, shell_ref) else {
196	        return Err(ShellLinkError::NoShell);
197	    };
198	    // Capabilities resolve from the merged view: the stored adapter_name is the
199	    // composite option, so a profiled shell links against its overlaid [shell].
200	    let adapters_dir = spt_store::perch::adapters_dir();
201	    let manifest = spt_runtime::registry::resolve_option(&adapters_dir, &info.adapter_name)
202	        .ok()
203	        .filter(|(_, m)| m.adapter.kind == AdapterKind::Shell)
204	        .map(|(_, m)| m);
205	    let Some(shell) = manifest.and_then(|m| m.shell) else {
206	        return Err(ShellLinkError::AdapterUnregistered(info.adapter_name));
207	    };
208	    Ok((id, info, shell))
209	}
210
211	/// Drive one vocabulary-checked typed command down the durable command
212	/// channel: vocab-check → compose → MAC-stamp under the parked token's link
213	/// key → spool on the shell perch. The shared core of the local
214	/// `spt shell cmd` and the remote serve. Returns `(canonical_id, shell)` —
215	/// the caller drains per the manifest receipt.
216	// [impl->REQ-SHELL-1]
217	pub fn drive_shell(
218	    owlery: &Path,
219	    owner: &str,
220	    shell_ref: &str,
221	    op: &str,
222	    args: &[String],
223	) -> Result<(String, Shell), ShellLinkError> {
224	    let (id, _info, shell) = resolve_link_target(owlery, owner, shell_ref)?;
225	    if shell.command_receipt == Some(CommandReceipt::Http) {
226	        // Spawn already refuses http; this guards a manifest swapped after
227	        // spawn — an unimplemented mode never falls back silently.
228	        return Err(ShellLinkError::ReceiptUnsupported);
229	    }
230	    let perch_path = spt_store::perch::resolve_shell_perch_path_in(owlery, owner, &id);
231	    let Some(token) = shellhost::read_link_token(&perch_path) else {
232	        return Err(ShellLinkError::NoLink);
233	    };
234	    let args_obj = shellchan::vocab_check(&shell, op, args).map_err(ShellLinkError::Vocab)?;
235	    // Per-capability act-gate (M11, REQ-CONSENT-3): a gated op refuses to spool
236	    // until granted — the command never lands in the durable channel on an
237	    // ungranted ask. Enforced here in the shared core so the cross-node serve
238	    // is gated identically to the local CLI (grants are target-node-local).
239	    // [impl->REQ-CONSENT-3]
240	    if let Some((ask, persist)) = act_gate_pending(owlery, &shell, op, owner) {
241	        return Err(ShellLinkError::NeedsApproval(ask, persist));
242	    }
243	    let frame = shellchan::compose_command_frame(owner, op, &args_obj);
244	    let stamped = shellchan::stamp_frame(&shellhost::link_key(&token), &frame);
245	    shellchan::spool_shell_frame(owlery, owner, &id, &stamped).map_err(ShellLinkError::Failed)?;
246	    Ok((id, shell))
247	}
248
249	/// The outcome of a drive-channel write (M11-W2, REQ-SHELL-3). Distinct from a

pub fn resolve_link_target(
    owlery: &Path,
    owner: &str,
    shell_ref: &str,
) -> Result<(String, ShellInfo, Shell), ShellLinkError> {
    let Some((id, info)) = shellinfo::resolve_shell_ref(owlery, owner, shell_ref) else {
        return Err(ShellLinkError::NoShell);
    };
    // Capabilities resolve from the merged view: the stored adapter_name is the
    // composite option, so a profiled shell links against its overlaid [shell].
    let adapters_dir = spt_store::perch::adapters_dir();
    let manifest = spt_runtime::registry::resolve_option(&adapters_dir, &info.adapter_name)
        .ok()
        .filter(|(_, m)| m.adapter.kind == AdapterKind::Shell)
        .map(|(_, m)| m);
    let Some(shell) = manifest.and_then(|m| m.shell) else {
        return Err(ShellLinkError::AdapterUnregistered(info.adapter_name));
    };
    Ok((id, info, shell))
}

/// Drive one vocabulary-checked typed command down the durable command
/// channel: vocab-check → compose → MAC-stamp under the parked token's link
/// key → spool on the shell perch. The shared core of the local
/// `spt shell cmd` and the remote serve. Returns `(canonical_id, shell)` —
/// the caller drains per the manifest receipt.
// [impl->REQ-SHELL-1]
pub fn drive_shell(
    owlery: &Path,
    owner: &str,
    shell_ref: &str,
    op: &str,
    args: &[String],
) -> Result<(String, Shell), ShellLinkError> {
    let (id, _info, shell) = resolve_link_target(owlery, owner, shell_ref)?;
    if shell.command_receipt == Some(CommandReceipt::Http) {
        // Spawn already refuses http; this guards a manifest swapped after
        // spawn — an unimplemented mode never falls back silently.
        return Err(ShellLinkError::ReceiptUnsupported);
    }
    let perch_path = spt_store::perch::resolve_shell_perch_path_in(owlery, owner, &id);
    let Some(token) = shellhost::read_link_token(&perch_path) else {
        return Err(ShellLinkError::NoLink);
    };
    let args_obj = shellchan::vocab_check(&shell, op, args).map_err(ShellLinkError::Vocab)?;
    // Per-capability act-gate (M11, REQ-CONSENT-3): a gated op refuses to spool
    // until granted — the command never lands in the durable channel on an
    // ungranted ask. Enforced here in the shared core so the cross-node serve
    // is gated identically to the local CLI (grants are target-node-local).
    // [impl->REQ-CONSENT-3]
    if let Some((ask, persist)) = act_gate_pending(owlery, &shell, op, owner) {
        return Err(ShellLinkError::NeedsApproval(ask, persist));
    }
    let frame = shellchan::compose_command_frame(owner, op, &args_obj);
    let stamped = shellchan::stamp_frame(&shellhost::link_key(&token), &frame);
    shellchan::spool_shell_frame(owlery, owner, &id, &stamped).map_err(ShellLinkError::Failed)?;
    Ok((id, shell))
}

/// The outcome of a drive-channel write (M11-W2, REQ-SHELL-3). Distinct from a


`resolve_link_target` gives the `Shell` manifest (`shell.tunnel.enable`). Add a daemon-side `ensure_shell_tunnel` open-hook helper in linkhost, wire it at `api bind-shell`. Check linkhost re-exports first:


grep -n 'pub use linkhost\|drive_shell\|resolve_link_target' crates/spt-daemon/src/lib.rs | head

Find linkhost re-exports


206:pub use linkhost::{
207:    drive_channel_write, drive_shell, relink_shell, request_shell_link, DriveDelivery,


206	pub use linkhost::{
207	    drive_channel_write, drive_shell, relink_shell, request_shell_link, DriveDelivery,
208	    ShellLinkError, ShellLinkRequestOutcome,
209	};
210	pub use nethost::{NetConfig, NetHost, NET_EFFECT_SESSION};

pub use linkhost::{
    drive_channel_write, drive_shell, relink_shell, request_shell_link, DriveDelivery,
    ShellLinkError, ShellLinkRequestOutcome,
};
pub use nethost::{NetConfig, NetHost, NET_EFFECT_SESSION};


Edit

pub use linkhost::{
    drive_channel_write, drive_shell, relink_shell, request_shell_link, DriveDelivery,
    ShellLinkError, ShellLinkRequestOutcome,
};

pub use linkhost::{
    drive_channel_write, drive_shell, ensure_shell_tunnel, relink_shell, request_shell_link,
    DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,
};


pub use linkhost::{
    drive_channel_write, drive_shell, relink_shell, request_shell_link, DriveDelivery,
    ShellLinkError, ShellLinkRequestOutcome,
};

pub use linkhost::{
    drive_channel_write, drive_shell, ensure_shell_tunnel, relink_shell, request_shell_link,
    DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,
};

pub use lifecycle::{BrainLifecycle, TickReport};

-    drive_channel_write, drive_shell, relink_shell, request_shell_link, DriveDelivery,

-    ShellLinkError, ShellLinkRequestOutcome,

+    drive_channel_write, drive_shell, ensure_shell_tunnel, relink_shell, request_shell_link,

+    DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,

pub use nethost::{NetConfig, NetHost, NET_EFFECT_SESSION};


/// Drive one vocabulary-checked typed command down the durable command

/// At shell-online (the `api bind-shell` seam), open the shell's opaque tunnel if
/// its manifest enables it (M11-W3, REQ-SHELL-4). Resolves the shell's `[shell]`
/// body and, when `[shell.tunnel].enable`, ensures the broker-held loopback stream
/// pair under the CURRENT link token — so the tunnel's lifetime is the link's
/// (a relink mints a fresh token ⇒ a fresh pair; the stale entry is inert by stamp,
/// the crash-proof R1 leg). Best-effort: `Ok(false)` when the shell declares no
/// tunnel or no link token is parked; `Err` only on a live ensure failure.
// [impl->REQ-SHELL-4]
pub fn ensure_shell_tunnel(owlery: &Path, owner: &str, shell_id: &str) -> io::Result<bool> {
    let Ok((id, _info, shell)) = resolve_link_target(owlery, owner, shell_id) else {
        return Ok(false); // unresolvable manifest ⇒ no tunnel
    };
    if !shell.tunnel.enable {
        return Ok(false);
    }
    let perch = spt_store::perch::resolve_shell_perch_path_in(owlery, owner, &id);
    let Some(token) = shellhost::read_link_token(&perch) else {
        return Ok(false); // no parked link token ⇒ nothing to stamp
    };
    match crate::tunnelhub::tunnel_ensure(
        &crate::endpoint::tunnel_socket_name(),
        owner,
        &id,
        &token,
    )? {
        Ok(_) => Ok(true),
        Err(e) => Err(io::Error::other(e)),
    }
}

/// Drive one vocabulary-checked typed command down the durable command


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

/// At shell-online (the `api bind-shell` seam), open the shell's opaque tunnel if
/// its manifest enables it (M11-W3, REQ-SHELL-4). Resolves the shell's `[shell]`
/// body and, when `[shell.tunnel].enable`, ensures the broker-held loopback stream
/// pair under the CURRENT link token — so the tunnel's lifetime is the link's
/// (a relink mints a fresh token ⇒ a fresh pair; the stale entry is inert by stamp,
/// the crash-proof R1 leg). Best-effort: `Ok(false)` when the shell declares no
/// tunnel or no link token is parked; `Err` only on a live ensure failure.
// [impl->REQ-SHELL-4]
pub fn ensure_shell_tunnel(owlery: &Path, owner: &str, shell_id: &str) -> io::Result<bool> {
    let Ok((id, _info, shell)) = resolve_link_target(owlery, owner, shell_id) else {
        return Ok(false); // unresolvable manifest ⇒ no tunnel
    };
    if !shell.tunnel.enable {
        return Ok(false);
    }
    let perch = spt_store::perch::resolve_shell_perch_path_in(owlery, owner, &id);
    let Some(token) = shellhost::read_link_token(&perch) else {
        return Ok(false); // no parked link token ⇒ nothing to stamp
    };
    match crate::tunnelhub::tunnel_ensure(
        &crate::endpoint::tunnel_socket_name(),
        owner,
        &id,
        &token,
    )? {
        Ok(_) => Ok(true),
        Err(e) => Err(io::Error::other(e)),
    }
}

/// Drive one vocabulary-checked typed command down the durable command

+/// At shell-online (the `api bind-shell` seam), open the shell's opaque tunnel if

+/// its manifest enables it (M11-W3, REQ-SHELL-4). Resolves the shell's `[shell]`

+/// body and, when `[shell.tunnel].enable`, ensures the broker-held loopback stream

+/// pair under the CURRENT link token — so the tunnel's lifetime is the link's

+/// (a relink mints a fresh token ⇒ a fresh pair; the stale entry is inert by stamp,

+/// the crash-proof R1 leg). Best-effort: `Ok(false)` when the shell declares no

+/// tunnel or no link token is parked; `Err` only on a live ensure failure.

+pub fn ensure_shell_tunnel(owlery: &Path, owner: &str, shell_id: &str) -> io::Result<bool> {

+    let Ok((id, _info, shell)) = resolve_link_target(owlery, owner, shell_id) else {

+        return Ok(false); // unresolvable manifest ⇒ no tunnel

+    let perch = spt_store::perch::resolve_shell_perch_path_in(owlery, owner, &id);

+    let Some(token) = shellhost::read_link_token(&perch) else {

+        return Ok(false); // no parked link token ⇒ nothing to stamp

+    match crate::tunnelhub::tunnel_ensure(

+        &crate::endpoint::tunnel_socket_name(),

+        Err(e) => Err(io::Error::other(e)),

/// channel: vocab-check → compose → MAC-stamp under the parked token's link

/// key → spool on the shell perch. The shared core of the local


Wire the open-hook at `api bind-shell`:


                Some((owner, shell_id)) => {
                    eprintln!("BOUND_SHELL:{shell_id} owner={owner} status=online");
                    0
                }

                Some((owner, shell_id)) => {
                    eprintln!("BOUND_SHELL:{shell_id} owner={owner} status=online");
                    // M11-W3 (REQ-SHELL-4): open the opaque tunnel if the manifest
                    // enables it. Best-effort — a tunnel-less shell is the norm,
                    // and a relink re-opens a fresh pair under the new token.
                    match spt_daemon::ensure_shell_tunnel(
                        &spt_store::perch::owlery_dir(),
                        &owner,
                        &shell_id,
                    ) {
                        Ok(true) => eprintln!("SHELL_TUNNEL_OPEN:{shell_id}"),
                        Ok(false) => {}
                        Err(e) => eprintln!("SHELL_TUNNEL_WARN:{shell_id}: {e}"),
                    }
                    0
                }


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

                Some((owner, shell_id)) => {
                    eprintln!("BOUND_SHELL:{shell_id} owner={owner} status=online");
                    0
                }

                Some((owner, shell_id)) => {
                    eprintln!("BOUND_SHELL:{shell_id} owner={owner} status=online");
                    // M11-W3 (REQ-SHELL-4): open the opaque tunnel if the manifest
                    // enables it. Best-effort — a tunnel-less shell is the norm,
                    // and a relink re-opens a fresh pair under the new token.
                    match spt_daemon::ensure_shell_tunnel(
                        &spt_store::perch::owlery_dir(),
                        &owner,
                        &shell_id,
                    ) {
                        Ok(true) => eprintln!("SHELL_TUNNEL_OPEN:{shell_id}"),
                        Ok(false) => {}
                        Err(e) => eprintln!("SHELL_TUNNEL_WARN:{shell_id}: {e}"),
                    }
                    0
                }

+                    // M11-W3 (REQ-SHELL-4): open the opaque tunnel if the manifest

+                    // enables it. Best-effort — a tunnel-less shell is the norm,

+                    // and a relink re-opens a fresh pair under the new token.

+                    match spt_daemon::ensure_shell_tunnel(

+                        &spt_store::perch::owlery_dir(),

+                        Ok(true) => eprintln!("SHELL_TUNNEL_OPEN:{shell_id}"),

+                        Ok(false) => {}

+                        Err(e) => eprintln!("SHELL_TUNNEL_WARN:{shell_id}: {e}"),


Now the surfaces. The shell side (`api tunnel --link`) must resolve owner+id from the token — check how `api drive-poll --link` does it:


378	             sensory is never spooled)"
379	        );
380	    }
381	    0
382	}
383
384	/// `api drive-poll <shell-id> --link <token>` — a Shell drains its single
385	/// pending drive frame (M11-W2, REQ-SHELL-3): the take-and-clear read of the
386	/// owner→shell ephemeral control slot. The link token is the auth (mirrors
387	/// `api emit`); the daemon serves the held frame **exactly-once** and ONLY when
388	/// the token matches the slot's write-time stamp — a frame written under a dead
389	/// link generation is never re-served (no stale-control replay on relink). The
390	/// frame prints to stdout when one is pending; an empty slot is a normal poll
391	/// (no stdout, exit 0). **Never reads a spool** — drive is in-memory only.
392	// [impl->REQ-SHELL-3]
393	// [impl->REQ-API-2]
394	pub fn cmd_drive_poll(id: &str, link: &str) -> i32 {
395	    let owlery = perch::owlery_dir();
396	    let Some((owner, shell_id)) = spt_daemon::shellhost::find_shell_by_token(&owlery, link) else {
397	        eprintln!("DRIVE_POLL_REFUSED:{id}: no instance holds this link token");
398	        return EXIT_REFUSED;
399	    };
400	    if shell_id != id {
401	        eprintln!("DRIVE_POLL_REFUSED:{id}: the link token belongs to {shell_id}");
402	        return EXIT_REFUSED;
403	    }
404	    match spt_daemon::drive_take(&spt_daemon::drive_socket_name(), &owner, &shell_id, link) {
405	        Ok(Some(frame)) => {
406	            // The drive frame to stdout (the shell parses it); the diagnostic to
407	            // stderr, mirroring the relay command drain.
408	            println!("{frame}");
409	            eprintln!("DRIVE_DELIVERED:{shell_id} owner={owner}");
410	            0
411	        }
412	        Ok(None) => {
413	            eprintln!("DRIVE_EMPTY:{shell_id} (no pending drive)");
414	            0
415	        }
416	        Err(e) => {
417	            eprintln!("DRIVE_POLL_FAIL:{shell_id}: {e}");

             sensory is never spooled)"
        );
    }
    0
}

/// `api drive-poll <shell-id> --link <token>` — a Shell drains its single
/// pending drive frame (M11-W2, REQ-SHELL-3): the take-and-clear read of the
/// owner→shell ephemeral control slot. The link token is the auth (mirrors
/// `api emit`); the daemon serves the held frame **exactly-once** and ONLY when
/// the token matches the slot's write-time stamp — a frame written under a dead
/// link generation is never re-served (no stale-control replay on relink). The
/// frame prints to stdout when one is pending; an empty slot is a normal poll
/// (no stdout, exit 0). **Never reads a spool** — drive is in-memory only.
// [impl->REQ-SHELL-3]
// [impl->REQ-API-2]
pub fn cmd_drive_poll(id: &str, link: &str) -> i32 {
    let owlery = perch::owlery_dir();
    let Some((owner, shell_id)) = spt_daemon::shellhost::find_shell_by_token(&owlery, link) else {
        eprintln!("DRIVE_POLL_REFUSED:{id}: no instance holds this link token");
        return EXIT_REFUSED;
    };
    if shell_id != id {
        eprintln!("DRIVE_POLL_REFUSED:{id}: the link token belongs to {shell_id}");
        return EXIT_REFUSED;
    }
    match spt_daemon::drive_take(&spt_daemon::drive_socket_name(), &owner, &shell_id, link) {
        Ok(Some(frame)) => {
            // The drive frame to stdout (the shell parses it); the diagnostic to
            // stderr, mirroring the relay command drain.
            println!("{frame}");
            eprintln!("DRIVE_DELIVERED:{shell_id} owner={owner}");
            0
        }
        Ok(None) => {
            eprintln!("DRIVE_EMPTY:{shell_id} (no pending drive)");
            0
        }
        Err(e) => {
            eprintln!("DRIVE_POLL_FAIL:{shell_id}: {e}");


Shell side resolves owner+id via `find_shell_by_token`. Both surfaces present the same parked link token (R1 stamp). Add the owner CLI `ShellCmd::Tunnel` variant:


    /// Send a text and/or file payload down the durable 2-way text+file
    /// channel (agent→shell; the shell answers via ordinary `spt send`).
    /// File transfers are progress-queryable by xfer id.
    Send {
        /// Canonical id or alias.
        shell_ref: String,
        /// The text payload.
        text: Option<String>,

    /// Use the shell's opaque byte TUNNEL: a held, reliable-ordered QUIC stream
    /// the channel taxonomy never reinterprets (first consumer: USB/IP URB
    /// traffic). `send` pipes raw stdin bytes into the tunnel; `recv` drains
    /// buffered bytes to stdout. The shell opts in via `[shell.tunnel]`; the
    /// tunnel lives for the link (a link-break closes it). Poll-drained at the
    /// surface.
    Tunnel {
        /// Canonical id or alias.
        shell_ref: String,
        /// `send` (raw stdin → tunnel) or `recv` (tunnel → raw stdout).
        direction: String,
        #[arg(long)]
        owner: Option<String>,
    },
    /// Send a text and/or file payload down the durable 2-way text+file
    /// channel (agent→shell; the shell answers via ordinary `spt send`).
    /// File transfers are progress-queryable by xfer id.
    Send {
        /// Canonical id or alias.
        shell_ref: String,
        /// The text payload.
        text: 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)

    /// Use the shell's opaque byte TUNNEL: a held, reliable-ordered QUIC stream
    /// the channel taxonomy never reinterprets (first consumer: USB/IP URB
    /// traffic). `send` pipes raw stdin bytes into the tunnel; `recv` drains
    /// buffered bytes to stdout. The shell opts in via `[shell.tunnel]`; the
    /// tunnel lives for the link (a link-break closes it). Poll-drained at the
    /// surface.
    Tunnel {
        /// Canonical id or alias.
        shell_ref: String,
        /// `send` (raw stdin → tunnel) or `recv` (tunnel → raw stdout).
        direction: String,
        #[arg(long)]
        owner: Option<String>,
    },
    /// Send a text and/or file payload down the durable 2-way text+file
    /// channel (agent→shell; the shell answers via ordinary `spt send`).
    /// File transfers are progress-queryable by xfer id.
    Send {
        /// Canonical id or alias.
        shell_ref: String,
        /// The text payload.
        text: Option<String>,

+    /// Use the shell's opaque byte TUNNEL: a held, reliable-ordered QUIC stream

+    /// the channel taxonomy never reinterprets (first consumer: USB/IP URB

+    /// traffic). `send` pipes raw stdin bytes into the tunnel; `recv` drains

+    /// buffered bytes to stdout. The shell opts in via `[shell.tunnel]`; the

+    /// tunnel lives for the link (a link-break closes it). Poll-drained at the

+        /// `send` (raw stdin → tunnel) or `recv` (tunnel → raw stdout).


Now the owner CLI handler. Add a daemon helper for the owner-side resolve, then the handler after the `ShellCmd::Drive` arm:


/// Drive one vocabulary-checked typed command down the durable command
/// channel: vocab-check → compose → MAC-stamp under the parked token's link

/// Resolve the OWNER-side tunnel stream id for `shell_ref` under `owner` (M11-W3,
/// REQ-SHELL-4): reads the parked link token and presents it to the registry so a
/// dead-generation stream is never surfaced (R1). `Ok(None)` ⇒ no live tunnel (the
/// shell is offline / declares none / the entry is a stale generation).
// [impl->REQ-SHELL-4]
pub fn tunnel_owner_stream(owlery: &Path, owner: &str, shell_ref: &str) -> io::Result<Option<u64>> {
    let Ok((id, _info, _shell)) = resolve_link_target(owlery, owner, shell_ref) else {
        return Ok(None);
    };
    let perch = spt_store::perch::resolve_shell_perch_path_in(owlery, owner, &id);
    let Some(token) = shellhost::read_link_token(&perch) else {
        return Ok(None);
    };
    crate::tunnelhub::tunnel_resolve(
        &crate::endpoint::tunnel_socket_name(),
        owner,
        &id,
        crate::tunnelhub::TunnelEnd::Owner,
        &token,
    )
}

/// Drive one vocabulary-checked typed command down the durable command
/// channel: vocab-check → compose → MAC-stamp under the parked token's link


/// Resolve the OWNER-side tunnel stream id for `shell_ref` under `owner` (M11-W3,
/// REQ-SHELL-4): reads the parked link token and presents it to the registry so a
/// dead-generation stream is never surfaced (R1). `Ok(None)` ⇒ no live tunnel (the
/// shell is offline / declares none / the entry is a stale generation).
// [impl->REQ-SHELL-4]
pub fn tunnel_owner_stream(owlery: &Path, owner: &str, shell_ref: &str) -> io::Result<Option<u64>> {
    let Ok((id, _info, _shell)) = resolve_link_target(owlery, owner, shell_ref) else {
        return Ok(None);
    };
    let perch = spt_store::perch::resolve_shell_perch_path_in(owlery, owner, &id);
    let Some(token) = shellhost::read_link_token(&perch) else {
        return Ok(None);
    };
    crate::tunnelhub::tunnel_resolve(
        &crate::endpoint::tunnel_socket_name(),
        owner,
        &id,
        crate::tunnelhub::TunnelEnd::Owner,
        &token,
    )
}

/// Drive one vocabulary-checked typed command down the durable command
/// channel: vocab-check → compose → MAC-stamp under the parked token's link

+/// Resolve the OWNER-side tunnel stream id for `shell_ref` under `owner` (M11-W3,

+/// REQ-SHELL-4): reads the parked link token and presents it to the registry so a

+/// dead-generation stream is never surfaced (R1). `Ok(None)` ⇒ no live tunnel (the

+/// shell is offline / declares none / the entry is a stale generation).

+pub fn tunnel_owner_stream(owlery: &Path, owner: &str, shell_ref: &str) -> io::Result<Option<u64>> {

+    let Ok((id, _info, _shell)) = resolve_link_target(owlery, owner, shell_ref) else {

+    let perch = spt_store::perch::resolve_shell_perch_path_in(owlery, owner, &id);

+    let Some(token) = shellhost::read_link_token(&perch) else {

+        &crate::endpoint::tunnel_socket_name(),

+        crate::tunnelhub::TunnelEnd::Owner,

/// key → spool on the shell perch. The shared core of the local


    drive_channel_write, drive_shell, ensure_shell_tunnel, relink_shell, request_shell_link,
    DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,

    drive_channel_write, drive_shell, ensure_shell_tunnel, relink_shell, request_shell_link,
    tunnel_owner_stream, DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,


    drive_channel_write, drive_shell, ensure_shell_tunnel, relink_shell, request_shell_link,
    DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,

    drive_channel_write, drive_shell, ensure_shell_tunnel, relink_shell, request_shell_link,
    tunnel_owner_stream, DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,

pub use lifecycle::{BrainLifecycle, TickReport};

-    DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,

+    tunnel_owner_stream, DriveDelivery, ShellLinkError, ShellLinkRequestOutcome,

pub use nethost::{NetConfig, NetHost, NET_EFFECT_SESSION};


Now the owner CLI handler (after the `ShellCmd::Drive` arm closes at line ~5707):


        ShellCmd::Send {
            shell_ref,
            text,
            file,
            owner,
        } => {
            // The durable 2-way text+file channel, agent→shell direction

        ShellCmd::Tunnel {
            shell_ref,
            direction,
            owner,
        } => {
            // The opaque byte tunnel (M11-W3, REQ-SHELL-4): the owner end of a
            // held, reliable-ordered QUIC stream pair the channel taxonomy never
            // reinterprets. `send` pipes raw stdin into the tunnel; `recv` drains
            // buffered bytes to raw stdout (poll-drained; lossless + ordered via
            // the broker's retentive backpressured stream). The owner-side stream
            // resolves only under the live link token (a stale generation is never
            // surfaced — R1). Owner-exclusivity is structural (the ref resolves
            // only under this owner's perch).
            // [impl->REQ-SHELL-4]
            let Some(owner) = owner_or_self(owner) else {
                eprintln!("NO_SELF: shell tunnel needs an owner (--owner <id> or $OWL_SESSION_ID)");
                return 1;
            };
            if let Err(e) = spt_daemon::ensure_running() {
                eprintln!("SHELL_TUNNEL_FAIL: daemon start: {e}");
                return 1;
            }
            let stream_id = match spt_daemon::tunnel_owner_stream(&owlery, &owner, &shell_ref) {
                Ok(Some(s)) => s,
                Ok(None) => {
                    eprintln!("NO_TUNNEL:{shell_ref}: no live tunnel for {owner} (offline, or the shell declares no [shell.tunnel])");
                    return 1;
                }
                Err(e) => {
                    eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: {e}");
                    return 1;
                }
            };
            let sock = spt_daemon::tunnel_socket_name();
            match direction.as_str() {
                "send" => {
                    use std::io::Read;
                    let mut buf = Vec::new();
                    if let Err(e) = std::io::stdin().read_to_end(&mut buf) {
                        eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: stdin read: {e}");
                        return 1;
                    }
                    match spt_daemon::tunnel_send(&sock, stream_id, &buf, false) {
                        Ok(()) => {
                            eprintln!("TUNNEL_SENT:{shell_ref} bytes={}", buf.len());
                            0
                        }
                        Err(e) => {
                            eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: send: {e}");
                            1
                        }
                    }
                }
                "recv" => match spt_daemon::tunnel_recv(&sock, stream_id) {
                    Ok((bytes, finished)) => {
                        use std::io::Write;
                        let _ = std::io::stdout().write_all(&bytes);
                        let _ = std::io::stdout().flush();
                        eprintln!(
                            "TUNNEL_RECV:{shell_ref} bytes={}{}",
                            bytes.len(),
                            if finished { " (eof)" } else { "" }
                        );
                        0
                    }
                    Err(e) => {
                        eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: recv: {e}");
                        1
                    }
                },
                other => {
                    eprintln!("USAGE: shell tunnel <ref> <send|recv> (got '{other}')");
                    2
                }
            }
        }
        ShellCmd::Send {
            shell_ref,
            text,
            file,
            owner,
        } => {
            // The durable 2-way text+file channel, agent→shell direction


        ShellCmd::Tunnel {
            shell_ref,
            direction,
            owner,
        } => {
            // The opaque byte tunnel (M11-W3, REQ-SHELL-4): the owner end of a
            // held, reliable-ordered QUIC stream pair the channel taxonomy never
            // reinterprets. `send` pipes raw stdin into the tunnel; `recv` drains
            // buffered bytes to raw stdout (poll-drained; lossless + ordered via
            // the broker's retentive backpressured stream). The owner-side stream
            // resolves only under the live link token (a stale generation is never
            // surfaced — R1). Owner-exclusivity is structural (the ref resolves
            // only under this owner's perch).
            // [impl->REQ-SHELL-4]
            let Some(owner) = owner_or_self(owner) else {
                eprintln!("NO_SELF: shell tunnel needs an owner (--owner <id> or $OWL_SESSION_ID)");
                return 1;
            };
            if let Err(e) = spt_daemon::ensure_running() {
                eprintln!("SHELL_TUNNEL_FAIL: daemon start: {e}");
                return 1;
            }
            let stream_id = match spt_daemon::tunnel_owner_stream(&owlery, &owner, &shell_ref) {
                Ok(Some(s)) => s,
                Ok(None) => {
                    eprintln!("NO_TUNNEL:{shell_ref}: no live tunnel for {owner} (offline, or the shell declares no [shell.tunnel])");
                    return 1;
                }
                Err(e) => {
                    eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: {e}");
                    return 1;
                }
            };
            let sock = spt_daemon::tunnel_socket_name();
            match direction.as_str() {
                "send" => {
                    use std::io::Read;
                    let mut buf = Vec::new();
                    if let Err(e) = std::io::stdin().read_to_end(&mut buf) {
                        eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: stdin read: {e}");
                        return 1;
                    }
                    match spt_daemon::tunnel_send(&sock, stream_id, &buf, false) {
                        Ok(()) => {
                            eprintln!("TUNNEL_SENT:{shell_ref} bytes={}", buf.len());
                            0
                        }
                        Err(e) => {
                            eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: send: {e}");
                            1
                        }
                    }
                }
                "recv" => match spt_daemon::tunnel_recv(&sock, stream_id) {
                    Ok((bytes, finished)) => {
                        use std::io::Write;
                        let _ = std::io::stdout().write_all(&bytes);
                        let _ = std::io::stdout().flush();
                        eprintln!(
                            "TUNNEL_RECV:{shell_ref} bytes={}{}",
                            bytes.len(),
                            if finished { " (eof)" } else { "" }
                        );
                        0
                    }
                    Err(e) => {
                        eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: recv: {e}");
                        1
                    }
                },
                other => {
                    eprintln!("USAGE: shell tunnel <ref> <send|recv> (got '{other}')");
                    2
                }
            }
        }
        ShellCmd::Send {
            shell_ref,
            text,
            file,
            owner,
        } => {
            // The durable 2-way text+file channel, agent→shell direction

+            // The opaque byte tunnel (M11-W3, REQ-SHELL-4): the owner end of a

+            // held, reliable-ordered QUIC stream pair the channel taxonomy never

+            // reinterprets. `send` pipes raw stdin into the tunnel; `recv` drains

+            // buffered bytes to raw stdout (poll-drained; lossless + ordered via

+            // the broker's retentive backpressured stream). The owner-side stream

+            // resolves only under the live link token (a stale generation is never

+            // surfaced — R1). Owner-exclusivity is structural (the ref resolves

+            // only under this owner's perch).

+            let Some(owner) = owner_or_self(owner) else {

+                eprintln!("NO_SELF: shell tunnel needs an owner (--owner <id> or $OWL_SESSION_ID)");

+            if let Err(e) = spt_daemon::ensure_running() {

+                eprintln!("SHELL_TUNNEL_FAIL: daemon start: {e}");

+            let stream_id = match spt_daemon::tunnel_owner_stream(&owlery, &owner, &shell_ref) {

+                    eprintln!("NO_TUNNEL:{shell_ref}: no live tunnel for {owner} (offline, or the shell declares no [shell.tunnel])");

+                    eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: {e}");

+            let sock = spt_daemon::tunnel_socket_name();

+                    let mut buf = Vec::new();

+                    if let Err(e) = std::io::stdin().read_to_end(&mut buf) {

+                        eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: stdin read: {e}");

+                    match spt_daemon::tunnel_send(&sock, stream_id, &buf, false) {

+                            eprintln!("TUNNEL_SENT:{shell_ref} bytes={}", buf.len());

+                            eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: send: {e}");

+                "recv" => match spt_daemon::tunnel_recv(&sock, stream_id) {

+                    Ok((bytes, finished)) => {

+                        use std::io::Write;

+                        let _ = std::io::stdout().write_all(&bytes);

+                        let _ = std::io::stdout().flush();

+                            "TUNNEL_RECV:{shell_ref} bytes={}{}",

+                            bytes.len(),

+                            if finished { " (eof)" } else { "" }

+                        eprintln!("SHELL_TUNNEL_FAIL:{shell_ref}: recv: {e}");

+                    eprintln!("USAGE: shell tunnel <ref> <send|recv> (got '{other}')");


Now the shell-side `api tunnel`. Find the `ApiCmd` enum + the drive-poll variant to mirror:


Bash

grep -n 'DrivePoll\|drive-poll\|cmd_drive_poll\|enum ApiCmd' crates/spt/src/api/*.rs crates/spt/src/cli.rs | head

Find ApiCmd drive-poll variant


crates/spt/src/api/mod.rs:52:pub enum ApiCmd {
crates/spt/src/api/mod.rs:215:    DrivePoll {
crates/spt/src/api/mod.rs:436:        ApiCmd::DrivePoll { id, link } => reporting::cmd_drive_poll(&id, &link),
crates/spt/src/api/mod.rs:596:        // [unit->REQ-SHELL-3] drive-poll is the shell-side drain; the link is the
crates/spt/src/api/mod.rs:598:        assert!(parse(&["spt", "--adapter", "m", "drive-poll", "shell-0", "--link", "cafe"]).is_ok());
crates/spt/src/api/mod.rs:600:            parse(&["spt", "--adapter", "m", "drive-poll", "shell-0"]).is_err(),
crates/spt/src/api/mod.rs:601:            "drive-poll requires the link credential"
crates/spt/src/api/reporting.rs:384:/// `api drive-poll <shell-id> --link <token>` — a Shell drains its single
crates/spt/src/api/reporting.rs:394:pub fn cmd_drive_poll(id: &str, link: &str) -> i32 {
crates/spt/src/cli.rs:648:    /// daemon, and drained by the shell's `api drive-poll --link`. NEVER spooled


210	    /// Take-and-clear a Shell's pending drive frame: the shell-side drain of the
211	    /// owner→shell ephemeral control channel. REST-only, exactly-once — the
212	    /// daemon serves the single latest frame and ONLY when the link matches the
213	    /// slot's write-time stamp (no stale-control replay on relink). The link
214	    /// token is the auth (mirrors `emit`). The frame, if any, prints to stdout.
215	    DrivePoll {
216	        /// The shell instance id (must match the link token's instance).
217	        id: String,
218	        /// Shell link token (the per-link credential from launch).
219	        #[arg(long)]
220	        link: String,
221	    },
222	    /// Print the adapter's declared capability (`hostable_types`).
223	    Capability,
224	    /// Keyword hints: the full user message arrives on **stdin**; emit at most
225	    /// one matched hint line (declaration order, first unseen wins) for the
226	    /// adapter's context channel. The per-session seen-set fires each hint once
227	    /// per `--session` (a `/clear` = a new session = re-armed). Needs `--manifest`.
228	    Hint {
229	        /// The harness session id keying the once-per-session seen-set.
230	        #[arg(long)]
231	        session: String,
232	    },
233	    /// Graceful live-agent signoff: run the final context save BEFORE
234	    /// teardown, then soft-stop. The `spt shutdown` lifecycle path.
235	    Shutdown {
236	        id: String,
237	        #[command(flatten)]

    /// Take-and-clear a Shell's pending drive frame: the shell-side drain of the
    /// owner→shell ephemeral control channel. REST-only, exactly-once — the
    /// daemon serves the single latest frame and ONLY when the link matches the
    /// slot's write-time stamp (no stale-control replay on relink). The link
    /// token is the auth (mirrors `emit`). The frame, if any, prints to stdout.
    DrivePoll {
        /// The shell instance id (must match the link token's instance).
        id: String,
        /// Shell link token (the per-link credential from launch).
        #[arg(long)]
        link: String,
    },
    /// Print the adapter's declared capability (`hostable_types`).
    Capability,
    /// Keyword hints: the full user message arrives on **stdin**; emit at most
    /// one matched hint line (declaration order, first unseen wins) for the
    /// adapter's context channel. The per-session seen-set fires each hint once
    /// per `--session` (a `/clear` = a new session = re-armed). Needs `--manifest`.
    Hint {
        /// The harness session id keying the once-per-session seen-set.
        #[arg(long)]
        session: String,
    },
    /// Graceful live-agent signoff: run the final context save BEFORE
    /// teardown, then soft-stop. The `spt shutdown` lifecycle path.
    Shutdown {
        id: String,
        #[command(flatten)]


Edit

    DrivePoll {
        /// The shell instance id (must match the link token's instance).
        id: String,
        /// Shell link token (the per-link credential from launch).
        #[arg(long)]
        link: String,
    },
    /// Print the adapter's declared capability (`hostable_types`).
    Capability,

    DrivePoll {
        /// The shell instance id (must match the link token's instance).
        id: String,
        /// Shell link token (the per-link credential from launch).
        #[arg(long)]
        link: String,
    },
    /// Use the shell end of the opaque byte TUNNEL (M11-W3): a held,
    /// reliable-ordered QUIC stream the channel taxonomy never reinterprets
    /// (first consumer: USB/IP URB traffic). `send` pipes raw stdin into the
    /// tunnel; `recv` drains buffered bytes to raw stdout. The link token is the
    /// auth (mirrors `drive-poll`); the stream resolves only under the live link
    /// generation. Poll-drained at the surface.
    Tunnel {
        /// The shell instance id (must match the link token's instance).
        id: String,
        /// `send` (raw stdin → tunnel) or `recv` (tunnel → raw stdout).
        direction: String,
        /// Shell link token (the per-link credential from launch).
        #[arg(long)]
        link: String,
    },
    /// Print the adapter's declared capability (`hostable_types`).
    Capability,


    DrivePoll {
        /// The shell instance id (must match the link token's instance).
        id: String,
        /// Shell link token (the per-link credential from launch).
        #[arg(long)]
        link: String,
    },
    /// Print the adapter's declared capability (`hostable_types`).
    Capability,

    DrivePoll {
        /// The shell instance id (must match the link token's instance).
        id: String,
        /// Shell link token (the per-link credential from launch).
        #[arg(long)]
        link: String,
    },
    /// Use the shell end of the opaque byte TUNNEL (M11-W3): a held,
    /// reliable-ordered QUIC stream the channel taxonomy never reinterprets
    /// (first consumer: USB/IP URB traffic). `send` pipes raw stdin into the
    /// tunnel; `recv` drains buffered bytes to raw stdout. The link token is the
    /// auth (mirrors `drive-poll`); the stream resolves only under the live link
    /// generation. Poll-drained at the surface.
    Tunnel {
        /// The shell instance id (must match the link token's instance).
        id: String,
        /// `send` (raw stdin → tunnel) or `recv` (tunnel → raw stdout).
        direction: String,
        /// Shell link token (the per-link credential from launch).
        #[arg(long)]
        link: String,
    },
    /// Print the adapter's declared capability (`hostable_types`).
    Capability,

+    /// Use the shell end of the opaque byte TUNNEL (M11-W3): a held,

+    /// reliable-ordered QUIC stream the channel taxonomy never reinterprets

+    /// (first consumer: USB/IP URB traffic). `send` pipes raw stdin into the

+    /// tunnel; `recv` drains buffered bytes to raw stdout. The link token is the

+    /// auth (mirrors `drive-poll`); the stream resolves only under the live link

+    /// generation. Poll-drained at the surface.

+        /// The shell instance id (must match the link token's instance).

+        /// `send` (raw stdin → tunnel) or `recv` (tunnel → raw stdout).

+        /// Shell link token (the per-link credential from launch).

/// Keyword hints: the full user message arrives on **stdin**; emit at most


ApiCmd::DrivePoll { id, link } => reporting::cmd_drive_poll(&id, &link),

        ApiCmd::DrivePoll { id, link } => reporting::cmd_drive_poll(&id, &link),
        ApiCmd::Tunnel {
            id,
            direction,
            link,
        } => reporting::cmd_tunnel(&id, &direction, &link),


        ApiCmd::DrivePoll { id, link } => reporting::cmd_drive_poll(&id, &link),
        ApiCmd::Tunnel {
            id,
            direction,
            link,
        } => reporting::cmd_tunnel(&id, &direction, &link),

} => reporting::cmd_emit(&id, &r#type, &payload, &link),

+        } => reporting::cmd_tunnel(&id, &direction, &link),

ApiCmd::OwnerShutdown { id, link } => reporting::cmd_owner_shutdown(&id, &link),


Now the `cmd_tunnel` shell-side handler in reporting.rs (mirror `cmd_drive_poll`, resolve the Shell end):


416	        Err(e) => {
417	            eprintln!("DRIVE_POLL_FAIL:{shell_id}: {e}");
418	            EXIT_REFUSED
419	        }
420	    }
421	}
422
423	/// `api owner-shutdown <shell-id> --link <token>` — a Shell suspends its
424	/// linked owner directly, bypassing agent comms (M5-D4d, CONTEXT §Shell
425	/// model machinery surface). The link token is the auth and names the
426	/// instance ("owner from the link"); the gate is the `can_shutdown`
427	/// pre-consent grant (`owner-shutdown × shell-id × this-node`, authored at
428	/// spawn from the manifest flag) — **fail-closed, no escalation**: the flag
429	/// IS the consent, so an ungranted ask refuses instead of prompting. The
430	/// suspend edge runs the full transition host (echo gate + the D4a shell
431	/// cascade — the firing shell goes offline with its siblings, by design).
432	// [impl->REQ-SHELL-2]
433	pub fn cmd_owner_shutdown(id: &str, link: &str) -> i32 {
434	    use spt_daemon::grants::{self, GrantDecision};
435	    let owlery = perch::owlery_dir();
436	    let Some((owner, shell_id)) = spt_daemon::shellhost::find_shell_by_token(&owlery, link) else {
437	        eprintln!("OWNER_SHUTDOWN_REFUSED:{id}: no instance holds this link token");
438	        return EXIT_REFUSED;
439	    };
440	    if shell_id != id {
441	        eprintln!("OWNER_SHUTDOWN_REFUSED:{id}: the link token belongs to {shell_id}");
442	        return EXIT_REFUSED;
443	    }

        Err(e) => {
            eprintln!("DRIVE_POLL_FAIL:{shell_id}: {e}");
            EXIT_REFUSED
        }
    }
}

/// `api owner-shutdown <shell-id> --link <token>` — a Shell suspends its
/// linked owner directly, bypassing agent comms (M5-D4d, CONTEXT §Shell
/// model machinery surface). The link token is the auth and names the
/// instance ("owner from the link"); the gate is the `can_shutdown`
/// pre-consent grant (`owner-shutdown × shell-id × this-node`, authored at
/// spawn from the manifest flag) — **fail-closed, no escalation**: the flag
/// IS the consent, so an ungranted ask refuses instead of prompting. The
/// suspend edge runs the full transition host (echo gate + the D4a shell
/// cascade — the firing shell goes offline with its siblings, by design).
// [impl->REQ-SHELL-2]
pub fn cmd_owner_shutdown(id: &str, link: &str) -> i32 {
    use spt_daemon::grants::{self, GrantDecision};
    let owlery = perch::owlery_dir();
    let Some((owner, shell_id)) = spt_daemon::shellhost::find_shell_by_token(&owlery, link) else {
        eprintln!("OWNER_SHUTDOWN_REFUSED:{id}: no instance holds this link token");
        return EXIT_REFUSED;
    };
    if shell_id != id {
        eprintln!("OWNER_SHUTDOWN_REFUSED:{id}: the link token belongs to {shell_id}");
        return EXIT_REFUSED;
    }


/// `api owner-shutdown <shell-id> --link <token>` — a Shell suspends its
/// linked owner directly, bypassing agent comms (M5-D4d, CONTEXT §Shell

/// `api tunnel <shell-id> <send|recv> --link <token>` — a Shell drives the SHELL
/// end of the opaque byte tunnel (M11-W3, REQ-SHELL-4): `send` pipes raw stdin
/// into the held stream, `recv` drains buffered bytes to raw stdout. The link
/// token is the auth (mirrors `drive-poll`) and names the instance; the daemon
/// resolves the shell-side stream ONLY under the live link generation (a stale
/// stream is never surfaced — R1). The bytes are opaque end-to-end (no envelope,
/// no taxonomy). Poll-drained; lossless + ordered via the broker's retentive
/// backpressured stream.
// [impl->REQ-SHELL-4]
// [impl->REQ-API-2]
pub fn cmd_tunnel(id: &str, direction: &str, link: &str) -> i32 {
    let owlery = perch::owlery_dir();
    let Some((owner, shell_id)) = spt_daemon::shellhost::find_shell_by_token(&owlery, link) else {
        eprintln!("TUNNEL_REFUSED:{id}: no instance holds this link token");
        return EXIT_REFUSED;
    };
    if shell_id != id {
        eprintln!("TUNNEL_REFUSED:{id}: the link token belongs to {shell_id}");
        return EXIT_REFUSED;
    }
    let sock = spt_daemon::tunnel_socket_name();
    let stream_id = match spt_daemon::tunnel_resolve(
        &sock,
        &owner,
        &shell_id,
        spt_daemon::TunnelEnd::Shell,
        link,
    ) {
        Ok(Some(s)) => s,
        Ok(None) => {
            eprintln!("NO_TUNNEL:{shell_id}: no live tunnel (declare [shell.tunnel] + relink)");
            return EXIT_REFUSED;
        }
        Err(e) => {
            eprintln!("TUNNEL_FAIL:{shell_id}: resolve: {e}");
            return 1;
        }
    };
    match direction {
        "send" => {
            use std::io::Read;
            let mut buf = Vec::new();
            if let Err(e) = std::io::stdin().read_to_end(&mut buf) {
                eprintln!("TUNNEL_FAIL:{shell_id}: stdin read: {e}");
                return 1;
            }
            match spt_daemon::tunnel_send(&sock, stream_id, &buf, false) {
                Ok(()) => {
                    eprintln!("TUNNEL_SENT:{shell_id} bytes={}", buf.len());
                    0
                }
                Err(e) => {
                    eprintln!("TUNNEL_FAIL:{shell_id}: send: {e}");
                    1
                }
            }
        }
        "recv" => match spt_daemon::tunnel_recv(&sock, stream_id) {
            Ok((bytes, finished)) => {
                use std::io::Write;
                let _ = std::io::stdout().write_all(&bytes);
                let _ = std::io::stdout().flush();
                eprintln!(
                    "TUNNEL_RECV:{shell_id} bytes={}{}",
                    bytes.len(),
                    if finished { " (eof)" } else { "" }
                );
                0
            }
            Err(e) => {
                eprintln!("TUNNEL_FAIL:{shell_id}: recv: {e}");
                1
            }
        },
        other => {
            eprintln!("USAGE: api tunnel <id> <send|recv> --link <token> (got '{other}')");
            2
        }
    }
}

/// `api owner-shutdown <shell-id> --link <token>` — a Shell suspends its
/// linked owner directly, bypassing agent comms (M5-D4d, CONTEXT §Shell


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

/// `api tunnel <shell-id> <send|recv> --link <token>` — a Shell drives the SHELL
/// end of the opaque byte tunnel (M11-W3, REQ-SHELL-4): `send` pipes raw stdin
/// into the held stream, `recv` drains buffered bytes to raw stdout. The link
/// token is the auth (mirrors `drive-poll`) and names the instance; the daemon
/// resolves the shell-side stream ONLY under the live link generation (a stale
/// stream is never surfaced — R1). The bytes are opaque end-to-end (no envelope,
/// no taxonomy). Poll-drained; lossless + ordered via the broker's retentive
/// backpressured stream.
// [impl->REQ-SHELL-4]
// [impl->REQ-API-2]
pub fn cmd_tunnel(id: &str, direction: &str, link: &str) -> i32 {
    let owlery = perch::owlery_dir();
    let Some((owner, shell_id)) = spt_daemon::shellhost::find_shell_by_token(&owlery, link) else {
        eprintln!("TUNNEL_REFUSED:{id}: no instance holds this link token");
        return EXIT_REFUSED;
    };
    if shell_id != id {
        eprintln!("TUNNEL_REFUSED:{id}: the link token belongs to {shell_id}");
        return EXIT_REFUSED;
    }
    let sock = spt_daemon::tunnel_socket_name();
    let stream_id = match spt_daemon::tunnel_resolve(
        &sock,
        &owner,
        &shell_id,
        spt_daemon::TunnelEnd::Shell,
        link,
    ) {
        Ok(Some(s)) => s,
        Ok(None) => {
            eprintln!("NO_TUNNEL:{shell_id}: no live tunnel (declare [shell.tunnel] + relink)");
            return EXIT_REFUSED;
        }
        Err(e) => {
            eprintln!("TUNNEL_FAIL:{shell_id}: resolve: {e}");
            return 1;
        }
    };
    match direction {
        "send" => {
            use std::io::Read;
            let mut buf = Vec::new();
            if let Err(e) = std::io::stdin().read_to_end(&mut buf) {
                eprintln!("TUNNEL_FAIL:{shell_id}: stdin read: {e}");
                return 1;
            }
            match spt_daemon::tunnel_send(&sock, stream_id, &buf, false) {
                Ok(()) => {
                    eprintln!("TUNNEL_SENT:{shell_id} bytes={}", buf.len());
                    0
                }
                Err(e) => {
                    eprintln!("TUNNEL_FAIL:{shell_id}: send: {e}");
                    1
                }
            }
        }
        "recv" => match spt_daemon::tunnel_recv(&sock, stream_id) {
            Ok((bytes, finished)) => {
                use std::io::Write;
                let _ = std::io::stdout().write_all(&bytes);
                let _ = std::io::stdout().flush();
                eprintln!(
                    "TUNNEL_RECV:{shell_id} bytes={}{}",
                    bytes.len(),
                    if finished { " (eof)" } else { "" }
                );
                0
            }
            Err(e) => {
                eprintln!("TUNNEL_FAIL:{shell_id}: recv: {e}");
                1
            }
        },
        other => {
            eprintln!("USAGE: api tunnel <id> <send|recv> --link <token> (got '{other}')");
            2
        }
    }
}

/// `api owner-shutdown <shell-id> --link <token>` — a Shell suspends its
/// linked owner directly, bypassing agent comms (M5-D4d, CONTEXT §Shell

+/// `api tunnel <shell-id> <send|recv> --link <token>` — a Shell drives the SHELL

+/// end of the opaque byte tunnel (M11-W3, REQ-SHELL-4): `send` pipes raw stdin

+/// into the held stream, `recv` drains buffered bytes to raw stdout. The link

+/// token is the auth (mirrors `drive-poll`) and names the instance; the daemon

+/// resolves the shell-side stream ONLY under the live link generation (a stale

+/// stream is never surfaced — R1). The bytes are opaque end-to-end (no envelope,

+/// no taxonomy). Poll-drained; lossless + ordered via the broker's retentive

+pub fn cmd_tunnel(id: &str, direction: &str, link: &str) -> i32 {

+    let Some((owner, shell_id)) = spt_daemon::shellhost::find_shell_by_token(&owlery, link) else {

+        eprintln!("TUNNEL_REFUSED:{id}: no instance holds this link token");

+        eprintln!("TUNNEL_REFUSED:{id}: the link token belongs to {shell_id}");

+    let sock = spt_daemon::tunnel_socket_name();

+    let stream_id = match spt_daemon::tunnel_resolve(

+            eprintln!("NO_TUNNEL:{shell_id}: no live tunnel (declare [shell.tunnel] + relink)");

+            eprintln!("TUNNEL_FAIL:{shell_id}: resolve: {e}");

+            if let Err(e) = std::io::stdin().read_to_end(&mut buf) {

+                eprintln!("TUNNEL_FAIL:{shell_id}: stdin read: {e}");

+            match spt_daemon::tunnel_send(&sock, stream_id, &buf, false) {

+                    eprintln!("TUNNEL_SENT:{shell_id} bytes={}", buf.len());

+                    eprintln!("TUNNEL_FAIL:{shell_id}: send: {e}");

+        "recv" => match spt_daemon::tunnel_recv(&sock, stream_id) {

+                let _ = std::io::stdout().write_all(&bytes);

+                let _ = std::io::stdout().flush();

+                    "TUNNEL_RECV:{shell_id} bytes={}{}",

+                    if finished { " (eof)" } else { "" }

+                eprintln!("TUNNEL_FAIL:{shell_id}: recv: {e}");

+            eprintln!("USAGE: api tunnel <id> <send|recv> --link <token> (got '{other}')");

/// model machinery surface). The link token is the auth and names the


Build the workspace with both surfaces wired:


Bash

cargo build -q -p spt 2>&1 | tail -30; echo "BUILD=$?"

Build spt with tunnel surfaces


CLI surface added → must strip milestone codes from clap `///` help (it's the public `--help`) + regen docs. The `ApiCmd::Tunnel` help has "(M11-W3)". Fix it:


Edit

    /// Use the shell end of the opaque byte TUNNEL (M11-W3): a held,
    /// reliable-ordered QUIC stream the channel taxonomy never reinterprets

    /// Use the shell end of the opaque byte TUNNEL: a held,
    /// reliable-ordered QUIC stream the channel taxonomy never reinterprets


    /// Use the shell end of the opaque byte TUNNEL (M11-W3): a held,
    /// reliable-ordered QUIC stream the channel taxonomy never reinterprets

    /// Use the shell end of the opaque byte TUNNEL: a held,
    /// reliable-ordered QUIC stream the channel taxonomy never reinterprets

-    /// Use the shell end of the opaque byte TUNNEL (M11-W3): a held,

+    /// Use the shell end of the opaque byte TUNNEL: a held,

/// (first consumer: USB/IP URB traffic). `send` pipes raw stdin into the

/// tunnel; `recv` drains buffered bytes to raw stdout. The link token is the


Verify no other milestone/REQ codes in the new clap help, then regen docs:


Bash

grep -nE 'M[0-9]+-W|REQ-[A-Z]' crates/spt/src/cli.rs crates/spt/src/api/mod.rs | grep -iE 'tunnel' ; echo "---grep done (empty=clean)---"; cargo run -q -p xtask -- gen 2>&1 | tail -3

Check clap help clean + regen docs


crates/spt/src/cli.rs:5727:            // The opaque byte tunnel (M11-W3, REQ-SHELL-4): the owner end of a
crates/spt/src/api/mod.rs:353:                    // M11-W3 (REQ-SHELL-4): open the opaque tunnel if the manifest
---grep done (empty=clean)---
xtask gen: wrote C:\Users\decid\Documents\projects\spt-core\docs-site/src/cli/reference.md