docs: phase-0 — UDP phase model vs alktty-style demux; fork-vs-reimplement collapses onto OQ-SK-04
- OQ-SK-03: RFC 1928 control/data never interleave (fast-socks5 wait_on_tcp treats post-reply control bytes as garbage), so the alktty 5-stream demux solves a problem SOCKS5 doesn't have; phase model (pass-through CONNECT, len-prefixed datagrams post-reply) subsumes it; the demux's real advantage is an additive in-band stream-type vocabulary — noted as the honest one-way-door fork - OQ-SK-04: no structural fork needed for the channels surface (router.rs interception shape covers CONNECT; run_udp_proxy_custom + reply_success + the pure UDP codec cover ASSOCIATE); the fork/wrap/reimplement decision is driven entirely by the wasm feature-gating outcome; full fork documented as least attractive
This commit is contained in:
@@ -361,11 +361,43 @@ the datagram stage must ride a channel. Sub-questions:
|
|||||||
sentinel-reply shape is fine and no virtualized relay address is
|
sentinel-reply shape is fine and no virtualized relay address is
|
||||||
ever invented.
|
ever invented.
|
||||||
|
|
||||||
Hunch: wrapper-aware client halves ride the same-channel
|
**Phase model vs alktty-style demux (2026-09-12 discussion).** An
|
||||||
length-prefixed-datagram shape; vanilla clients get UDP ASSOCIATE only
|
alternative shape surfaced: alktty's logical demux (input/output/error/
|
||||||
via the optional local backend. But this is the crate's largest
|
control sub-streams inside one channel, type byte per chunk, up to the
|
||||||
unknown — a POC candidate (OQ-SK-07 #2), decided by an ADR before the
|
u8 = 255 stream-type limit) applied to the SOCKS5 channel — a `data`
|
||||||
first consumer (the datagram framing is wire-stable once published).
|
stream and a `udp-relay` stream type would carry the two phases
|
||||||
|
structurally. Analysis against RFC 1928's actual structure: **RFC 1928
|
||||||
|
is one command per connection, and control and data never interleave in
|
||||||
|
either direction.** CONNECT's control conversation ends at the reply,
|
||||||
|
after which the channel is a pure byte stream (alktunnels' 0 B
|
||||||
|
pass-through conclusion); ASSOCIATE's control stream goes silent after
|
||||||
|
the reply — fast-socks5's own `wait_on_tcp` (`src/server.rs:1195`)
|
||||||
|
treats any post-reply control byte as protocol garbage
|
||||||
|
(`UnexpectedUdpControlGarbage`). So the per-chunk type byte demuxes a
|
||||||
|
problem SOCKS5 doesn't have; a **phase model** (raw RFC conversation
|
||||||
|
until the reply, then a mode switch synchronized by the reply itself —
|
||||||
|
pass-through for CONNECT, `[len: u16 BE]` datagrams for ASSOCIATE)
|
||||||
|
subsumes it with no type byte and no sentinel address: wrapper-aware
|
||||||
|
clients know "post-reply = datagram mode," the same way they'd know
|
||||||
|
"relay stream = datagram mode" under the demux. What the demux genuinely
|
||||||
|
buys that the phase model does not: an **additive in-band vocabulary** —
|
||||||
|
new stream types extend the wire additively (no format break), whereas
|
||||||
|
adding a post-reply phase later is a wire break. alktunnels accepted the
|
||||||
|
opposite ("no in-band control path, ever — the escape hatch is a new
|
||||||
|
ALPN, a wire change is not"); the demux reintroduces that vocabulary at
|
||||||
|
2 B/chunk (type byte + length prefix) on every datagram. The one-way-door
|
||||||
|
cost is real either way: demux bakes the vocabulary in before it has a
|
||||||
|
user; phase model keeps 0 B overhead on CONNECT and 2 B on ASSOCIATE
|
||||||
|
datagrams. Hunch: phase model (simpler, RFC-shaped); the extensibility
|
||||||
|
argument is the honest case for the demux if Phase 1 wants the
|
||||||
|
vocabulary. Decide via ADR with the first consumer in sight.
|
||||||
|
|
||||||
|
Hunch (datagram stage, unchanged): wrapper-aware client halves ride the
|
||||||
|
same-channel length-prefixed-datagram shape; vanilla clients get UDP
|
||||||
|
ASSOCIATE only via the optional local backend. But this is the crate's
|
||||||
|
largest unknown — a POC candidate (OQ-SK-07 #2), decided by an ADR
|
||||||
|
before the first consumer (the datagram framing is wire-stable once
|
||||||
|
published).
|
||||||
|
|
||||||
### OQ-SK-04: fast-socks5 wasm posture (blocks the wasm-clean invariant)
|
### OQ-SK-04: fast-socks5 wasm posture (blocks the wasm-clean invariant)
|
||||||
|
|
||||||
@@ -396,6 +428,30 @@ Option A upstream if confirmed. This OQ gates the wasm verification
|
|||||||
command in AGENTS.md (the expected-failure note is already written
|
command in AGENTS.md (the expected-failure note is already written
|
||||||
there).
|
there).
|
||||||
|
|
||||||
|
**Fork vs reimplement vs wrap — collapses onto this OQ (2026-09-12
|
||||||
|
discussion).** The wrap surface was re-checked and **no structural fork
|
||||||
|
is needed** for the channels path: CONNECT rides the interception points
|
||||||
|
as-is — `read_command` → own dial → `reply_success` → `transfer` (the
|
||||||
|
`examples/router.rs` shape), with the producer's `BiStream` as `T` on
|
||||||
|
both sides of `transfer`. UDP is ours either way: `run_udp_proxy_custom`
|
||||||
|
takes a custom `transfer` closure for the relay half, `reply_success`
|
||||||
|
accepts any `SocketAddr` (the sentinel reply), and
|
||||||
|
`new_udp_header`/`parse_udp_request` are public, pure, and reusable.
|
||||||
|
The one hardwired piece — `run_udp_proxy`'s peer relay socket
|
||||||
|
(`udp_bind_random_port`, socket2-based, called inside the default
|
||||||
|
handler before `run_udp_proxy_custom` even runs) — is in the *default*
|
||||||
|
handler, not the protocol: the wrapper's custom closure never needs it.
|
||||||
|
So the fork-vs-reimplement decision is not about the channels surface
|
||||||
|
at all; it is driven by the wasm question above: **wrap (with the
|
||||||
|
upstream `net` feature-gate ask) if the gating lands cleanly;
|
||||||
|
reimplement if the gating turns out structural.** The reimplementation
|
||||||
|
is genuinely small — RFC 1928 over `tokio::io` generics is the
|
||||||
|
alktunnels-codec scale of code — and fast-socks5 would remain the
|
||||||
|
differential-test oracle for RFC edge cases (reply-code mapping,
|
||||||
|
domain addressing, fragmentation) either way. A full fork (divergent
|
||||||
|
copy we publish) is the least attractive branch: it duplicates the
|
||||||
|
maintenance without buying anything the two options above don't.
|
||||||
|
|
||||||
### OQ-SK-05: Target policy and egress scoping
|
### OQ-SK-05: Target policy and egress scoping
|
||||||
|
|
||||||
SOCKS5 is arbitrary-egress by nature — the open gate plus the target
|
SOCKS5 is arbitrary-egress by nature — the open gate plus the target
|
||||||
@@ -536,11 +592,13 @@ Candidate reading for the research specialist (to be expanded):
|
|||||||
dialer hunch); decide in Phase 1 against the spec
|
dialer hunch); decide in Phase 1 against the spec
|
||||||
- [ ] OQ-SK-02 (auth mapping) — posture drafted (identity seam
|
- [ ] OQ-SK-02 (auth mapping) — posture drafted (identity seam
|
||||||
primary, in-band auth for the local backend); decide in Phase 1
|
primary, in-band auth for the local backend); decide in Phase 1
|
||||||
- [ ] OQ-SK-03 (UDP ASSOCIATE) — the shape space written; resolve via
|
- [ ] OQ-SK-03 (UDP ASSOCIATE) — the shape space written (including
|
||||||
research + POC #2, ADR before the first consumer
|
the phase-model-vs-alktty-demux analysis); resolve via research
|
||||||
|
+ POC #2, ADR before the first consumer
|
||||||
- [ ] OQ-SK-04 (fast-socks5 wasm) — verify empirically (POC #4);
|
- [ ] OQ-SK-04 (fast-socks5 wasm) — verify empirically (POC #4);
|
||||||
likely an upstream feature-gating ask; file early per AGENTS.md
|
the fork-vs-reimplement-vs-wrap decision collapses onto this
|
||||||
convention 17
|
OQ; likely an upstream feature-gating ask; file early per
|
||||||
|
AGENTS.md convention 17
|
||||||
- [ ] OQ-SK-05 (target policy) — folded into the OQ-SK-01 decision;
|
- [ ] OQ-SK-05 (target policy) — folded into the OQ-SK-01 decision;
|
||||||
scope-gate convention pinned in Phase 1
|
scope-gate convention pinned in Phase 1
|
||||||
- [ ] OQ-SK-06 (noq client) — research pass (trait-shape comparison,
|
- [ ] OQ-SK-06 (noq client) — research pass (trait-shape comparison,
|
||||||
|
|||||||
Reference in New Issue
Block a user