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,
|
||||
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
|
||||
|
||||
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
|
||||
transparently via byte-for-byte data-channel forwarding with ID
|
||||
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
|
||||
|
||||
@@ -87,7 +153,11 @@ Considerations:
|
||||
|
||||
**Status:** open — research needed. Half-answer (hunch): a scheme-tagged
|
||||
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
|
||||
|
||||
@@ -111,12 +181,16 @@ 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 — research needed (survey SSH/russh/SOCKS5/tun2proxy
|
||||
approaches; likely a targeted POC if boundary preservation is chosen).
|
||||
Half-answer (hunch): length-prefix each datagram inside the channel
|
||||
(boundary-preserving), and one channel = one association with per-endpoint
|
||||
multiplexing inside, mirroring SOCKS5 UDP — but this is exactly the kind
|
||||
of guess that needs survey + POC before it becomes an ADR.
|
||||
**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). A targeted POC
|
||||
(OQ-TN-10 #1) is likely still +EV for the chosen shape.
|
||||
|
||||
### OQ-TN-03: Direction semantics (`-L` / `-R` / dynamic)
|
||||
|
||||
@@ -237,7 +311,13 @@ consumer — ALPN strings are wire-stable once published.
|
||||
substrate enum).
|
||||
|
||||
**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
|
||||
|
||||
@@ -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
|
||||
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
|
||||
side carries. Derisks OQ-TN-03's advertisement/lifecycle shape.
|
||||
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,
|
||||
per-endpoint multiplexing — the closest standardized "arbitrary
|
||||
tunnel + UDP" model.
|
||||
- tun2proxy (`/workspace/tun2proxy`): UDP-over-TCP tunnel framing in
|
||||
production; also handles DNS over tunnel.
|
||||
- tun2proxy (`/workspace/tun2proxy`, `src/udpgw.rs`): UDP gateway over
|
||||
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
|
||||
(`/workspace/iroh`): datagram-native transports; how they model
|
||||
per-endpoint flows.
|
||||
@@ -331,10 +419,15 @@ Candidate reading for the research specialist (to be expanded):
|
||||
## Convergence checklist (what Phase 0 must produce)
|
||||
|
||||
- [ ] Survey notes: SSH/SOCKS5/tun2proxy addressing + UDP framing
|
||||
(OQ-TN-01, OQ-TN-02)
|
||||
- [ ] Recommendation: addressing format sketch (OQ-TN-01)
|
||||
(OQ-TN-01, OQ-TN-02) — tun2proxy UDP gateway done (§Prior art);
|
||||
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
|
||||
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
|
||||
surface sketch satisfying no-forced-binding (OQ-TN-04)
|
||||
- [ ] Decision input: backend trait vs no-trait (OQ-TN-05)
|
||||
|
||||
Reference in New Issue
Block a user