Renames the written-usage rule into practice across the contract surface: docs/ (architecture, research, plans, sdd), tasks/ (all 43 task files, taskgraph validate passes), AGENTS.md, README.md, CHANGELOG.md, and the .opencode/agents/ prompts. Every replacement is a sense-preserving rewrite (colons, semicolons, commas, parentheses, sentence splits, varied), never a mechanical swap; no spaced hyphens were substituted. Enum fields, frontmatter, tables, wire data, and cited identifiers survive verbatim; en dashes inside numeric ranges keep their meaning. Verification: taskgraph validate (43 tasks), repo grep clean (single exempt hit: the verification command's own search pattern in AGENTS.md), cargo test, clippy --all-targets -D warnings, fmt --check, doc all green.
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), placing 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, and 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, where 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, with 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, and 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, with 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, since 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, so 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, meaning 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; the 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, either required or 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, with no leaked count and 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, so 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, where 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", so 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, and 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), since identity crossing the alkcall wire is not a protocol concept. Decision: the composition, meaning 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 since 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), since 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, so 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, as 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, and 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, in both the shipped crate and the tests). -
The real-tool smoke, with 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).