docs: SSH/SOCKS5 survey + fold findings into OQ ledger
Research specialist survey (docs/research/ssh-socks5-survey.md):
SSH forwarding model (direct-tcpip/forwarded-tcpip payloads, open-
failure reason codes, tcpip-forward registration lifecycle), SOCKS5
(ATYP, CONNECT, UDP ASSOCIATE), error-vocabulary comparison table,
endpoint-at-open vs per-datagram analysis, and an explicit anti-
prior-art list (SSH window updates, packet-size negotiation, dual
channel numbering, SOCKS5 FRAG/auth/BND semantics, udpgw
KEEPALIVE/pooling — all mechanisms channels already owns or the
substrate change obsoletes).
Key corrections to phase-0 findings:
- OQ-TN-02 category error fixed: SSH has NO UDP forwarding at all;
the earlier '-D UDP associate over SSH' line was wrong (SSH -D
carries only the SOCKS5 TCP control connection). Real UDP-over-
stream prior art: tun2proxy udpgw + SOCKS5's own UDP relay.
- OpenSSH -D is protocol-level indistinguishable from -L (one
direct-tcpip channel per SOCKS CONNECT) — direct confirmation of
the -D = 'tunnel a socks5 connection' resolution.
OQ ledger updates:
- OQ-TN-01: minimal params shape supported ({resource, substrate});
direct-streamlocal is the extensibility template; ATYP not needed
in base params
- OQ-TN-02: RESOLVED as split by path — endpoint-at-open for base UDP
resources; per-datagram addressing only inside the -D payload;
channel ID replaces CONN_ID (no second demux); KEEPALIVE drops
- OQ-TN-07: option A (single alk/tunnel ALPN) strengthened
- OQ-TN-09: reason-code vocabulary from SSH's four codes + detail
string; udpgw ERR bit flagged as the counterexample to avoid
- OQ-TN-10: reverse-flow POC gains the tcpip-forward template
- Checklist: SSH/SOCKS5 survey item checked off
This commit is contained in:
@@ -307,6 +307,16 @@ and discovery residue). Direction of travel:
|
||||
field layout (Phase 1 spec, ADR before first consumer — wire-stable
|
||||
once published).
|
||||
|
||||
**Survey input 2026-09-06** (`ssh-socks5-survey.md`): SSH's
|
||||
`direct-tcpip` payload reduces to "target + informational originator" —
|
||||
the originator pair has no analogue here (ACL rides the channels
|
||||
open-op machinery), supporting the two-field reframe. OpenSSH's
|
||||
`direct-streamlocal` extension is the extensibility template: new
|
||||
substrate = same open-op shape, degenerate address slots, new type
|
||||
string → new `substrate` value, not a format change. SOCKS5 ATYP is
|
||||
not needed in base params (dynamic-path addressing only). Minimal
|
||||
shape: `{ "resource": <id>, "substrate": "tcp" | "udp" | <extensible> }`.
|
||||
|
||||
### OQ-TN-02: Datagram substrates (UDP) — boundary preservation
|
||||
|
||||
Does a UDP tunnel preserve datagram boundaries end-to-end, or does the
|
||||
@@ -316,11 +326,14 @@ re-chunked arbitrarily)?
|
||||
- Channels is a chunk stream with bounded buffers; the zero-length chunk
|
||||
is the EOF sentinel — datagram boundaries are *not* preserved by the
|
||||
substrate (alknet ADR-071/093; the POC only exercised TCP).
|
||||
- SSH's `-D` UDP associate tunnels UDP as a stream with per-datagram
|
||||
framing re-added by the tunnel protocol (e.g. SOCKS5 UDP over TCP).
|
||||
Note (2026-09-05): russh does not support UDP channels at all — no
|
||||
`direct-udp`-style prior art exists there; the tun2proxy gateway
|
||||
(§Prior art) is the strongest framing precedent.
|
||||
- **Corrected 2026-09-06** (`ssh-socks5-survey.md`): an earlier line
|
||||
here claimed "SSH's `-D` UDP associate tunnels UDP as a stream" — a
|
||||
category error. SSH has *no UDP forwarding at all* (RFC 4254 defines
|
||||
only TCP/X11/session channels; russh is grep-confirmed UDP-free;
|
||||
only SSH3 — a different HTTP/3 protocol — has `direct-udp`). SSH
|
||||
`-D` carries only the SOCKS5 *TCP* control connection. The real
|
||||
UDP-over-stream prior art is tun2proxy udpgw (§Prior art) and
|
||||
SOCKS5's own UDP relay.
|
||||
- iroh and quinn-proxy-poc have native datagram transports; tun2proxy has
|
||||
a full UDP-over-TCP model worth reading.
|
||||
- Boundary preservation is a wire-format decision (per-datagram length
|
||||
@@ -331,20 +344,30 @@ re-chunked arbitrarily)?
|
||||
"association" carries many remote endpoints — does one tunnel channel
|
||||
carry one endpoint or many, and how are per-endpoint replies routed?
|
||||
|
||||
**Status:** open — survey mostly resolved by tun2proxy prior art
|
||||
(§Prior art): per-datagram length framing over the stream is
|
||||
production-proven (`LEN | FLAGS | CONN_ID | [addr] | DATA`), one
|
||||
stream carries many UDP flows via a protocol-level `CONN_ID`, and
|
||||
flow lifecycle (idle timeout + keepalive) is packet-level. Remaining:
|
||||
whether alktunnels fixes the UDP endpoint at open (per-channel, TCP-
|
||||
like) or carries per-datagram addresses (udpgw-like), and whether a
|
||||
u16 conn-id vocabulary is right for channels (vs the channel ID
|
||||
itself doing the demux and one channel per UDP flow). Note (2026-09-05,
|
||||
hub model §Prior art): if UDP resources are produced like any other
|
||||
resource, endpoint-at-open aligns naturally with resource naming
|
||||
(OQ-TN-01); per-datagram addressing matches the `-D`/dynamic-target
|
||||
composition path instead. A targeted POC (OQ-TN-10 #1) is likely still
|
||||
+EV for the chosen shape.
|
||||
**Status: resolved as a split by path, 2026-09-06** (survey
|
||||
`ssh-socks5-survey.md` + tun2proxy prior art):
|
||||
|
||||
- **Base open-op UDP resources: endpoint-at-open.** The resource
|
||||
identifies the endpoint; one channel = one UDP flow (or one pinned
|
||||
association). Aligns with resource naming (OQ-TN-01); per-datagram
|
||||
addressing would reintroduce the "general addressing in params" the
|
||||
reframe removed. Boundary preservation inside the channel stays
|
||||
per-datagram length framing (tun2proxy-proven) — endpoint-at-open is
|
||||
about addressing, not about dropping the LEN prefix.
|
||||
- **Dynamic/`-D` UDP (SOCKS5-style): per-datagram addressing inside
|
||||
the tunnel payload** (SOCKS5 UDP header or udpgw format), composed
|
||||
at the assembly layer, never in base params.
|
||||
- **The channel ID replaces udpgw's CONN_ID** — a conn-id inside the
|
||||
channel would be a second demux layer (AGENTS.md convention 10).
|
||||
One channel per UDP flow; no protocol-level flow key.
|
||||
- **KEEPALIVE drops** — udpgw's heartbeats exist for NAT-traversed
|
||||
long-lived TCP; channels transport liveness is the alkcall layer's
|
||||
concern. Flow-level idle expiry remains producer-side bookkeeping.
|
||||
- A UDP gateway resource (multi-endpoint, udpgw-shaped) remains
|
||||
possible *inside* a channel as self-describing framing — invisible
|
||||
to base params, keeping OQ-TN-07 option A viable. A targeted POC
|
||||
(OQ-TN-10 #1) remains +EV for the channels-layer fit
|
||||
(chunk-size vs datagram-size, MTU vs bounded buffers, idle expiry).
|
||||
|
||||
### OQ-TN-03: Direction semantics (`-L` / `-R` / dynamic)
|
||||
|
||||
@@ -486,14 +509,17 @@ consumer — ALPN strings are wire-stable once published.
|
||||
consumer-side API bifurcation (two session types vs one with a
|
||||
substrate enum).
|
||||
|
||||
**Status:** open — needs the OQ-TN-02 outcome first (if datagrams need
|
||||
different framing, option B gets stronger). Note from the tun2proxy
|
||||
prior art (§Prior art): udpgw runs its packet framing over a plain TCP
|
||||
stream — one framing covers both the stream and datagram cases there.
|
||||
If alktunnels follows the same shape (datagram framing self-describing
|
||||
inside the channel), option A (single `alk/tunnel` ALPN) stays viable
|
||||
even with UDP support; option B remains cleaner if the datagram
|
||||
channel needs structurally different framing from the first chunk on.
|
||||
**Status: option A strengthened, 2026-09-06** (survey
|
||||
`ssh-socks5-survey.md`): SSH uses one channel mechanism for all
|
||||
forwarding types (the type string is per-open metadata, not a separate
|
||||
transport); SOCKS5 runs CONNECT and UDP ASSOCIATE over one control
|
||||
connection with a CMD discriminator; udpgw proves datagram framing
|
||||
self-describes over a stream. With OQ-TN-02 resolved as endpoint-at-
|
||||
open for base UDP resources, the substrate discriminator in `params`
|
||||
tells the handler which framing to expect — exactly option A's shape.
|
||||
Option B (`alk/tunnel-dgram`) remains defensible only if Phase 1 wants
|
||||
structurally different framing from byte zero with no params-dependent
|
||||
dispatch; prior art gives no reason to prefer that.
|
||||
|
||||
### OQ-TN-08: Access control and ownership scope
|
||||
|
||||
@@ -578,15 +604,29 @@ POC issue #6). What's missing is the error/level above bytes:
|
||||
(standard two-pump behavior) — is that always desired, or does the
|
||||
consumer need a "close both" control?
|
||||
|
||||
**Status: direction set 2026-09-05** — the original half-answer is
|
||||
accepted: a self-contained control frame (alktty ADR-006 shape)
|
||||
**Status: direction set 2026-09-05; vocabulary informed 2026-09-06**
|
||||
(survey `ssh-socks5-survey.md` §Comparison). The original half-answer
|
||||
is accepted: a self-contained control frame (alktty ADR-006 shape)
|
||||
carrying an establishment result/error, sent before any data chunk;
|
||||
dial errors are tunnel-closing (the whole channel dies), whereas
|
||||
byte-level EOFs stay per-direction. Beyond that, the frame vocabulary
|
||||
should follow the alktty stream-splitting pattern (§Prior art: the
|
||||
alktty stream-splitting pattern) rather than invent a parallel
|
||||
mechanism. Concrete frame set lands as a Phase 1 ADR (wire-stable
|
||||
before the first consumer).
|
||||
byte-level EOFs stay per-direction. The alktty-style JSON frame is the
|
||||
strictly richest of the four prior-art vocabularies (SSH code+desc,
|
||||
SOCKS5 codes-only, udpgw 1 opaque bit — the counterexample to avoid).
|
||||
|
||||
Survey inputs for the Phase 1 frame ADR:
|
||||
- Content: establishment ack + failure frame with a small reason-code
|
||||
set mirroring SSH's four (policy-denied / dial-failed /
|
||||
unknown-resource-or-substrate / resource-shortage — mapping 1:1 onto
|
||||
what an open handler can produce: ACL denial, dial failure,
|
||||
unregistered resource/substrate, channels limits) + a detail string
|
||||
(SOCKS5's network/host/refused granularity lives in `detail`, not
|
||||
more codes — codes are wire-stable, detail strings are not).
|
||||
- Failure ⇒ channel closes before data flows: all four prior arts
|
||||
agree; there is no "fail the open but keep the channel" prior art,
|
||||
and none is needed (consumer just opens a new channel).
|
||||
- The frame does NOT need: window adjustments (channels owns
|
||||
backpressure), keepalives (channels owns liveness), fragmentation
|
||||
(not the tunnel's job), per-datagram error replies.
|
||||
|
||||
### OQ-TN-10: POC scope for what remains unvalidated
|
||||
|
||||
@@ -603,6 +643,9 @@ Phase 0 may need (in rough priority order, per the SDD process's
|
||||
endpoint-addressing shape. Derisks OQ-TN-02 (and OQ-TN-07's option B).
|
||||
2. **Reverse-flow POC** — `-R`-style: the accept side listens, the far
|
||||
side carries. Derisks OQ-TN-03's advertisement/lifecycle shape.
|
||||
Template available: SSH's `tcpip-forward` global-request
|
||||
registration → per-accept `forwarded-tcpip` opens → cancel
|
||||
(`ssh-socks5-survey.md` §RFC 4254 §7.1).
|
||||
3. **Unix socket + stdio bridge POC** — cheap; validates "substrate
|
||||
agnostic" beyond IP substrates.
|
||||
4. **Two-pump helper extraction spike** — OQ-TN-06, only after 1–3.
|
||||
@@ -648,12 +691,18 @@ Candidate reading for the research specialist (to be expanded):
|
||||
- alktty: `NegotiateRequest` shape (self-contained negotiation
|
||||
precedent), `TtyBackend` inversion point, `TTY_OPEN_SCOPE` access
|
||||
gate.
|
||||
- `ssh-socks5-survey.md` (this directory, 2026-09-06): SSH
|
||||
channel-open/open-failure/forwarding model, SOCKS5 ATYP/CONNECT/
|
||||
UDP ASSOCIATE, error-vocabulary comparison, endpoint-at-open vs
|
||||
per-datagram analysis, anti-prior-art list (what NOT to carry over).
|
||||
Feeds OQ-TN-01/02/07/09 statuses above.
|
||||
|
||||
## Convergence checklist (what Phase 0 must produce)
|
||||
|
||||
- [ ] Survey notes: SSH/SOCKS5 addressing + UDP framing
|
||||
(OQ-TN-01, OQ-TN-02) — tun2proxy UDP gateway done (§Prior art);
|
||||
SSH/SOCKS5 survey delegated to research specialist
|
||||
- [x] Survey notes: SSH/SOCKS5 addressing + UDP framing
|
||||
(OQ-TN-01, OQ-TN-02) — tun2proxy UDP gateway (§Prior art) +
|
||||
`ssh-socks5-survey.md` (SSH/SOCKS5, error vocabularies,
|
||||
endpoint-vs-per-datagram, anti-prior-art list)
|
||||
- [x] Reframe landed (OQ-TN-01) — params = self-contained JSON open-op
|
||||
object identifying a produced resource + substrate
|
||||
discriminator (`tcp`/`udp`/extensible); producer owns the
|
||||
|
||||
Reference in New Issue
Block a user