- The route-1 bridge (ADR-006 decision 1, the POC-3 stage-C recipe re-landed as contract): per accepted `direct-tcpip` / `direct-streamlocal` open, the target/substrate are constructed from the SSH open arguments and the spawned task runs `TunnelSession::open_direct` over the held `Arc<ChannelClient>` → accept only after the fabric open succeeds → `take_halves()` pumped against the taken channel (`pump_bidi`) — the open hook returns fast; a fabric failure answers the explicit `ConnectFailed` reject (the wave-2 stub's reason surviving as the real decline; the stub retired from the direct rows, kept on `forwarded-tcpip` for w3-tunnel-listen). - The cap across the fabric window (recorded): the admission is taken at the hook and held across the fabric open; a fabric failure releases the count inside the spawned task — the admitted-but- rejected shape no `channel_close` can ever fire for. `ChannelCap` became a mutex-backed `Arc`: admissions stay packet-loop-only, releases have exactly two disjoint sites (close hook, bridge task), both saturating. - The identity ride recorded (the `w2-auth-bridge` parked item): the alkhub `hub_leg.rs` one-time-set precedent is a serving-side set; the client-side `take_call_connection()` seam is rejected for this door (one-shot detach of the shared assembly handle's open surface — a fortiori the relay note's "wrong for hubs"); the identity the fabric's `AccessControl::check` consults is the dial identity (CF-005), the composition is assembly policy, and the door structurally refuses anonymous serving (`no-identity`, unreachable on the wire). Upstream ask booked for the gate. - The OQ-11 runtime-Unix gate: `direct-streamlocal` compiles wherever `tunnel` compiles; off-Unix a clean open-decline before any fabric call (dial-call witness at zero), on-Unix the real bridge. - Decline classes recorded: policy → `AdministrativelyProhibited` (unfeatured/cap/`no-identity`); cannot-bridge → `ConnectFailed` (failed fabric open, absent handle under the feature, invalid target, off-Unix streamlocal); the event vocabulary gains `invalid-target` and `unix-unavailable`. - The optional dep lands: `alktunnels 0.4.1` (pin re-verified against crates.io — max stable, MIT OR Apache-2.0, rust_version 1.88, alkcall 0.10.1 exact), wired `tunnel = ["dep:alktunnels"]`, `local` never enabled. - The fabric double (`tests/tunnel_mapping.rs`): the POC-3 §C.4 in-process recipe — `ChannelsAdapter` + `InstallChannelZero` + the alktunnels openables + a hand-rolled dial/dial-fail seam (in-process duplex targets keyed by the rendered string; the smoke's dial is real TCP). Rows: both-substrate serve round trips, fabric-failure, cap refusal + release + recovery, the fabric-ACL composed-gate witness, the CF-006 identity witness, the OQ-11 gate, and the `ssh -L` + curl real-tool smoke (conditional-skip posture, ran green). Verification: `cargo test` rows green per feature build (default, tunnel, git, session, git+session, tunnel+git, tunnel+session, --all-features); `cargo clippy --all-targets -- -D warnings` ×2 (default + --all-features) clean; `cargo fmt --check`; `cargo doc --no-deps` 0 warnings; `taskgraph validate` (26 tasks).
14 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| w3-tunnel-direct | Wave 3 — tunnel leg 1 (the route-1 bridge — direct-tcpip + streamlocal) | completed |
|
moderate | medium | phase | implementation |
Description
Land the tunnel branch's proven leg (ADR-006 decision 1) — the
adapter-side route-1 bridge for direct-tcpip and direct-streamlocal
(POC-3 stage C's shape, re-landed as contract):
- The route-1 bridge — the
tunnel_row_stubreplaced with the real flow: per accepted open, the target/substrate are constructed from the SSH open arguments (direct-tcpip(host, port) →SubstrateAddr::HostPort+Substrate::Tcp;direct-streamlocal(socket path) →SubstrateAddr::Path+Substrate::Unix) and spawn the flow POC-3 proved:TunnelSession::open_direct(& channel_client, target, substrate)→ accept only after the fabric open succeeds (accept consumes the handle — the reject shape is only possible pre-accept) → on fabric failure the explicitConnectFailedreject (the stub's reason survives as the real decline;tunnel_seam_stub_declineretires into it) → on successtake_halves()pumped against the taken channel's stream (pump_bidi— the alkcall primitive the POC recipe used) on the spawned task, per spawn-and-return. The open hook returns fast with the fabric open in flight. - The cap admission across the fabric window — the wave-2 stub
declined without consuming a count; the real flow admits. Mechanics
to get right (record): the admission is taken at the hook (the
packet-loop single-writer site — no race window against the cap
check) and held across the fabric open; a fabric failure must
release the count inside the spawned task (the
channel_close-hook release point only fires for established channels — a rejected open never established one), so a failed bridge cannot leak budget. Decide the release mechanism and record. - Identity propagation to the family fabric (auth.md §Server
direction step 4 — the visible-surface invariant's gate): the
resolved identity rides the channels connection so the fabric's
AccessControl::check(peer_identity)gates the mapped opens. The parked wave-2auth_succeededitem lands here: on auth completion the handler propagates once via alkcall'sConnection:: set_identity— mechanics decided and recorded (the alkhubhub_leg.rsone-time-set precedent:ChannelClient:: take_call_connection()→set_identity(identity); check thetakesemantics against alkcall 0.10.1's docs — the relay note says the detach is wrong for hubs; verify a door's reading and hook placement,auth_succeededvs the first seam call). - The runtime-Unix gate (OQ-11 — universal compile, clean
open-decline off-Unix):
direct-streamlocalcompiles wherevertunnelcompiles; off-Unix a clean open-decline before any fabric call (never a partial bridge); on-Unix the real bridge. - Contract rows through the harness, per build (the wave-2 gate's
booked acceptor vocabulary note is the listen leg's — this task's
rows ride plain client opens): a git/session build with
tunneloff declines the tunnel opens (unfeatured,AdministrativelyProhibited); the featured rows: a configuredtunnelhandle (feature +Arc< ChannelClient>) serves, an absent handle declined with a clean error even under the feature (the unchanged unset-declines posture — the wave-1 config note), the cap rows (cap-gated before spawn — never a leaked count over a failed bridge: the release mechanism pinning row), and theConnectFailedrow over a refusing fabric double. The fabric double is the POC-3 §C.4 in-process recipe as test composition (ChannelsAdapter + InstallChannelZero + the alktunnels openables + a hand-rolled dial/dial-fail seam) — hand-rolled intests/, never a sibling-checkout path. - The optional dep lands here (
alktunnels = "0.4.1", wiredtunnel = ["dep:alktunnels"]— first consuming code; crates.io pin re-verified before committing). - The real-tool smoke — one
ssh -L(+ curl) through the harness against the fabric double (tooling posture decided like the git task — required vs conditional-skip, recorded).
Acceptance Criteria
direct-tcpipserves through the route-1 bridge: fabric open → accept →take_halves()pump on the spawned task; failure → the explicitConnectFailedreject, the stub retired; the open hook returns fast- The cap admission across the fabric window pinned (admit at the hook, release on fabric failure inside the spawned task — no leaked count, no double-admit)
- The identity propagates to the channels connection once
(
set_identitymechanics + hook placement decided and recorded); the wave-2auth_succeededitem discharged direct-streamlocalcarries the runtime-Unix gate (compile-everywhere, clean open-decline off-Unix before any fabric call)- The per-build decline-matrix rows green through the harness
(unfeatured/unset/fabric-failure/cap/serve classes), each with
its contract stamp; the
ssh -Lsmoke row - The
alktunnelsoptional dep lands, pin re-verified;tunnel-off builds carry no alktunnels types in code paths cargo test(+ per-feature +--all-featuresrows), clippy ×2 (-D warnings), fmt,cargo doc --no-depspass
References
- docs/architecture/mapping.md (the
direct-tcpip/direct- streamlocalchannel-type rows; §the structural constraint) - docs/architecture/decisions/006-mapping-bridge-and-dispatch.md (decision 1 — route 1; the reject shape; the wiring recipe pointer)
- docs/architecture/decisions/003-feature-graph.md (the tunnel row, the OQ-11 runtime-Unix gate)
- docs/architecture/auth.md §Server direction step 4 (set_identity, once), §Open questions (all resolved)
- docs/research/poc-3-mapping-bakeoff.md §Stage C (the bridge + the failure shapes + §C.4 the fabric recipe + the identity witness)
- alktunnels 0.4.1:
consumer.rs:89-148(TunnelSession::open,open_direct),consumer.rs:235-260(take_halves),params.rs:105-126(SubstrateAddr/TunnelDirectParams),error.rs(TunnelOpenError) - alkcall 0.10.1:
channels/client.rs(take_call_connection),core/types.rs:541(Connection::set_identity),channels/hub_leg.rs:217-220(the one-time-set precedent)
Notes
Recorded at implementation time (2026-10-29). All decisions live in the module docs (
src/tunnel_map.rs,src/channel_cap.rs,src/handler.rs) and are summarized here for the gate.
-
The cap release mechanism (the task's first decide-and-record) —
ChannelCapbecame aparking_lot::Mutex<usize>shared as anArc: admissions stay packet-loop-only (everyadmitcall site is an open hook — the check-and-increment is one atomic section against the shared writer), while releases have exactly two disjoint sites — thechannel_closehook for established channels, and inside the spawned bridge task on a fabric-open failure (the admitted-but-rejected shape: ADR-006 decision 1 rejects the open, so noCHANNEL_CLOSEcan ever cycle back, and the count must be released where the failure is known — before the reject is answered, so the very next open observes the recovered budget). Both release sites saturate. Pinning rows:a_failed_bridge_releases_the_cap_count_before_the_next_open(failure-then-serve undermax_concurrent_channels: 1— a leaked count would answerAdministrativelyProhibited) andthe_cap_refuses_beyond_the_ceiling_and_recovers_on_close(refusal + close-hook recovery). -
set_identitymechanics (the task's second decide-and-record; thew2-auth-bridgeparked item discharged) — the alkhubhub_leg.rsone-time-set precedent is a serving-side set (the hub resolves the peer identity and sets it on its accept-side channel-0Connectioninside the channel-0 install, before any dispatch). The client-side surface the task named (ChannelClient::take_call_connection()→CallConnection::connection()→Connection::set_identity) was checked and rejected for this door, verified against alkcall 0.10.1's docs: the take is a one-shot single-slot detach ("taking it detaches the client's own calling surface" — every latercall_open_op, and with it everyTunnelSession::open_direct, errors "channel client closed"), and the slot is the shared assembly handle (SshAdapterhands oneArc<ServeConfig>to every mounted session), so the first session's take would detach the open surface for every later session of the whole serving direction — the relay note's "the detach is wrong for hubs" applies a fortiori. The identity that alkcall's protocol consults at the fabric's serving dispatch is the channels connection's dial identity (CF-005: set once at dial, propagated to channel 0 atfrom_connection) — identity crossing the alkcall wire is not a protocol concept. Decision: the composition — which identity the fabric leg dials with, against which the fabric's ACL gates alkssh's mapped opens — is assembly policy (the assembly supplies both the injectedIdentityProviderand the fabric leg); alkssh structurally refuses anonymous serving (theno-identityopen guard, unreachable on the wire — russh authorizes before channels exist — fail-closed like the git/tty rows) so no SSH-shaped exception to the visible-surface invariant exists, and the harness rows witness the composed gate end to end:the_fabric_acl_refuses_an_unscoped_channels_identity(the fabric'sAccessControl::checkrefusing a scopeless channels identity — the establisher never dialed) anddirect_tcpip_serves_through_the_route_1_bridge's identity-witness assertion (the establisher's per-call auth seeing the channels identity — POC-3 §C.3). Hook placement: the identity store stays theauth_publickeyride-along in handler state; the tunnel rows' dispatch reads it;auth_succeededstays unimplemented (russh default) — nothing rides it that the store does not carry. Upstream ask booked for the wave-3 gate: a non-detaching post-construction identity seam onChannelClient(or per-session fabric legs) would let the door attach per-peer identities to a shared leg; nothing is silently absorbed. -
Wire decline classes (recorded) — two vanilla-observable classes at the open: policy refusals →
AdministrativelyProhibited(unfeatured, cap-exceeded, theno-identityguard) vs cannot-bridge →ConnectFailed([dispatch::connect_failed_open_decline] — the wave-2 stub's reason surviving as the real decline): the failed fabric open, the absent fabric handle under the feature (seam-unconfigured, the unset-declines posture), an impossible wire target (invalid-target, an out-of-range port), and the off-Unixdirect-streamlocalOQ-11 gate (unix-unavailable). The classes are distinguishable in the event vocabulary, which gainedinvalid-targetandunix-unavailable. -
Admission mechanics — the pre-admit decline classes are checked in order (unset handle → no-identity → invalid target), so every pure-decline path consumes nothing; the admission is taken at the hook and held across the fabric open; the success release rides the established channel's close.
forwarded-tcpipkept its pre-admithas_roomcap gate and stub — its-Rdata-leg flow isw3-tunnel-listenmaterial (the harness's russh client cannot initiate the type; its wire rows are acceptor vocabulary). -
The fabric double — the POC-3 §C.4 in-process recipe as test composition (
wire_fabricintests/tunnel_mapping.rs):ChannelsAdapter+InstallChannelZero+ChannelOperations+register_tunnel_direct_openable+Dispatcher::serve_single_streamover a tokio duplex, the connect side theArc<ChannelClient>the serve-config holds. The hand-rolled dial/dial-fail seam is an in-process map of duplex halves keyed by the establisher-rendered target string (consume-once — the cap-recovery row reads the consume-once dial as its release signal), with an atomics call counter for the "never called" assertions (the ACL row and the OQ-11 row); the smoke's dial seam is a real-TCP closure (alktunnels'localfeature stays off everywhere — the shipped crate and the tests). -
The real-tool smoke — tooling posture decided like the git task (conditional skip with a printed reason when
ssh/curlis absent), and it ran green here: onessh -N -L(OpenSSH client) through a TCP-mounted adapter → the route-1 bridge → the fabric double with a real-TCP dial → a local HTTP endpoint, curled through the forwarded port (body parity asserted). 8/8 rows green intests/tunnel_mapping.rs. -
Pins re-verified against crates.io (2026-10-29): alktunnels 0.4.1 is
max_stable_version/newest_version, MIT OR Apache-2.0,rust_version = 1.88≤ ours 1.89, alkcall0.10.1exact (single-versioned against ouralkcall = "0.10"); wiredtunnel = ["dep:alktunnels"],default-features = false(thelocalfeature never enabled).tunnel-off builds carry no alktunnels types in code paths (dep-to-feature gating; verified by the per-feature test rows). -
Verification (all green):
cargo test(default +--features tunnel/git/session/git,session/tunnel,git/tunnel,session/--all-features),cargo clippy --all-targets -- -D warnings×2 (default +--all-features),cargo fmt --check,cargo doc --no-deps0 warnings,taskgraph validate(26 tasks).