docs: codec direction set — raw pass-through (TCP) + u16 length prefix (UDP)

Structural rationale: alktty's 5-byte header is a sub-demux key for
five logical streams sharing one BiStream; a tunnel has exactly one
data stream per direction, so no type byte. TCP needs no length
prefix (channels already length-prefixes every chunk). UDP needs
boundary re-framing only, and u16 suffices (UDP max payload 65507).

Wire overhead table: tunnel TCP = 8 B/chunk total (channels header
only), tunnel UDP = 10 B/datagram, vs alktty's 13 B. udpgw
calibration: 5 B + per-datagram address.

Trade-offs accepted: no in-band control path for TCP channels ever
(post-ADR-049 this is clean; escape hatch = new ALPN, not a wire
change); sentinel collision does not exist (UDP len=0 = empty
datagram, legal; EOF is the channels-level sentinel on the BiStream
at a different layer); future multi-endpoint UDP gateway framing
lives inside datagram payloads (udpgw precedent).

Residual for the Phase 1 codec ADR: empty-datagram semantics; u16 BE
over varint (leaned, MTU-bounded anyway).
This commit is contained in:
2026-09-07 07:21:25 +00:00
parent d88e148fdc
commit 20672be48c
+56 -8
View File
@@ -233,11 +233,54 @@ Implications:
split/pump plumbing is tokio-async, no substrate types in the split/pump plumbing is tokio-async, no substrate types in the
protocol layer. Same as alktty. protocol layer. Same as alktty.
Open: whether the tunnel codec is *literally* alktty's 5-byte format **Codec direction set 2026-09-06 (discussion): raw pass-through for
with different stream-type constants (reuse), a stripped 5-byte stream substrates, `u16` length-prefix for UDP — no 5-byte header.**
variant (1 stream type + ctrl), or raw pass-through with framing only Phase 1 ADR, before the first consumer.
for UDP (the OQ-TN-01 reframe suggests TCP needs nothing beyond the
channels chunk stream). Phase 1 ADR, before the first consumer. Rationale (structural, not stylistic): alktty's 5-byte
`[stream_type:u8][len:u32]` exists because five logical streams share
one `BiStream` — the type byte is a sub-demux key. A tunnel has
exactly one data stream per direction (the channel's own read/write
halves), so there is nothing to demux and no per-chunk type byte. TCP
needs no length prefix either: the channels layer already
length-prefixes every chunk (8-byte header), so the codec is pure
pass-through. Only UDP needs boundary re-framing (datagram boundaries
do not survive the chunk stream), and `u16` suffices — UDP's max
payload is 65507 < 65535.
Per-chunk wire overhead (data chunks, both directions):
| | channels header | tunnel codec | total |
|---|---|---|---|
| alktty in channels | 8 B | 5 B (type + len) | 13 B |
| tunnel TCP | 8 B | **0 B** | 8 B |
| tunnel UDP | 8 B | **2 B** (len:u16) | 10 B |
Calibration: udpgw's packet header is 5 bytes + a per-datagram SOCKS5
address (its CONN_ID/FLAGS vocabulary is dropped per OQ-TN-02); a
DNS-sized datagram (~100 B) pays ~3% for boundary framing.
Trade-offs accepted:
- **No in-band control path for TCP channels, ever** (any future
mid-stream signaling is a wire break). Post-ADR-049 this is clean —
establishment is call-level (`channel:open_failed`), the survey
found no mid-stream control need, and the escape hatch is
protocol-level (a new ALPN is cheap; a wire change is not). The
udpgw KEEPALIVE/ERR vocabulary is dropped per OQ-TN-02.
- **Sentinel collision does not exist** across layers: the UDP codec's
`len=0` means *empty datagram* (legal in UDP; DNS uses it, e.g. TCP
length-prefix `0`), while EOF is the channels-level sentinel on the
`BiStream` (`length=0` in the channels 8-byte header). They live at
different layers and do not interact.
- UDP-specific framing lives only in the UDP path; a hypothetical
future multi-endpoint UDP gateway resource would carry its own
self-describing framing *inside* the datagram payloads (udpgw
precedent, OQ-TN-02) — invisible to this base codec.
Remaining for the Phase 1 codec ADR: confirm `len=0` empty-datagram
semantics (send allowed? receive maps to a zero-payload datagram),
and whether the UDP length prefix rides `u16 BE` (leaned) or a varint
(rejected for v1 simplicity — datagrams are MTU-bounded anyway).
## Open Questions ## Open Questions
@@ -754,9 +797,14 @@ Candidate reading for the research specialist (to be expanded):
unknown_resource / resource_shortage / handler_error / timeout), unknown_resource / resource_shortage / handler_error / timeout),
never a phantom channel. Residual for Phase 1: half-open never a phantom channel. Residual for Phase 1: half-open
semantics only; v1 needs no mid-stream control frame semantics only; v1 needs no mid-stream control frame
- [ ] Codec decision (from the alktty pattern prior art) — reuse - [x] Codec direction set (from the alktty pattern prior art) — raw
5-byte format vs stripped variant vs raw pass-through + UDP pass-through for stream substrates (0 B tunnel overhead),
framing; feeds OQ-TN-07 (ALPN) and the OQ-TN-05 trait shape `[len:u16 BE]` per datagram for UDP (2 B); no 5-byte header (a
tunnel has one data stream per direction — no sub-demux key
needed). Residual for the Phase 1 ADR: empty-datagram
(`len=0`) semantics. Feeds OQ-TN-07 (option A strengthened:
substrate discriminator in params selects the framing) and the
OQ-TN-05 trait shape
- [ ] Decision input: backend trait vs no-trait (OQ-TN-05), now - [ ] Decision input: backend trait vs no-trait (OQ-TN-05), now
including the hub re-produce composition question including the hub re-produce composition question
- [ ] Targeted POC(s) run + summary (OQ-TN-10) — UDP first, reverse - [ ] Targeted POC(s) run + summary (OQ-TN-10) — UDP first, reverse