Files
alkssh/docs/architecture/decisions/006-mapping-bridge-and-dispatch.md
T
glm-5.3-flash 263f91fdb7 docs(architecture): review-pass cleanup — inconsistencies, stale refs, scaffolding refresh
- OQ-02: rewritten as a clean resolved statement; contradictory
  deferred-table row removed
- OQ-05: 'old-code anti-pattern' framing corrected — ephemeral-fleet
  accept-and-continue is a legitimate case-2 HostKeyPolicy; the crate
  rule is 'no hard-coded trust model', not 'accept-all forbidden';
  dangling 'expected ADR-007' pointer replaced with the real contract
  locations (auth.md, client.md)
- README: OQ counts corrected (6 resolved / 3 open / 4 deferred);
  sibling references normalized to real ADR IDs (alkcall 010 is alkhttp
  014's analog; 'ADR-016-analog' was a wrong number)
- client/auth: OQ-07 status corrected to resolved-at-adapter-level;
  TOFO typo; TOFU framing
- ADR-004: dangling 'ADR-008' -> serve-config defaults
- ADR-006: SOCKS5 typo
- mapping: alktty typo; '(OQ-… note below)' placeholder -> the real
  POC-3 note reference
- research: cross-repo numbers qualified (alktty OQ-44, alkvault
  ADR-021); reverse-runner lesson 5 reframed per correction
- scaffolding: Cargo.toml description/comments and src/lib.rs doc
  comment updated against ADR-002/003/006 (dynamic-forward retired,
  git exec-only, russh pin decided); CHANGELOG logs POC completion and
  ADRs 001-006

Verification: cargo test, cargo clippy --all-targets -- -D warnings,
cargo fmt --check, cargo check — all pass
2026-10-27 10:00:11 +00:00

5.4 KiB
Raw Blame History

status, last_updated
status last_updated
draft 2026-10-27

ADR-006: Mapping decisions — route-1 bridge, exec-only git, session dispatch

Status

Accepted — 2026-10-26. All three decisions are POC-3-validated against published sibling crates; amendments are new ADRs. Amended same day (review untangle): point 3's SOCKS-detect is retired — with the dynamic-forward feature removed (ADR-003 §1a), session dispatch is parse-only and a request-less session channel simply declines. The parse-vs-anything split was only ever needed if a session channel could be a SOCKS stream; no consumer is that client (vanilla -D rides direct-tcpip; the family -D door is client-local, alksocks composed at the assembly). POC-3 stage B's evidence survives as: (a) the load-bearing correction that vanilla -D = client-side SOCKS + plain direct-tcpip, (b) proof that SOCKS-in-channel is feasible if ever wanted back.

Context

mapping-table.md left three choices explicit for the architecture pass: which of alktunnels' two routes serves direct-tcpip; whether git over SSH has a subsystem path; and the session-channel dispatch shape when one channel type serves both parseable commands and SOCKS5-in-channel traffic. POC-3 resolved all three by construction (real git 2.43, real curl 8.5, real OpenSSH through published alkgit/alksocks/alktunnels/alktty).

Decision

  1. direct-tcpip rides the adapter-side bridge (alktunnels route 1). alkssh holds a ChannelClient into the family fabric and calls TunnelSession::open_direct(client, target, substrate), pumping take_halves() against the russh channel (channel_open_direct_tcpip → accept only after the fabric open succeeds → copy bidirectional). POC-3 stage C proved it end to end (8 MiB in ~333 ms; fabric-open failure maps to an explicit SSH ConnectFailed reject — accept happens only post-success, since accept() consumes the handle). Registry-side bridging (route 2: alkssh-shaped ChannelOperations in a serving endpoint's registry) is rejected: it inverts the door posture by putting adapter work upstream of the door. The alktunnels absence note (no serve-over-stream entry) is thereby resolved — route 1 never needed one.
  2. Git over SSH is exec-only; every subsystem request declines. The vanilla git connection model is: git spawns its ssh transport, which opens one session channel and sends exec_request "git-{upload,receive}-pack '/repo'" — the repo path rides inside the command string, so a subsystem request (name only, no repo id) can never dispatch. POC-3 proved the parse grammar (quoted/unquoted paths, service-only → decline, non-git → parse-then-fail with no resource alloc) and clone/push/second-push through the published seam with zero fixups. Vanilla git behavior loses nothing — vanilla git never speaks subsystem for git; the decline only refuses a manual ssh -s-style invocation, with a reason.
  3. Session-channel dispatch: request-driven parse wins; detect covers the request-less case — amended: parse-only. The original decision composed the POC-3 modes as: requests take the parse path (git under git, pipe/shell under session); request-less session channels ran a SOCKS-detect (dynamic-forward: spawn, first-byte 0x05 sniff, 500 ms bounded window). With dynamic-forward removed from the feature graph (ADR-003 §1a), the detect has no owner: session dispatch is parse-only — requests parse to git (exec) or pipe/shell, and a request-less session channel declines as unfeatured. This still resolves OQ-02 (the composition was the unknown; the resolution is now the simpler of the two candidates).
  4. The spawn-and-return rule is a structural constraint, not an implementation detail. russh 0.64.1-era servers run handler callbacks inline inside the session's packet loop — a blocking handler deadlocks against the peer's follow-up bytes (POC-3 proved by construction: receive-pack pushes bytes immediately after the exec request). Every serving path is tokio::spawn + handler-returns-fast; exit reporting from the spawned task rides the cloned Session::handle() — exit-status(then close) before channel close, exit-signal for signal termination (signal-name fidelity: OQ-10).

Consequences

  • Positive: every one-seam-per-mapping row now has its route recorded; the OQ list loses its highest-priority pending item; the alkforge assembly gets an exact wiring recipe (POC-3 §C.4's in-process fabric leg is the template, verbatim).
  • Negative: the SOCKS-detect retirement removes a tunable the POC proved workable — if a family client ever wants a remote SOCKS door, the shape is re-addable via ADR (500 ms bounded window preserved in the reference here).
  • Neutral: route 2 remains documented in mapping-table.md as the rejected alternative, with its rationale — useful if the posture is ever revisited (that revisit is an ADR, not a code flip).

References

  • docs/research/poc-3-mapping-bakeoff.md (stages A–C: all three decisions' evidence; §C.4 the fabric wiring recipe)
  • mapping-table.md (the rows + the absence note this ADR resolves)
  • ADR-003 (the feature gates these dispatch decisions sit behind)
  • docs/research/reverse-runner.md (the client-direction mirror of leg 1 — the -L/-R legs the same route-1 bridge serves)
  • OQ-02 (resolved by this ADR), OQ-10 (exit-signal fidelity, open)