docs(architecture): ADR-006 (route-1 bridge, exec-only git, SOCKS dispatch) + OQ-02 resolved
- ADR-006 (Accepted): direct-tcpip rides the adapter-side bridge
(alktunnels route 1; registry-side bridging rejected as door-posture
inversion; absence note resolved); git over SSH is exec-only with
subsystem declining (vanilla git's connection model is exec of
git-{upload,receive}-pack — POC-3 grammar table); session dispatch:
request-driven parse wins, request-less channels get the bounded
SOCKS-detect (first-byte 0x05, 500 ms default, assembly-configurable);
spawn-and-return recorded as a structural constraint
- OQ-02 resolved by ADR-006; README doc/ADr tables + OQ summary updated
This commit is contained in:
1 parent
4d4da6651c
commit
e9f78bf6f7
3 files changed
+107
-9
No files matched your search
@@ -40,11 +40,13 @@ below are being written. No implementation until specs reach
|
||||
| [ADR-003](decisions/003-feature-graph.md) | The capability feature graph — flat, independent, two-direction | Accepted |
|
||||
| [ADR-004](decisions/004-auth-dispositions.md) | Auth dispositions — publickey-only, fail-closed, fingerprint identity | Accepted |
|
||||
| [ADR-005](decisions/005-at-rest-key-custody.md) | At-rest key custody — vault-wrapped OpenSSH-PEM, decrypt-at-use | Accepted |
|
||||
| [ADR-006](decisions/006-mapping-bridge-and-dispatch.md) | Mapping decisions — route-1 bridge, exec-only git, SOCKS dispatch | Accepted |
|
||||
|
||||
## Open Questions
|
||||
|
||||
Tracked in [open-questions.md](open-questions.md) — OQ-01 … OQ-13,
|
||||
seeded from the phase 0 POC findings and research docs.
|
||||
seeded from the phase 0 POC findings and research docs. Five resolved
|
||||
(ADR-002/006 review + evidence), four deferred with named blockers.
|
||||
|
||||
## Document Lifecycle
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-10-26
|
||||
---
|
||||
|
||||
# ADR-006: Mapping decisions — route-1 bridge, exec-only git, SOCKS dispatch
|
||||
|
||||
## Status
|
||||
|
||||
**Accepted** — 2026-10-26. All three decisions are
|
||||
POC-3-validated against published sibling crates; amendments are new
|
||||
ADRs.
|
||||
|
||||
## 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
|
||||
SOCK5-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.** The two POC-3 modes compose thus:
|
||||
- a session channel that receives `exec_request` /
|
||||
`pty_request` / `shell_request` takes the parse path first
|
||||
(git parse under `git`; else pipe/shell under `session`);
|
||||
- a session channel that receives **no request** (the
|
||||
family-native SOCKS shape — a client speaking SOCKS5 in-band)
|
||||
runs the **SOCKS-detect** under `dynamic-forward`: spawn, first-
|
||||
byte sniff (`0x05`), bounded window — **500 ms default,
|
||||
assembly-configurable**, silent or non-SOCKS → decline (logged);
|
||||
- the detect never runs when the channel got a request, and the
|
||||
parse never runs on a request-less channel. One channel, one
|
||||
route, decided by what actually arrives.
|
||||
This resolves OQ-02 (the composition was the unknown; the window
|
||||
bound and configurability are now records, not questions).
|
||||
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 500 ms detect window is a tunable that can be
|
||||
misconfigured (too low: false declines on slow clients; too high:
|
||||
slow-fail paths) — it is bounded and configurable by design, and
|
||||
the decline is logged.
|
||||
- **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)
|
||||
@@ -25,16 +25,12 @@ alkcall trackers have their own OQ spaces).
|
||||
|
||||
## Theme B — session-channel dispatch & mapping details
|
||||
|
||||
### OQ-02: What is the session-channel dispatch shape (SOCKS-detect vs parse, and who bounds the window)?
|
||||
### OQ-02: ~~What is the session-channel dispatch shape (SOCKS-detect vs parse, and who bounds the window)?~~ — resolved
|
||||
|
||||
- **Origin**: [poc-3-mapping-bakeoff.md](../research/poc-3-mapping-bakeoff.md) §Stage B.3, `mapping-table.md` §notes
|
||||
- **Status**: deferred(unclear)
|
||||
- **Door type**: two-way
|
||||
- **Priority**: high
|
||||
- **Impacts**: blocks the exact dispatch contract detail in the `mapping.md` spec (ordering: exec-parse vs SOCKS-detect vs pipe; the detect window's bound; crate default vs assembly config).
|
||||
- **Investigation**: the pieces exist — POC-3 proven both modes (stage A parse-then-route for git; stage B bounded first-byte sniff for SOCKS) and proven both serve "session" channels behind different features. Unclear is the composition: walk 2+ concrete session-channel flows (git exec, family-native SOCKS client, plain pipe exec) and pin dispatch precedence, the detect window's default bound, and the configuration seam. Do during `mapping.md` writing.
|
||||
- **Resolution**: Not yet decidable by inspection — needs the composition worked through, not new facts.
|
||||
- **Cross-references**: OQ-08
|
||||
- **Status**: resolved (ADR-006 point 3)
|
||||
- **Resolution**: request-driven parse wins (exec/pty/shell → git-parse-else-pipe under their features); request-less session channels under `dynamic-forward` run the SOCKS-detect — spawn + first-byte sniff (`0x05`), bounded 500 ms default (assembly-configurable), silent/non-SOCKS → decline. One channel, one route, decided by what arrives. The composition — the actual unknown — was worked through against the POC-3 stage A/B constructions; the window bound and configurability record as values, not questions.
|
||||
- **Cross-references**: ADR-006, ADR-003
|
||||
|
||||
### OQ-03: ~~Username disposition~~ — resolved
|
||||
|
||||
|
||||
Reference in new issue
Block a user