docs: iroh-socks5 evaluated — not the base, but OQ-SK-03 prior art
- new docs/research/iroh-socks5-eval.md: full source review of iroh-socks5 0.5.0 (github.com/mattgeddes/iroh-socks5, MIT OR Apache-2.0) — quality verified, structural sibling of the alksocks shape, but iroh-coupled (no generic-T seam) with an RFC subset, so fast-socks5 stays the protocol base (POC #1/#4 conclusions stand) - phase-0: prior-art section for iroh-socks5; OQ-SK-03 updated with its UDP design as the POC #2 baseline (framed {addr, data} in-band datagrams, RFC header codec at front doors only, no producer-side flow table, control-EOF association lifetime, FRAG != 0 refused)
This commit is contained in:
@@ -0,0 +1,261 @@
|
|||||||
|
---
|
||||||
|
status: complete
|
||||||
|
last_updated: 2026-09-13
|
||||||
|
---
|
||||||
|
|
||||||
|
# alksocks research — iroh-socks5 as an alternative base
|
||||||
|
|
||||||
|
Evaluation of `iroh-socks5` 0.5.0 (crates.io, published 2026-08-31 by
|
||||||
|
Matthew Geddes) as a potential base for alksocks, in place of or in
|
||||||
|
addition to `fast-socks5`. Source reviewed in full:
|
||||||
|
`github.com/mattgeddes/iroh-socks5` (the crates.io entry has no
|
||||||
|
repository link, but the repo exists and matches the published source;
|
||||||
|
vcs sha in the `.crate` file: `af31f7fc`). ~1.3k LoC, 9 src files, MIT OR
|
||||||
|
Apache-2.0.
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
**Not a base for the protocol layer; a useful design reference for
|
||||||
|
OQ-SK-03 (UDP) and a confirmation of POC #1's conclusions.** The crate is
|
||||||
|
well-written and the closest structural sibling to alksocks' shape
|
||||||
|
("SOCKS5 sessions riding a single bidirectional stream over an
|
||||||
|
ALPN-multiplexed QUIC connection"), but it is iroh-coupled where alksocks
|
||||||
|
must be substrate-agnostic, implements its own simplified RFC 1928
|
||||||
|
subset, and lacks the typestate/interception-point structure that makes
|
||||||
|
fast-socks5 composable. What it *does* validate: the "one SOCKS5 session
|
||||||
|
= one bidirectional stream, framed request/reply, then pass-through"
|
||||||
|
wire shape (POC #1's conclusion, independently arrived at), and a
|
||||||
|
channels-native UDP ASSOCIATE design that answers several OQ-SK-03
|
||||||
|
sub-questions.
|
||||||
|
|
||||||
|
## What it is
|
||||||
|
|
||||||
|
An iroh `ProtocolHandler` (ALPN `iroh-socks5/1`) with two halves:
|
||||||
|
|
||||||
|
- **`Server`** (the remote side): accepts the ALPN, checks the connecting
|
||||||
|
`EndpointId` against an allow-list (empty = accept all), reads one
|
||||||
|
framed `Request`, dials the target (`TcpStream` / `TcpListener` /
|
||||||
|
`UdpSocket` on the exit host), writes one framed `Reply`, then relays
|
||||||
|
raw bytes (CONNECT/BIND) or framed `Datagram`s (UDP ASSOCIATE) until
|
||||||
|
the stream ends.
|
||||||
|
- **`Client`** (the local side): binds a real TCP listener, speaks the
|
||||||
|
*RFC 1928 wire format* to any vanilla SOCKS5 client that connects
|
||||||
|
(curl/browser/tun2proxy front door), and maps each session onto one
|
||||||
|
iroh `open_bi()` stream carrying the framed `Request`/`Reply` protocol.
|
||||||
|
|
||||||
|
Module map:
|
||||||
|
|
||||||
|
| file | contents | iroh-coupled? |
|
||||||
|
|---|---|---|
|
||||||
|
| `protocol.rs` | frame codec (u32-LE length + postcard), `Request`/`Reply`/`Datagram`/`TargetAddr` | no (pure async I/O) |
|
||||||
|
| `socks5.rs` | RFC 1928 wire codec: greeting (no-auth only), request parse, reply write, UDP header codec | no (pure async I/O) |
|
||||||
|
| `server.rs` | iroh `ProtocolHandler` impl, dial/accept logic | yes |
|
||||||
|
| `client.rs` | local SOCKS5 proxy (TCP listener + RFC codec → iroh stream) | yes |
|
||||||
|
| `relay.rs` | two-pump relays: TCP↔stream (with half-close), UDP select loops | mostly no (`SendStream`/`RecvStream` in signatures) |
|
||||||
|
| `dns.rs` | system / DNS-over-HTTPS resolver choice for the exit side | no |
|
||||||
|
| `key.rs` | secret-key file load/store | iroh types |
|
||||||
|
| `lib.rs` | endpoint builder presets | iroh |
|
||||||
|
| `test_util.rs` | local relay infra + local DoH server for tests | yes |
|
||||||
|
|
||||||
|
## Quality assessment (verified against the source)
|
||||||
|
|
||||||
|
Better than the README's self-deprecation suggests ("LLM-assisted... All
|
||||||
|
bugs are mine"). The wire codecs are clean, bounded, and unit-tested;
|
||||||
|
the integration suite (`tests/integration.rs`, 12 tests under
|
||||||
|
`test-utils`) exercises real iroh connections — direct and via-relay
|
||||||
|
dials, CONNECT (IP + domain), refused-target error mapping, full UDP
|
||||||
|
ASSOCIATE round-trip, BIND, allow-list accept/deny, greeting edge cases.
|
||||||
|
Doc comments are genuinely explanatory. Notable specifics:
|
||||||
|
|
||||||
|
- **Framing**: `read_frame` handles clean EOF vs truncated-frame
|
||||||
|
correctly (tested), bounds frame size at 128 KiB, and uses postcard +
|
||||||
|
serde (not hand-rolled enums) for the inner types.
|
||||||
|
- **UDP header codec** (`socks5.rs` `parse_udp_packet`/
|
||||||
|
`format_udp_packet`): correct ATYP handling, uses `get(..)?` bounds
|
||||||
|
checks, propagates the FRAG byte, domain targets supported — same
|
||||||
|
functional slot as fast-socks5's `new_udp_header`/`parse_udp_request`.
|
||||||
|
- **Half-close propagation** in `relay_tcp`: each direction signals
|
||||||
|
`finish()`/`shutdown()` on completion — a hand-rolled two-pump that
|
||||||
|
*does* shut down the opposite sink (the ADR-050 contract), then joins
|
||||||
|
both halves (`tokio::join!` + propagate both errors).
|
||||||
|
- **UDP association lifecycle**: the client side ends the association
|
||||||
|
when the SOCKS5 control connection closes (`wait_until_closed`
|
||||||
|
drains-and-detects EOF on the control TCP), not merely when the UDP
|
||||||
|
socket goes quiet — the RFC's "TCP control connection closes →
|
||||||
|
association ends" semantic, which many proxies get wrong.
|
||||||
|
- **Reply-code mapping** is faithful where it matters (refused vs
|
||||||
|
unreachable distinguishes `ErrorKind::ConnectionRefused`).
|
||||||
|
|
||||||
|
Weaknesses (acceptable for its scope, relevant if borrowed):
|
||||||
|
|
||||||
|
- **RFC 1929 username/password is absent** — no-auth only
|
||||||
|
(`negotiate` writes `0xFF` if the client doesn't offer no-auth). Auth
|
||||||
|
is the iroh `EndpointId` allow-list at the connection level.
|
||||||
|
- **BIND's second-incoming-connection shape** accepts exactly one
|
||||||
|
inbound connection and has no timeout or multi-accept handling (RFC
|
||||||
|
1928 BIND is notoriously underspecified; this is a reasonable minimal
|
||||||
|
read, but it binds a real `TcpListener` — the exact thing alksocks'
|
||||||
|
no-bind principle forbids in the protocol layer).
|
||||||
|
- **UDP relay is one socket + flow-table-free on the server side**: all
|
||||||
|
datagrams from the association share one `UdpSocket`, replies are
|
||||||
|
attributed by source address implicitly (`udp.recv_from` → echoed
|
||||||
|
back as `TargetAddr::Ip(from)`). Fine for the common case; no
|
||||||
|
per-endpoint flow table like alknet's `Socks5UdpSocket` model.
|
||||||
|
- **`allowlist_denies_unknown_peer`** closes the QUIC connection without
|
||||||
|
an RFC reply — a vanilla client sees a hard close, not a SOCKS5 error
|
||||||
|
code. (In alksocks this maps to the typed `channel:open_failed`
|
||||||
|
establisher refusal, which POC #1 already handles better.)
|
||||||
|
- Client-side UDP binds a real socket per association (necessarily —
|
||||||
|
it's the front door for vanilla clients); server-side UDP likewise
|
||||||
|
binds.
|
||||||
|
|
||||||
|
## Fit against alksocks' requirements
|
||||||
|
|
||||||
|
### What matches alksocks' shape (validating evidence)
|
||||||
|
|
||||||
|
1. **"ALPN as a service" isomorphism.** iroh's `ProtocolHandler::accept`
|
||||||
|
+ `Router::accept(ALPN, handler)` is the same posture as alkcall's
|
||||||
|
`register_openable` (ALPN-scoped produced resource, handler per
|
||||||
|
connection). iroh 1.0's `ProtocolHandler` is even *single-method*
|
||||||
|
(`accept(Connection)`) — structurally the open-handler shape POC #1
|
||||||
|
used. The crate's ALPN string convention (`iroh-socks5/1`) matches
|
||||||
|
the alk/ ALPN convention too.
|
||||||
|
2. **One stream per session, no sub-demux.** The `Request`/`Reply`
|
||||||
|
framing phase followed by raw pass-through (or datagram framing) is
|
||||||
|
exactly the phase model phase-0 §OQ-SK-03 hunches at, and exactly
|
||||||
|
what POC #1 validated for CONNECT. Two independent implementations
|
||||||
|
converging on this shape is prior-art confirmation.
|
||||||
|
3. **Producer/consumer split.** `Server` = producer half (accepts,
|
||||||
|
dials), `Client` = consumer half with the optional local TCP front
|
||||||
|
door (`bind_listener`/`serve` is the ssh `-D` shape POC #1's
|
||||||
|
`local_front_door` example validated). The *only* consumer path is
|
||||||
|
the local bind — there is no in-band (channels-native) client session
|
||||||
|
type, which is where alksocks' wrapper differs.
|
||||||
|
4. **Target resolution on the producing side.** Domains are carried
|
||||||
|
unresolved (`TargetAddr::Domain`) and resolved at the exit node —
|
||||||
|
the ssh `-D` semantic phase-0 §OQ-SK-05 notes, with a pluggable
|
||||||
|
resolver (`DnsResolver`) and a privacy rationale (DoH so the exit
|
||||||
|
host's network can't observe names) that mirrors the iroh-privacy
|
||||||
|
motivation in OQ-SK-06.
|
||||||
|
|
||||||
|
### Where it cannot serve as the base
|
||||||
|
|
||||||
|
1. **iroh types are load-bearing in the data plane.** `server.rs` and
|
||||||
|
`client.rs` are written against `iroh::endpoint::{Connection,
|
||||||
|
SendStream, RecvStream}` concretely; `relay.rs` signatures take
|
||||||
|
`&mut SendStream`. Extracting a substrate-agnostic core would mean
|
||||||
|
keeping only `protocol.rs` + `socks5.rs` (~550 LoC of codecs) — and
|
||||||
|
those are the parts fast-socks5 already provides in stricter, more
|
||||||
|
complete form (typestate, auth traits, reply-code enums, generic `T`).
|
||||||
|
2. **Not generic over the stream.** The whole reason POC #1 could drop a
|
||||||
|
channels `BiStream` into fast-socks5's `Socks5ServerProtocol<T>` with
|
||||||
|
zero adaptation is fast-socks5's `T: AsyncRead + AsyncWrite + Unpin`
|
||||||
|
genericity (AGENTS.md convention 7). iroh-socks5 has no such seam —
|
||||||
|
the equivalent refactor would be a rewrite, not a fork.
|
||||||
|
3. **RFC 1928 subset.** No username/password auth (OQ-SK-02's RFC-facing
|
||||||
|
path), no auth-method negotiation machinery (fast-socks5's
|
||||||
|
`AuthMethod` trait), simplified BIND, no `ReplyError` enum fidelity
|
||||||
|
(reply codes are bare `u8` consts). fast-socks5 is a conformant
|
||||||
|
implementation with a test suite covering the RFC edge cases;
|
||||||
|
iroh-socks5 is a pragmatic subset.
|
||||||
|
4. **tokio "full" + iroh pin** (`iroh = "=1.0.3"`, exact pin) — heavy
|
||||||
|
dependency for the wasm-clean default-crate invariant (OQ-SK-04);
|
||||||
|
the vendored-subset posture would reduce it to the two codec files
|
||||||
|
anyway, at which point fast-socks5's already-POC'd fork (POC #4:
|
||||||
|
~120 cfg lines, functional wasm-clean subset) dominates.
|
||||||
|
5. **No in-band client session type.** The only consumer path is the
|
||||||
|
local TCP front door — there is no client half that speaks the
|
||||||
|
framed protocol from inside a process without a kernel listener
|
||||||
|
(fast-socks5's `Socks5Stream::use_stream` slot). alksocks'
|
||||||
|
consumer half needs exactly that; iroh-socks5 doesn't offer it.
|
||||||
|
|
||||||
|
### The valuable part: OQ-SK-03 evidence
|
||||||
|
|
||||||
|
iroh-socks5 implements channels-native UDP ASSOCIATE without binding a
|
||||||
|
relay-visible port on the producer side beyond the real egress socket —
|
||||||
|
the exact question OQ-SK-03 poses. Its answers:
|
||||||
|
|
||||||
|
- **Datagram stage rides the same stream** (its "second channel" option
|
||||||
|
is not used; the association's stream carries framed `Datagram`s
|
||||||
|
post-reply). Matches the phase-model hunch, including "control stream
|
||||||
|
goes silent after the reply" — nothing else is ever sent on the
|
||||||
|
control stream after the ASSOCIATE reply; the client-side relay
|
||||||
|
monitors the control connection's EOF (`wait_until_closed`) as the
|
||||||
|
association's end-of-life, and the RFC's "post-reply control data is
|
||||||
|
garbage" semantic holds.
|
||||||
|
- **The datagram framing is a length prefix, not the RFC UDP header** —
|
||||||
|
the RFC UDP header (`RSV/FRAG/ATYP/addr/port`) is applied only at the
|
||||||
|
*front door* (the local UDP socket a vanilla client sends to), where
|
||||||
|
it belongs: `relay_udp_client` parses/`format_udp_packet`s at the
|
||||||
|
socket boundary and shuttles clean `(addr, data)` frames inboard.
|
||||||
|
This is a cleaner cut than phase-0's hunch (which put the length
|
||||||
|
prefix inside the same stream as RFC headers): the alkcall-channel
|
||||||
|
wire carries `{addr, data}` postcard frames (boundary-preserving by
|
||||||
|
construction — an empty datagram is a frame with empty `data`, never
|
||||||
|
EOF; alktunnels F-2's requirement is met structurally), and RFC header
|
||||||
|
encode/decode exists only where a vanilla-client socket boundary
|
||||||
|
exists (the optional local backends on either side).
|
||||||
|
- **Server-side `BND.ADDR` semantics**: the ASSOCIATE reply reports the
|
||||||
|
*real* bound UDP socket address (`udp.local_addr()`) — because in
|
||||||
|
this design the server-side relay socket is real. On alksocks'
|
||||||
|
channels path (no socket), phase-0's sentinel question remains open;
|
||||||
|
iroh-socks5 sidesteps it by having the vanilla-facing side be the
|
||||||
|
*client* half. A producer-side vanilla UDP front door (OQ-SK-03's
|
||||||
|
local-backend-bridge option) would need the same real-socket
|
||||||
|
treatment; the wrapper-aware in-band path can carry a sentinel or
|
||||||
|
(better, per this evidence) no address at all — the framed `Datagram`
|
||||||
|
stream needs no `BND.ADDR` to function; the address is pure
|
||||||
|
RFC-compatibility surface.
|
||||||
|
- **No per-endpoint flow table on the producer side** — the relay
|
||||||
|
socket is one `send_to`/`recv_from` loop with `TargetAddr` carried
|
||||||
|
per datagram. This is *simpler* than alknet's flow-table model and
|
||||||
|
sufficient: per-datagram addressing in the frame does the
|
||||||
|
demultiplexing. Notable simplification for POC #2's design.
|
||||||
|
- **UDP fragmentation dropped with a warning** (FRAG ≠ 0 rejected).
|
||||||
|
Reasonable v1 posture; tun2proxy's udpgw does the same.
|
||||||
|
- **Client-side per-session UDP socket bound at the front door with a
|
||||||
|
fallback** (requested addr → 0.0.0.0:port on failure).
|
||||||
|
|
||||||
|
## Comparison table
|
||||||
|
|
||||||
|
| dimension | fast-socks5 (current base) | iroh-socks5 |
|
||||||
|
|---|---|---|
|
||||||
|
| RFC 1928 fidelity | full (typestate, all commands, reply-code enum, RFC 1929) | subset (no-auth only, bare-code BIND) |
|
||||||
|
| Stream genericity | `T: AsyncRead + AsyncWrite + Unpin` throughout | concrete iroh `SendStream`/`RecvStream` |
|
||||||
|
| Server structure | explicit typestate + interception points | single `accept()` method, monolithic |
|
||||||
|
| Auth extension seam | `AuthMethod` trait | none (allow-list only) |
|
||||||
|
| UDP codec | public pure fns (`new_udp_header`, `parse_udp_request`) | public pure fns (`parse_udp_packet`, `format_udp_packet`) — equivalent |
|
||||||
|
| UDP ASSOCIATE over the transport | default handler binds sockets; custom seam narrow (phase-0 caveat) | clean framed-datagram design, no producer-side virtualization needed |
|
||||||
|
| Producer dial seam | interception points (`run_tcp_proxy`, command-read typestate) | none (dial hardwired in `handle_connection`) |
|
||||||
|
| Client session genericity | `Socks5Stream::use_stream` (any `T`) | none (local TCP front door only) |
|
||||||
|
| wasm posture | fork verified minimal (POC #4) | moot — iroh + tokio-full are native-only |
|
||||||
|
| Tests | unit + examples; router/custom-auth examples | strong integration suite over real iroh infra |
|
||||||
|
| License | MIT (we own upstream) | MIT OR Apache-2.0 |
|
||||||
|
| LOC | ~4k | ~1.3k |
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
**Keep fast-socks5 as the protocol base** (the POC #1/#4 conclusions
|
||||||
|
stand); **adopt iroh-socks5's UDP design as prior art for OQ-SK-03**:
|
||||||
|
|
||||||
|
1. The framed `{addr, data}` datagram stream (no RFC header in-band, RFC
|
||||||
|
header only at vanilla front doors) is the cleaner expression of the
|
||||||
|
phase model — the RFC UDP header becomes a front-door codec concern,
|
||||||
|
not a channels-wire concern.
|
||||||
|
2. Per-datagram addressing with no producer-side flow table suffices
|
||||||
|
(revisit alknet's flow-table posture in light of this).
|
||||||
|
3. Control-stream EOF ends the association (`wait_until_closed` shape —
|
||||||
|
on channels, the channel's EOF is the same signal; no extra control
|
||||||
|
vocabulary needed).
|
||||||
|
4. FRAG ≠ 0 refused is an acceptable v1 posture.
|
||||||
|
|
||||||
|
Attribution if code is reused (the two codec files are the candidates —
|
||||||
|
though fast-socks5 already covers them): MIT OR Apache-2.0 dual license,
|
||||||
|
copyright Matthew Geddes; keep LICENSE-MIT/LICENSE-APACHE entries and a
|
||||||
|
provenance note in any vendored file header.
|
||||||
|
|
||||||
|
Also worth filing upstream (courtesy, we own fast-socks5, but
|
||||||
|
iroh-socks5 is upstream's own crate): nothing blocking. Optionally a
|
||||||
|
repository link on the crates.io entry would help discoverability — the
|
||||||
|
repo exists at `github.com/mattgeddes/iroh-socks5` but `repository` is
|
||||||
|
null on crates.io.
|
||||||
@@ -244,6 +244,36 @@ before designing against it. Key surface points, verified:
|
|||||||
channels open handler (inspect params → dial or refuse) and of the
|
channels open handler (inspect params → dial or refuse) and of the
|
||||||
alknet ADR-090 client (hand the established stream to quinn).
|
alknet ADR-090 client (hand the established stream to quinn).
|
||||||
|
|
||||||
|
### iroh-socks5 — the structural sibling (evaluated, not the base)
|
||||||
|
|
||||||
|
`crates.io/crates/iroh-socks5` (v0.5.0, MIT OR Apache-2.0, repo:
|
||||||
|
`github.com/mattgeddes/iroh-socks5` — crates.io lacks the repository
|
||||||
|
link). A ~1.3k-LoC iroh `ProtocolHandler` tunneling SOCKS5 over iroh
|
||||||
|
QUIC: framed `Request`/`Reply` protocol postcard-encoded over one
|
||||||
|
bidirectional stream, then raw pass-through (CONNECT/BIND) or framed
|
||||||
|
`Datagram`s (UDP ASSOCIATE); producer-side dial + allow-list
|
||||||
|
(`EndpointId`s), consumer-side local TCP front door for vanilla clients.
|
||||||
|
Full evaluation: `docs/research/iroh-socks5-eval.md`. Verified:
|
||||||
|
|
||||||
|
- **Independent confirmation of POC #1's shape** — one stream per SOCKS5
|
||||||
|
session, framed control phase then pass-through, RFC codec only at
|
||||||
|
vanilla-client boundaries, resolution producer-side. Convergent
|
||||||
|
evolution from a codebase with no alk* lineage.
|
||||||
|
- **The load-bearing OQ-SK-03 prior art** — its UDP ASSOCIATE design
|
||||||
|
carries framed `{addr, data}` datagrams in-stream (no RFC UDP header
|
||||||
|
on the tunnel wire; the RFC header codec applies only at the local
|
||||||
|
front-door socket), needs no producer-side flow table (per-datagram
|
||||||
|
addressing in the frame does the demux), ends the association on
|
||||||
|
control-stream EOF, and refuses FRAG ≠ 0. This is the phase-model
|
||||||
|
hunch's cleaner expression; adopt as the POC #2 design baseline.
|
||||||
|
- **Why it is not the base:** iroh types are load-bearing in the data
|
||||||
|
plane (`SendStream`/`RecvStream` in relay signatures; no generic `T`
|
||||||
|
seam), the RFC surface is a subset (no-auth only, no auth-method
|
||||||
|
machinery, no reply-code enum fidelity), and the client side has no
|
||||||
|
in-band session type (local front door only). Extracting a
|
||||||
|
substrate-agnostic core yields ~550 LoC of codecs fast-socks5 already
|
||||||
|
provides in stricter form — the POC #1/#4 base decision stands.
|
||||||
|
|
||||||
### alknet's SOCKS5 client + the quinn-proxy POC — the client-side ancestor
|
### alknet's SOCKS5 client + the quinn-proxy POC — the client-side ancestor
|
||||||
|
|
||||||
`/workspace/@alkdev/alknet/crates/alknet-client/src/socks5.rs` (ADR-090)
|
`/workspace/@alkdev/alknet/crates/alknet-client/src/socks5.rs` (ADR-090)
|
||||||
@@ -380,6 +410,12 @@ the datagram stage must ride a channel. Sub-questions:
|
|||||||
clients will literally `sendto()` that address, so this shape only
|
clients will literally `sendto()` that address, so this shape only
|
||||||
works for *wrapper-aware* client halves (the crate's own consumer
|
works for *wrapper-aware* client halves (the crate's own consumer
|
||||||
session, or an assembly layer bridging a real UDP socket).
|
session, or an assembly layer bridging a real UDP socket).
|
||||||
|
**iroh-socks5's refinement (2026-09-13 eval):** the in-band datagram
|
||||||
|
carries `{addr, data}` (boundary-preserving, no RFC header on the
|
||||||
|
tunnel wire) and the RFC UDP-header codec lives only at the
|
||||||
|
vanilla-client front doors — cleaner than putting the length
|
||||||
|
framing *inside* the RFC header stream as first hunched; see
|
||||||
|
`docs/research/iroh-socks5-eval.md`.
|
||||||
- **Second channel for the datagram stage** — the producer
|
- **Second channel for the datagram stage** — the producer
|
||||||
establishes the association on channel 1, then the client opens a
|
establishes the association on channel 1, then the client opens a
|
||||||
second channel that becomes the relay. Keeps CONNECT-shaped
|
second channel that becomes the relay. Keeps CONNECT-shaped
|
||||||
@@ -397,10 +433,12 @@ the datagram stage must ride a channel. Sub-questions:
|
|||||||
are ours to define.
|
are ours to define.
|
||||||
- **Per-datagram addressing** — the SOCKS5 UDP header carries the
|
- **Per-datagram addressing** — the SOCKS5 UDP header carries the
|
||||||
destination per datagram (ATYP + addr + port), so one association
|
destination per datagram (ATYP + addr + port), so one association
|
||||||
multiplexes many endpoints: per-endpoint flow table producer-side
|
multiplexes many endpoints. alknet's model used a producer-side
|
||||||
(the alknet `Socks5UdpSocket` model, and the udpgw prior art
|
per-endpoint flow table; iroh-socks5 demonstrates the simpler cut —
|
||||||
alktunnels documented). Boundary preservation is mandatory (empty
|
per-datagram addressing *in the frame* does the demultiplexing, no
|
||||||
datagram ≠ EOF — alktunnels F-2's lesson transfers directly).
|
flow table. Boundary preservation is mandatory (empty
|
||||||
|
datagram ≠ EOF — alktunnels F-2's lesson transfers directly; the
|
||||||
|
`{addr, data}` frame shape meets it structurally).
|
||||||
- **Do vanilla (wrapper-unaware) clients need channels-native UDP at
|
- **Do vanilla (wrapper-unaware) clients need channels-native UDP at
|
||||||
all?** If the only UDP ASSOCIATE consumers are wrapper-aware, the
|
all?** If the only UDP ASSOCIATE consumers are wrapper-aware, the
|
||||||
sentinel-reply shape is fine and no virtualized relay address is
|
sentinel-reply shape is fine and no virtualized relay address is
|
||||||
@@ -612,6 +650,13 @@ promising approaches"):
|
|||||||
flow table, and empty-datagram handling (alktunnels F-2 transfers).
|
flow table, and empty-datagram handling (alktunnels F-2 transfers).
|
||||||
(Reuses POC #1's producer skeleton; note `run_udp_proxy_custom`
|
(Reuses POC #1's producer skeleton; note `run_udp_proxy_custom`
|
||||||
cannot be used as-is — see the corrected §Prior art caveat.)
|
cannot be used as-is — see the corrected §Prior art caveat.)
|
||||||
|
**Design baseline updated 2026-09-13:** the iroh-socks5 eval
|
||||||
|
(`docs/research/iroh-socks5-eval.md`) suggests the in-band datagram
|
||||||
|
carries `{addr, data}` directly (no RFC UDP header on the tunnel
|
||||||
|
wire; RFC codec at front doors only) and drops the producer-side
|
||||||
|
flow table (per-datagram addressing in the frame); the sentinel
|
||||||
|
question may dissolve for the in-band path (no `BND.ADDR` needed
|
||||||
|
when the datagram stage needs no relay address).
|
||||||
3. **noq `AsyncUdpSocket` impl POC** — the client-side story against
|
3. **noq `AsyncUdpSocket` impl POC** — the client-side story against
|
||||||
noq 1.2: associate through a fast-socks5 server, wrap as
|
noq 1.2: associate through a fast-socks5 server, wrap as
|
||||||
`noq::AsyncUdpSocket`, complete a QUIC handshake. Derisks OQ-SK-06
|
`noq::AsyncUdpSocket`, complete a QUIC handshake. Derisks OQ-SK-06
|
||||||
@@ -644,6 +689,14 @@ Candidate reading for the research specialist (to be expanded):
|
|||||||
(`new_udp_header`/`parse_udp_request`, `ReplyError`),
|
(`new_udp_header`/`parse_udp_request`, `ReplyError`),
|
||||||
`examples/router.rs` (conditional interception), `examples/
|
`examples/router.rs` (conditional interception), `examples/
|
||||||
custom_auth_server.rs`.
|
custom_auth_server.rs`.
|
||||||
|
- iroh-socks5 — `crates.io/crates/iroh-socks5` v0.5.0, repo
|
||||||
|
`github.com/mattgeddes/iroh-socks5` (crates.io has no repository
|
||||||
|
link; the published source matches). Evaluated in
|
||||||
|
`docs/research/iroh-socks5-eval.md`: not the protocol base (iroh
|
||||||
|
coupling, RFC subset), but the OQ-SK-03 UDP prior art (framed
|
||||||
|
`{addr, data}` in-band datagrams, no flow table, control-EOF
|
||||||
|
association lifetime) and an independent confirmation of POC #1's
|
||||||
|
one-stream-per-session shape.
|
||||||
- alktunnels — `docs/research/phase-0-findings.md` (the `-D`
|
- alktunnels — `docs/research/phase-0-findings.md` (the `-D`
|
||||||
composition conclusion, hub-owns-the-connection, the UDP codec
|
composition conclusion, hub-owns-the-connection, the UDP codec
|
||||||
decision + F-2), `docs/architecture/` (params/ALPN/ACL ADR template),
|
decision + F-2), `docs/architecture/` (params/ALPN/ACL ADR template),
|
||||||
@@ -685,7 +738,8 @@ Candidate reading for the research specialist (to be expanded):
|
|||||||
including the three-component split)
|
including the three-component split)
|
||||||
- [ ] Prior-art pass complete: fast-socks5 surface verified (§Prior
|
- [ ] Prior-art pass complete: fast-socks5 surface verified (§Prior
|
||||||
art), alktunnels/alknet/noq lineage mapped, anti-prior-art list
|
art), alktunnels/alknet/noq lineage mapped, anti-prior-art list
|
||||||
written
|
written; iroh-socks5 evaluated (structural sibling, not the base;
|
||||||
|
OQ-SK-03 prior art — `docs/research/iroh-socks5-eval.md`)
|
||||||
- [x] OQ-SK-01 (dial policy) — researched, half-answered (injected
|
- [x] OQ-SK-01 (dial policy) — researched, half-answered (injected
|
||||||
dialer hunch); **POC #1 resolved the shape empirically** (dial
|
dialer hunch); **POC #1 resolved the shape empirically** (dial
|
||||||
callback at the command-read interception point, boxed halves, no
|
callback at the command-read interception point, boxed halves, no
|
||||||
|
|||||||
Reference in New Issue
Block a user