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

106 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
status: draft
last_updated: 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)