docs: OQ-TN-09 direction set; alktty stream-splitting pattern as prior art

- OQ-TN-08 residue updated with the upstream posture: we own alkcall,
  upstream asks happen early (alktty/alkhttp precedent — both required
  upstream fixes/additions; only 3 downstream dependents now, each
  resolved issue makes future dependents cheaper). Ops-listing
  metadata is expected to be a small upstream alkcall change, not a
  workaround.
- OQ-TN-09: control-frame hunch accepted (self-contained frame,
  alktty ADR-006 shape; dial errors are channel-closing, byte EOFs
  stay per-direction); frame vocabulary follows the alktty pattern;
  concrete set = Phase 1 ADR.
- New prior art section: the alktty stream-splitting pattern
  (split BiStream -> ChunkReader/ChunkWriter -> pump to handle halves)
  proposed as the general tunnel pattern — 'tty without the five
  stream types'. Comparison table (tty vs TCP vs UDP codecs/handles/
  pumps); implications for OQ-TN-09 (ctrl frames via stream types),
  OQ-TN-05 (handle = boxed AsyncRead/AsyncWrite halves; UDP flow
  table is the real design surface), OQ-TN-06 (helper convergence
  test against pump_session may be nearly free). Open: codec choice
  (reuse 5-byte / stripped / raw + UDP framing).
- Checklist updated accordingly.
This commit is contained in:
2026-09-06 09:02:12 +00:00
parent 0397194fe6
commit 42790797b5
+78 -7
View File
@@ -184,6 +184,57 @@ documented. Key mechanics, all of which generalize:
protocol likely needs only flow-level idle expiry, not
transport-level keepalive packets — TBD in the spec.
## Prior art: the alktty stream-splitting pattern (`OQ-TN-09`, `OQ-TN-05`)
Discussed 2026-09-05. alktty tunnels io-like streams: the adapter
splits the `BiStream` from `accept_bi` into read/write halves
(`tokio::io::split`), wraps each half in a `ChunkReader`/`ChunkWriter`
(`wire.rs` — the 5-byte `[stream_type: u8][length: u32 be][payload]`
codec), and pumps chunks between the wire halves and the backend's
`TtyHandle` halves. Strip packets on the way in, wrap packets on the
way out; everything above the codec sees plain `AsyncRead`/`AsyncWrite`.
The proposal: that same pattern (or close) may be *the* tunnel pattern.
A tunnel is then literally "tty without the five stream types" — split
the channel's `BiStream`, wrap the halves in a codec, pump to/from
substrate halves. What differs per concern is only the codec and the
far-end handle:
| | alktty | alktunnels (TCP) | alktunnels (UDP) |
|---|---|---|---|
| wire codec | 5-byte, 5 stream types | raw pass-through or minimal framing | length-prefixed datagrams (tun2proxy-style) |
| far end | `TtyHandle` (stdin/stdout/stderr/ctrl) | `TcpStream` split halves | `UdpSocket` + flow table |
| pumps | 3 (stdout/stderr + stdin, exit future coordinates) | 2 (the ADR-078 shape) | 2 + flow expiry |
Implications:
- **OQ-TN-09 falls out naturally.** alktty's `STREAM_CTRL_IN/OUT`
(flag frames inside the same codec) is the precedent for the
establishment/error frame: same codec, one extra stream type
(`TUNNEL_CTRL` or similar), typed control messages — rather than a
separate framing layer. alktty's zero-length-sentinel EOF semantics
carry over unchanged.
- **OQ-TN-05 (backend trait) gets a concrete shape to judge.** If
tunnels are "split + wrap + pump," the far-end handle is just
"(AsyncRead + AsyncWrite) halves" for stream substrates — which is
either a very thin trait or no trait (fn returning boxed halves).
The UDP case's flow table is the real differentiator to design
around, not the stream case.
- **Possible convergence with OQ-TN-06 (two-pump helper).** If the
pump loop shape is identical to alktty's modulo the number of pumps,
a shared helper extraction (this crate's second-consumer moment,
alknet ADR-078) should be evaluated against alktty's `pump_session`
too — the convergence test the ADR asked for may be nearly free.
- **Wasm story intact** — the codec is pure byte manipulation; the
split/pump plumbing is tokio-async, no substrate types in the
protocol layer. Same as alktty.
Open: whether the tunnel codec is *literally* alktty's 5-byte format
with different stream-type constants (reuse), a stripped 5-byte
variant (1 stream type + ctrl), or raw pass-through with framing only
for UDP (the OQ-TN-01 reframe suggests TCP needs nothing beyond the
channels chunk stream). Phase 1 ADR, before the first consumer.
## Open Questions
These are the design questions Phase 0 must resolve (or explicitly defer)
@@ -501,6 +552,15 @@ produced tunnels — or whether `params`-describing metadata rides
alongside the operation spec. That is an alkcall ADR-047 interaction,
not a new mechanism.
**Upstream posture (2026-09-05):** we own the upstream. Both prior
downstream crates (alktty, alkhttp — `/workspace/@alkdev/alkhttp`)
required fixes/additions to alkcall, and the rule is to make those
asks *early*: there are only three downstream dependents right now,
and each issue resolved upstream makes the next dependent cheaper. So
if the ops listing needs per-resource metadata (or anything else in
the ADR-047 interaction), the expectation is a small upstream alkcall
change in Phase 0/1 — not a workaround in this crate.
### OQ-TN-09: Lifecycle, teardown, and error reporting
The two-pump shape gives byte-level teardown for free (EOF sentinels;
@@ -518,11 +578,15 @@ 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:** open — needs a wire-format decision (ADR) if an error frame
is added. Half-answer (hunch): 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.
**Status: direction set 2026-09-05** — 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).
### OQ-TN-10: POC scope for what remains unvalidated
@@ -601,8 +665,15 @@ Candidate reading for the research specialist (to be expanded):
applies as-is; ops listing (ADR-047) is tunnel-resource
discovery; `-D` = "tunnel a socks5 connection" (target selection
in the socks5 protocol, not params). Residue: per-resource
metadata in the ops listing (alkcall ADR-047 interaction,
Phase 1)
metadata in the ops listing — small upstream alkcall ask,
early per the upstream posture (alktty/alkhttp precedent)
- [x] Lifecycle/error direction set (OQ-TN-09) — establishment/error
control frame accepted in principle (dial errors are
channel-closing); frame vocabulary follows the alktty
stream-splitting pattern; concrete set = Phase 1 ADR
- [ ] Codec decision (new, from the alktty pattern prior art) — reuse
5-byte format vs stripped variant vs raw pass-through + UDP
framing; feeds OQ-TN-07 (ALPN) and the OQ-TN-05 trait shape
- [ ] Decision input: backend trait vs no-trait (OQ-TN-05), now
including the hub re-produce composition question
- [ ] Targeted POC(s) run + summary (OQ-TN-10) — UDP first, reverse