- 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
106 lines
5.4 KiB
Markdown
106 lines
5.4 KiB
Markdown
---
|
||
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) |