docs(architecture): retire the dynamic-forward feature — the -D untangle
Review untangle (three layers: local door / wire protocol / fabric): - vanilla ssh -D binds its SOCKS5 proxy client-side (the local door — alksocks' local-door shape) and opens plain direct-tcpip per connection: the server needs nothing SOCKS-shaped (tunnel row + route-1 bridge cover the leg; POC-3 correction 2 already said this) - the family-native SOCKS5-in-channel server row served no consumer of ours: family apps compose alksocks' local door locally over the alkssh client's direct-tcpip DialFn — alkssh doesn't depend on alksocks on this path - amendments: ADR-003 §1a (feature removed), ADR-006 point 3 (SOCKS- detect retired; session dispatch is parse-only; request-less session channels decline), OQ-02 resolution updated with the simplification, mapping-table.md rows annotated (research doc keeps POC-3 stage B as the re-add feasibility record)
This commit is contained in:
1 parent
e9f78bf6f7
commit
c7ab19ed8a
4 files changed
+76
-46
No files matched your search
@@ -13,18 +13,23 @@ decline paths are contract territory on every side of every feature.
|
||||
|
||||
## Context
|
||||
|
||||
`mapping-table.md` (POC-3-validated) split the SSH `session` channel
|
||||
one channel type carries shell, exec, git, and SOCKS traffic, and
|
||||
vanilla SSH multiplexes them all through session channels. The feature
|
||||
`mapping-table.md` (POC-3-validated) split the SSH `session` stream
|
||||
into capabilities by what rides it (shell/exec, git, pipes), and
|
||||
vanilla SSH multiplexes tunnel legs through `direct-tcpip`/`forwarded-tcpip`
|
||||
channels (a vanilla `ssh -D` is its own client-side SOCKS door plus
|
||||
`direct-tcpip` — the wire never sees SOCKS, POC-3 correction 2). The feature
|
||||
graph is how alkforge gets an endpoint that is *structurally incapable*
|
||||
of serving what it didn't enable: a git-only build accepts no shell, no
|
||||
tunnels, no SOCKS — and, in the opposite direction, no capabilities a
|
||||
client build didn't opt into.
|
||||
of serving what it didn't enable: a git-only build accepts no shell and
|
||||
no tunnels — and, in the opposite direction, no capabilities a client
|
||||
build didn't opt into.
|
||||
|
||||
Phase 0 validated the seams end to end (POC-3 stages A–D: git exec,
|
||||
SOCKS-in-channel, direct-tcpip via the alktunnels bridge, session/pty,
|
||||
all against published siblings) and the user's direction confirmed the
|
||||
scope: full feature set except sftp (placeholder until alkfs lands).
|
||||
direct-tcpip via the alktunnels bridge, session/pty, all against
|
||||
published sibling crates — with stage B additionally establishing,
|
||||
against real vanilla `ssh -D` + curl, that the wire-level SOCKS story
|
||||
is client-side; its SOCKS-in-channel serve path was subsequently
|
||||
retired, see §1a), and the user's direction confirmed the scope: full
|
||||
feature set except sftp (placeholder until alkfs lands).
|
||||
|
||||
## Decision
|
||||
|
||||
@@ -35,18 +40,37 @@ scope: full feature set except sftp (placeholder until alkfs lands).
|
||||
|---|---|---|---|
|
||||
| `session` | session channels: pty/shell, pipe-mode exec | client pty/shell/exec/subsystem opens | alktty |
|
||||
| `git` | git-parseable exec (dispatched out of session channels) | client git consumption of upstream servers | alkgit |
|
||||
| `tunnel` | `direct-tcpip`, `forwarded-tcpip`, `direct-streamlocal`, the `tcpip_forward`/`streamlocal_forward` listen flow | client `direct-tcpip` opens; the client `-R` leg (listen request + forwarded-channel acceptor) | alktunnels |
|
||||
| `dynamic-forward` | SOCKS5-in-channel on session channels (family-native client shape) | client-side SOCKS consumption | alksocks |
|
||||
| `tunnel` | `direct-tcpip`, `forwarded-tcpip`, `direct-streamlocal`, the `tcpip_forward`/`streamlocal_forward` listen flow | client `direct-tcpip` opens (which is what `-D` rides — see 1a) | alktunnels |
|
||||
| `sftp` | *placeholder — declines until alkfs lands (OQ-13)* | client sftp half, also deferred | alkfs (future) |
|
||||
|
||||
**1a. No `dynamic-forward` feature.** Amended 2026-10-26 (review
|
||||
untangle): the earlier graph carried a `dynamic-forward` feature
|
||||
with a server-side SOCKS5-in-channel row — a family-native client
|
||||
shape ("speak SOCKS into a session channel, server dials through
|
||||
alksocks"). No consumer of ours is that client: vanilla `ssh -D`
|
||||
does its SOCKS5 handshake client-side and opens plain
|
||||
`direct-tcpip` channels (POC-3 correction 2), and the family
|
||||
behavior of `-D` is a **client-side local door** — alksocks' local
|
||||
door given a `DialFn` backed by the alkssh client's
|
||||
`direct-tcpip` opens, composed at the assembly. The server needs
|
||||
only the `tunnel` row for the whole `-D` leg, and alkssh does not
|
||||
depend on alksocks on this path. The SSH-detect dispatch from
|
||||
ADR-006 point 3 retires with it (session dispatch is parse-only;
|
||||
a request-less session channel declines). If a deferred future
|
||||
(OQ-01's relay/browser leg) ever produces a client that genuinely
|
||||
wants a remote SOCKS door, adding it back is an explicit ADR —
|
||||
the two transports are "just byte streams", so nothing forbids
|
||||
it; nothing needs it.
|
||||
|
||||
2. **Flat and independent.** No feature implies another. `default = []`
|
||||
— a default build has no capabilities at all; every unfeatured
|
||||
channel type/open/request declines with a clean error. `git` does
|
||||
not imply `session` (a git-only build serves the git-parseable exec
|
||||
path by declining to parse to anything else — the parse-then-fail
|
||||
contract from POC-3 stage A); `dynamic-forward` does not imply
|
||||
`session` (SOCKS-in-channel rides session channels but is gated at
|
||||
dispatch, before any serve).
|
||||
contract from POC-3 stage A). Note the `tunnel` client leg's SOCKS
|
||||
door is *local to the dialing host* (alksocks composed at the
|
||||
assembly), not a wire capability — the wire sees `direct-tcpip`
|
||||
only.
|
||||
3. **Two-direction symmetry.** Each feature name gates *both* the
|
||||
server-direction (what an embedded alkssh serves vanilla clients)
|
||||
and the client-direction (what a family app drives when dialing an
|
||||
|
||||
@@ -3,13 +3,22 @@ status: draft
|
||||
last_updated: 2026-10-26
|
||||
---
|
||||
|
||||
# ADR-006: Mapping decisions — route-1 bridge, exec-only git, SOCKS dispatch
|
||||
# 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.
|
||||
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
|
||||
|
||||
@@ -49,21 +58,18 @@ alkgit/alksocks/alktunnels/alktty).
|
||||
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).
|
||||
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
|
||||
@@ -81,10 +87,10 @@ alkgit/alksocks/alktunnels/alktty).
|
||||
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.
|
||||
- **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).
|
||||
|
||||
@@ -25,12 +25,11 @@ 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)?~~ — resolved
|
||||
### OQ-02: ~~What is the session-channel dispatch shape (SOCKS-detect vs parse, and who bounds the window)?~~ — resolved (then simplified)
|
||||
|
||||
- **Origin**: [poc-3-mapping-bakeoff.md](../research/poc-3-mapping-bakeoff.md) §Stage B.3, `mapping-table.md` §notes
|
||||
- **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
|
||||
- **Status**: resolved — resolved twice over. First by ADR-006 point 3 (dispatch composition: request-driven parse wins, request-less channels get the detect). Then **simplified by the ADR-003 §1a amendment**: the `dynamic-forward` feature retired (no consumer is ever the family-native SOCKS-in-channel client; vanilla `-D` is client-side SOCKS + plain `direct-tcpip`; the family `-D` door is alksocks composed locally at the assembly), so the detect lost its owner. Final shape: **session dispatch is parse-only**; request-less session channels decline. POC-3 stage B survives as the feasibility record for re-adding a remote SOCKS door via explicit ADR.
|
||||
- **Cross-references**: ADR-006, ADR-003 §1a
|
||||
|
||||
### OQ-03: ~~Username disposition~~ — resolved
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ request-level extras.
|
||||
| `session` → `pty_request`+`shell_request` | `ssh host` interactive shell | `session` | alktty producer: `NegotiateRequest` constructed locally → `drive_session` (or pre-allocated variant) | `pty_request` args → `TerminalParamsWire` (term/cols/rows/pixels; SSH modes → `modes` JSON reserved); shell case needs a cmd convention (OQ); PTY mode merges stderr into stdout (kernel PTY). **[POC-3 corrected]** the data-plane mapping is a *translation bridge*, not a byte pump: alktty's plane is chunk-framed both directions (stdin/stdout/stderr + CTRL_IN/CTRL_OUT chunks), so alkssh encodes SSH data→stdin chunks and decodes stdout→data, stderr→extended_data, exit chunk→exit-status/exit-signal. New OQ: the exit chunk carries no signal identity (negative code only) |
|
||||
| `session` → `exec_request` (non-git command) | `ssh host "cmd"` | `session` | alktty pipe mode: `terminal: None`, separate stderr | stderr flows back as SSH `extended_data` code 1 — only valid without PTY |
|
||||
| `session` → `exec_request`/`subsystem_request` git | `git push/clone ssh://` | `git` | alkgit: parse command → `Service` + repo (same grammar as `preamble.rs`, without `\0host=`) → `serve_duplex_resolved(repo_id, identity, service, duplex, ...)` | Process-mode dialect is correct (SSH supplies service+repo out of band; server speaks first = advertisement). **[POC-3 corrected]** git over SSH is exec-only: a `subsystem` request carries the service *name* but no repo id, so even a git-service subsystem must decline — vanilla git always executes `git-upload-pack '/repo'`. POC-3: vanilla clone/push/second-push through the published seam + substrate composition, exit-status in both directions |
|
||||
| `session` → SOCKS5-in-channel | `ssh -D` | `dynamic-forward` | alksocks: `serve_socks5_over(channel_duplex, dial, egress, accept, can_bind)` | alkssh embeds `Socks5Producer`-shaped config from the assembly; `DialFn`/`EgressFactory` injected at assembly. **[POC-3 corrected]** this row is the family-native client shape — a vanilla `ssh -D` does its SOCKS5 client-side and opens plain `direct-tcpip` channels (the `tunnel` row above), so the two rows split by client shape, not just feature. POC-3: SOCKS-detect (bounded first-byte sniff) + real curl through the session channel |
|
||||
| `session` → SOCKS5-in-channel | (n/a — retired) | ~~`dynamic-forward`~~ **retired (ADR-003 §1a)** | was alksocks: `serve_socks5_over` | **Post-architecture retirement:** no consumer is ever the family-native client shape this row served. Vanilla `ssh -D` does SOCKS client-side and opens plain `direct-tcpip` (POC-3 correction 2 → the `tunnel` row); the family `-D` behavior is a *client-local* alksocks door over the alkssh client's `direct-tcpip` opens (assembly composition). POC-3 stage B remains the evidence that SOCKS-in-channel is feasible if a family client ever wants a remote SOCKS door (back via explicit ADR). **[ADR-003 amended]** |
|
||||
| `direct-tcpip` | `ssh -L` | `tunnel` | alktunnels (two routes, below) | Target = `SubstrateAddr::HostPort`/`Path`; `Substrate` = Tcp/Unix |
|
||||
| `forwarded-tcpip` | `ssh -R` (data channel) | `tunnel` | alktunnels: `register_tunnel_forwarded_openable`'s `ForwardedHandler(params, BiStream)` shape — in the adapter, hand the russh channel duplex to the handler | listen flow via global-request rows below |
|
||||
| `direct-streamlocal` | unix-socket forward | `tunnel` | same `SubstrateAddr::Path` treatment | unix-only; decline on other platforms (compile-time feature + runtime decline) |
|
||||
@@ -60,12 +60,13 @@ a shell, a pipe-mode command. The failure matrix this produces:
|
||||
unfeatured channel type declines at open.
|
||||
|
||||
Dispatch is parse-then-route by command shape: parsing to a git
|
||||
service and repo requires `git`; SOCKS-in-channel requires
|
||||
`dynamic-forward`; otherwise the alktty pipe/pty path requires
|
||||
`session`. Parsing happens before dispatch and before any resource
|
||||
allocation (a leaked channel alloc is a capability leak — decline
|
||||
first). Exact grammar/precedence is a phase 1 spec question (OQ
|
||||
candidate).
|
||||
service and repo requires `git`; otherwise the alktty pipe/pty path
|
||||
requires `session`. Parsing happens before dispatch and before any
|
||||
resource allocation (a leaked channel alloc is a capability leak —
|
||||
decline first). (Post-architecture: the former SOCKS-detect dispatch
|
||||
arm is retired with the `dynamic-forward` feature — ADR-003 §1a /
|
||||
ADR-006 amendment; request-less session channels decline.) Grammar/
|
||||
precedence per ADR-006.
|
||||
|
||||
## Global-request rows (`tcpip_forward` / `streamlocal_forward`) — the `-R` listen flow
|
||||
|
||||
|
||||
Reference in new issue
Block a user