Files
alkssh/tasks/implementation/w3-tunnel-direct.md
T
glm-5.3-flash f4b5f896b4 docs: sweep em dashes from all markdown prose
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.
2026-11-04 20:28:00 +00:00

14 KiB
Raw Blame History

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
w3-tty-session
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_stub replaced 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 explicit ConnectFailed reject (the stub's reason survives as the real decline; tunnel_seam_stub_decline retires into it) → on success take_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-2 auth_succeeded item lands here: on auth completion the handler propagates once via alkcall's Connection:: set_identity, with mechanics decided and recorded (the alkhub hub_leg.rs one-time-set precedent: ChannelClient:: take_call_connection() → set_identity(identity); check the take semantics 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_succeeded vs the first seam call).
  • The runtime-Unix gate (OQ-11: universal compile, clean open-decline off-Unix): direct-streamlocal compiles wherever tunnel compiles; 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 tunnel off declines the tunnel opens (unfeatured, AdministrativelyProhibited); the featured rows: a configured tunnel handle (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 the ConnectFailed row 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 in tests/, never a sibling-checkout path.
  • The optional dep lands here (alktunnels = "0.4.1", wired tunnel = ["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-tcpip serves through the route-1 bridge: fabric open → accept → take_halves() pump on the spawned task; failure → the explicit ConnectFailed reject, 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_identity mechanics + hook placement decided and recorded); the wave-2 auth_succeeded item discharged
  • direct-streamlocal carries 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 -L smoke row
  • The alktunnels optional dep lands, pin re-verified; tunnel-off builds carry no alktunnels types in code paths
  • cargo test (+ per-feature + --all-features rows), clippy ×2 (-D warnings), fmt, cargo doc --no-deps pass

References

  • docs/architecture/mapping.md (the direct-tcpip / direct- streamlocal channel-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): ChannelCap became a parking_lot::Mutex<usize> shared as an Arc: admissions stay packet-loop-only (every admit call 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, the channel_close hook 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 no CHANNEL_CLOSE can 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 under max_concurrent_channels: 1, where a leaked count would answer AdministrativelyProhibited) and the_cap_refuses_beyond_the_ceiling_and_recovers_on_close (refusal + close-hook recovery).

  • set_identity mechanics (the task's second decide-and-record; the w2-auth-bridge parked item discharged), the alkhub hub_leg.rs one-time-set precedent is a serving-side set (the hub resolves the peer identity and sets it on its accept-side channel-0 Connection inside 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 later call_open_op, and with it every TunnelSession::open_direct, errors "channel client closed"), and the slot is the shared assembly handle (SshAdapter hands one Arc<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 at from_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 injected IdentityProvider and the fabric leg); alkssh structurally refuses anonymous serving (the no-identity open 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's AccessControl::check refusing a scopeless channels identity, the establisher never dialed) and direct_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 the auth_publickey ride-along in handler state; the tunnel rows' dispatch reads it; auth_succeeded stays 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 on ChannelClient (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, the no-identity guard) 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-Unix direct-streamlocal OQ-11 gate (unix-unavailable). The classes are distinguishable in the event vocabulary, which gained invalid-target and unix-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-tcpip kept its pre-admit has_room cap gate and stub, as its -R data-leg flow is w3-tunnel-listen material (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_fabric in tests/tunnel_mapping.rs): ChannelsAdapter + InstallChannelZero + ChannelOperations + register_tunnel_direct_openable + Dispatcher::serve_single_stream over a tokio duplex, the connect side the Arc<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' local feature 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/curl is absent), and it ran green here: one ssh -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 in tests/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, alkcall 0.10.1 exact (single-versioned against our alkcall = "0.10"); wired tunnel = ["dep:alktunnels"], default-features = false (the local feature 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-deps 0 warnings, taskgraph validate (26 tasks).