docs: capture tun2proxy udpgw prior art in phase 0 findings
Documents the previously-undocumented UDP gateway prior art discussed with the alknet-channels POC agent (/workspace/tun2proxy/src/udpgw.rs): per-datagram length framing over a stream (LEN|FLAGS|CONN_ID|[SOCKS5 addr]|DATA), SOCKS5 ATYP addressing in-band, CONN_ID flow multiplexing, keepalive/ERR flag packets, MTU cap, idle expiry. Folds implications into OQ-TN-01 (addressing fork: per-channel vs per-datagram), OQ-TN-02 (framing mechanics production-proven; remaining fork documented), OQ-TN-07 (single-ALPN option strengthened), OQ-TN-09 (flag-packet vocabulary maps to establishment/error frame question), OQ-TN-10 (UDP POC scope narrowed to channels-layer fit), and the survey checklist. Also records the transport story: TCP vs QUIC is the alkcall layer's concern; the tunnel crate is transport-agnostic.
This commit is contained in:
@@ -19,6 +19,13 @@ channel structure, alktunnels generalizes the tunnel handler shape to
|
|||||||
arbitrary bidirectional tunnels in the `ssh -L` / `ssh -D` sense — TCP, UDP,
|
arbitrary bidirectional tunnels in the `ssh -L` / `ssh -D` sense — TCP, UDP,
|
||||||
unix sockets, and other stream or datagram substrates.
|
unix sockets, and other stream or datagram substrates.
|
||||||
|
|
||||||
|
The 2026-09-05 first revision adds the tun2proxy UDP gateway prior art
|
||||||
|
(`OQ-TN-01`, `OQ-TN-02`, `OQ-TN-07`, `OQ-TN-10`) and the transport story
|
||||||
|
clarification (TCP vs QUIC at the channels layer, invisible to this crate —
|
||||||
|
§What is already settled). This prior art was discussed with the POC agent
|
||||||
|
around the alknet-channels POC but never documented; it is captured here so
|
||||||
|
it survives into the spec.
|
||||||
|
|
||||||
## What is already settled
|
## What is already settled
|
||||||
|
|
||||||
The foundation is POC-validated and ADR-pinned; this crate is not starting
|
The foundation is POC-validated and ADR-pinned; this crate is not starting
|
||||||
@@ -50,6 +57,65 @@ from zero. It inherits:
|
|||||||
- **The relay story** — tunnels traverse alkcall hub relays
|
- **The relay story** — tunnels traverse alkcall hub relays
|
||||||
transparently via byte-for-byte data-channel forwarding with ID
|
transparently via byte-for-byte data-channel forwarding with ID
|
||||||
rewrite (alkcall ADR-042). No tunnel-specific relay work.
|
rewrite (alkcall ADR-042). No tunnel-specific relay work.
|
||||||
|
- **The transport story** — the underlying channels transport is TCP or
|
||||||
|
QUIC (QUIC preferred) but that is the alkcall layer's concern, not
|
||||||
|
this crate's: the tunnel protocol sees a `BiStream` and is
|
||||||
|
transport-agnostic like the sibling crates. UDP tunneling (below)
|
||||||
|
rides the same chunk stream; the stream-vs-datagram question is
|
||||||
|
about what the tunnel protocol frames *inside* the channel, not
|
||||||
|
about the transport.
|
||||||
|
|
||||||
|
## Prior art: tun2proxy UDP gateway (`OQ-TN-01`, `OQ-TN-02`, `OQ-TN-07`, `OQ-TN-10`)
|
||||||
|
|
||||||
|
`/workspace/tun2proxy/src/udpgw.rs` implements a UDP gateway over a TCP
|
||||||
|
stream — structurally the same problem this crate faces for UDP tunnels
|
||||||
|
over channels data channels. Discussed with the alknet-channels POC
|
||||||
|
agent as the example of "UDP over a stream substrate," but never
|
||||||
|
documented. Key mechanics, all of which generalize:
|
||||||
|
|
||||||
|
- **Per-datagram length framing over the stream** — the packet format is
|
||||||
|
`LEN(u16 BE) | FLAGS(u8) | CONN_ID(u16) | [SOCKS5 address] | DATA`
|
||||||
|
(`udpgw.rs:82-88`). Boundary preservation is re-added by the protocol,
|
||||||
|
not by the substrate: exactly the "length-prefix each datagram inside
|
||||||
|
the channel" half-answer in OQ-TN-02, proven in production.
|
||||||
|
- **SOCKS5 address format travels per data packet** — `ATYP`
|
||||||
|
(0x01 IPv4 / 0x03 domain / 0x04 IPv6) + variable address + port
|
||||||
|
(`udpgw.rs:68-76`). This is concrete prior art for OQ-TN-01's
|
||||||
|
addressing: a scheme-tagged addressing encoding with v4/v6/domain
|
||||||
|
coverage already standardized. Note the asymmetry with TCP tunnels:
|
||||||
|
for UDP, the remote endpoint is per-datagram, not per-channel.
|
||||||
|
- **One stream carries many UDP flows** — `CONN_ID(u16)` multiplexes
|
||||||
|
associations over a single gateway connection (`udpgw.rs:66`),
|
||||||
|
with `keepalive` (0x01) and `error` (0x20) flag packets as the only
|
||||||
|
non-data frame types (`udpgw.rs:21-26`). This is the "one channel =
|
||||||
|
one association, per-endpoint multiplexing inside" half-answer in
|
||||||
|
OQ-TN-02, with the refinement that the per-endpoint multiplexing key
|
||||||
|
(`CONN_ID`) is protocol-level, allocated by the client, u16.
|
||||||
|
- **Flow lifecycle is packet-level** — `udp_timeout` idle expiry,
|
||||||
|
`keepalive_time` heartbeats on idle connections
|
||||||
|
(`UDPGW_KEEPALIVE_TIME = 30s`, `udpgw.rs:16`), and an MTU cap
|
||||||
|
(`parse_udp_response` rejects `data.len() > udp_mtu`,
|
||||||
|
`udpgw.rs:527`). Also `UDPGW_MAX_CONNECTIONS = 5` pooled gateway
|
||||||
|
connections *above* the packet layer — a throughput choice, not a
|
||||||
|
protocol requirement; channels gives us N channels already.
|
||||||
|
- **Implications for alktunnels:**
|
||||||
|
- UDP boundary preservation over the chunk stream is validated
|
||||||
|
prior art, not speculation — raises confidence in the OQ-TN-02
|
||||||
|
half-answer considerably.
|
||||||
|
- The frame-type set (DATA/KEEPALIVE/ERR) is a useful minimal
|
||||||
|
vocabulary — it maps onto OQ-TN-09's establishment/error frame
|
||||||
|
question (tun2proxy uses flag packets, alktty uses typed control
|
||||||
|
chunks; both are self-contained frames inside the data stream).
|
||||||
|
- Per-datagram addressing (SOCKS5-style) vs per-channel addressing
|
||||||
|
(fixed target at open) is a real fork for the params design: TCP
|
||||||
|
tunnels fix the target at open; UDP associations may either fix
|
||||||
|
one endpoint at open or carry per-datagram addresses like udpgw.
|
||||||
|
This interaction is unresolved and feeds OQ-TN-01 + OQ-TN-07.
|
||||||
|
- NAT/keepalive concerns partially disappear on channels: the
|
||||||
|
underlying transport (QUIC/TCP) handles connection keepalive, and
|
||||||
|
channel liveness is the channels layer's concern. The tunnel
|
||||||
|
protocol likely needs only flow-level idle expiry, not
|
||||||
|
transport-level keepalive packets — TBD in the spec.
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
@@ -87,7 +153,11 @@ Considerations:
|
|||||||
|
|
||||||
**Status:** open — research needed. Half-answer (hunch): a scheme-tagged
|
**Status:** open — research needed. Half-answer (hunch): a scheme-tagged
|
||||||
JSON object rather than a URL-ish string, so params stay typed and
|
JSON object rather than a URL-ish string, so params stay typed and
|
||||||
extensible; exact shape TBD.
|
extensible; exact shape TBD. Strengthened 2026-09-05: the tun2proxy UDP
|
||||||
|
gateway (§Prior art) validates SOCKS5 ATYP addressing (v4/v6/domain) as
|
||||||
|
in-band prior art, and surfaces a fork — for UDP, remote addressing is
|
||||||
|
per-datagram (SOCKS5-style) rather than fixed-at-open like TCP. The
|
||||||
|
params design must account for both modes.
|
||||||
|
|
||||||
### OQ-TN-02: Datagram substrates (UDP) — boundary preservation
|
### OQ-TN-02: Datagram substrates (UDP) — boundary preservation
|
||||||
|
|
||||||
@@ -111,12 +181,16 @@ re-chunked arbitrarily)?
|
|||||||
"association" carries many remote endpoints — does one tunnel channel
|
"association" carries many remote endpoints — does one tunnel channel
|
||||||
carry one endpoint or many, and how are per-endpoint replies routed?
|
carry one endpoint or many, and how are per-endpoint replies routed?
|
||||||
|
|
||||||
**Status:** open — research needed (survey SSH/russh/SOCKS5/tun2proxy
|
**Status:** open — survey mostly resolved by tun2proxy prior art
|
||||||
approaches; likely a targeted POC if boundary preservation is chosen).
|
(§Prior art): per-datagram length framing over the stream is
|
||||||
Half-answer (hunch): length-prefix each datagram inside the channel
|
production-proven (`LEN | FLAGS | CONN_ID | [addr] | DATA`), one
|
||||||
(boundary-preserving), and one channel = one association with per-endpoint
|
stream carries many UDP flows via a protocol-level `CONN_ID`, and
|
||||||
multiplexing inside, mirroring SOCKS5 UDP — but this is exactly the kind
|
flow lifecycle (idle timeout + keepalive) is packet-level. Remaining:
|
||||||
of guess that needs survey + POC before it becomes an ADR.
|
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). A targeted POC
|
||||||
|
(OQ-TN-10 #1) is likely still +EV for the chosen shape.
|
||||||
|
|
||||||
### OQ-TN-03: Direction semantics (`-L` / `-R` / dynamic)
|
### OQ-TN-03: Direction semantics (`-L` / `-R` / dynamic)
|
||||||
|
|
||||||
@@ -237,7 +311,13 @@ consumer — ALPN strings are wire-stable once published.
|
|||||||
substrate enum).
|
substrate enum).
|
||||||
|
|
||||||
**Status:** open — needs the OQ-TN-02 outcome first (if datagrams need
|
**Status:** open — needs the OQ-TN-02 outcome first (if datagrams need
|
||||||
different framing, option B gets stronger).
|
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.
|
||||||
|
|
||||||
### OQ-TN-08: Access control and ownership scope
|
### OQ-TN-08: Access control and ownership scope
|
||||||
|
|
||||||
@@ -292,7 +372,11 @@ Phase 0 may need (in rough priority order, per the SDD process's
|
|||||||
|
|
||||||
1. **UDP tunnel POC** — boundary-preserving length framing over a
|
1. **UDP tunnel POC** — boundary-preserving length framing over a
|
||||||
channels channel, per-endpoint multiplexing inside one association,
|
channels channel, per-endpoint multiplexing inside one association,
|
||||||
backpressure behavior. Derisks OQ-TN-02 (and OQ-TN-07's option B).
|
backpressure behavior. Partially derisked by the tun2proxy prior
|
||||||
|
art (§Prior art) — the POC now mainly validates *channels-layer*
|
||||||
|
fit: chunk-size vs datagram-size interaction, MTU cap against the
|
||||||
|
channels bounded buffers, idle-expiry mapping, and the chosen
|
||||||
|
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
|
2. **Reverse-flow POC** — `-R`-style: the accept side listens, the far
|
||||||
side carries. Derisks OQ-TN-03's advertisement/lifecycle shape.
|
side carries. Derisks OQ-TN-03's advertisement/lifecycle shape.
|
||||||
3. **Unix socket + stdio bridge POC** — cheap; validates "substrate
|
3. **Unix socket + stdio bridge POC** — cheap; validates "substrate
|
||||||
@@ -315,8 +399,12 @@ Candidate reading for the research specialist (to be expanded):
|
|||||||
- SOCKS5 (RFC 1928): addressing (ATYP), UDP ASSOCIATE framing,
|
- SOCKS5 (RFC 1928): addressing (ATYP), UDP ASSOCIATE framing,
|
||||||
per-endpoint multiplexing — the closest standardized "arbitrary
|
per-endpoint multiplexing — the closest standardized "arbitrary
|
||||||
tunnel + UDP" model.
|
tunnel + UDP" model.
|
||||||
- tun2proxy (`/workspace/tun2proxy`): UDP-over-TCP tunnel framing in
|
- tun2proxy (`/workspace/tun2proxy`, `src/udpgw.rs`): UDP gateway over
|
||||||
production; also handles DNS over tunnel.
|
TCP — per-datagram length framing, SOCKS5 per-datagram addressing,
|
||||||
|
CONN_ID flow multiplexing, keepalive/ERR flag packets, MTU cap,
|
||||||
|
idle expiry. Analyzed in §Prior art. Its `socks.rs` /
|
||||||
|
`proxy_handler.rs` are also relevant for the `-D` (dynamic/SOCKS)
|
||||||
|
composition question (OQ-TN-03).
|
||||||
- quinn-proxy-poc (`/workspace/quinn-proxy-poc`) and iroh
|
- quinn-proxy-poc (`/workspace/quinn-proxy-poc`) and iroh
|
||||||
(`/workspace/iroh`): datagram-native transports; how they model
|
(`/workspace/iroh`): datagram-native transports; how they model
|
||||||
per-endpoint flows.
|
per-endpoint flows.
|
||||||
@@ -331,10 +419,15 @@ Candidate reading for the research specialist (to be expanded):
|
|||||||
## Convergence checklist (what Phase 0 must produce)
|
## Convergence checklist (what Phase 0 must produce)
|
||||||
|
|
||||||
- [ ] Survey notes: SSH/SOCKS5/tun2proxy addressing + UDP framing
|
- [ ] Survey notes: SSH/SOCKS5/tun2proxy addressing + UDP framing
|
||||||
(OQ-TN-01, OQ-TN-02)
|
(OQ-TN-01, OQ-TN-02) — tun2proxy UDP gateway done (§Prior art);
|
||||||
- [ ] Recommendation: addressing format sketch (OQ-TN-01)
|
SSH/SOCKS5 addressing survey still open
|
||||||
|
- [ ] Recommendation: addressing format sketch (OQ-TN-01) — SOCKS5
|
||||||
|
ATYP validated as in-band encoding prior art; per-channel vs
|
||||||
|
per-datagram fork unresolved
|
||||||
- [ ] Recommendation: datagram strategy (OQ-TN-02) + ALPN strategy
|
- [ ] Recommendation: datagram strategy (OQ-TN-02) + ALPN strategy
|
||||||
dependent on it (OQ-TN-07)
|
dependent on it (OQ-TN-07) — framing mechanics de-risked by
|
||||||
|
tun2proxy prior art; endpoint-at-open vs per-datagram addressing
|
||||||
|
fork remains
|
||||||
- [ ] Recommendation: direction model (-L/-R/-D) (OQ-TN-03) + API
|
- [ ] Recommendation: direction model (-L/-R/-D) (OQ-TN-03) + API
|
||||||
surface sketch satisfying no-forced-binding (OQ-TN-04)
|
surface sketch satisfying no-forced-binding (OQ-TN-04)
|
||||||
- [ ] Decision input: backend trait vs no-trait (OQ-TN-05)
|
- [ ] Decision input: backend trait vs no-trait (OQ-TN-05)
|
||||||
|
|||||||
Reference in New Issue
Block a user