docs: Phase 0 converged — scope pinned to socks+channels, recommended approach written

- Scope resolution: the quinn/noq AsyncUdpSocket client wrapper
  (component 3) transfers to the alknet rewrite (its ancestor work —
  ADR-090, quinn-proxy POC — lives there; it consumes no channels and
  has no consumers planned here). OQ-SK-06 and would-be POC #3 go
  with it. alksocks = the producer/consumer pair only.
- Convergence section added (Phase 0's output for the Architect): the
  validated approach pieces (fast-socks5 typestate driven directly for
  both commands, the handle-swap wire shape, DialFn + EgressFactory
  egress seam, "both" auth split by the door, alktty/alktunnels
  layering, vendored-subset fork posture) and the list of Phase 1 ADR
  decisions with their collected inputs (datagram framing, demux vs
  phase, params/ALPN, scope convention, door mapping, wasm posture).
- Checklist: OQ-SK-06 resolved-by-transfer; POC list complete (#1/#2/#4
  passed, #3 dropped); convergence checked off.
This commit is contained in:
2026-09-14 11:07:57 +00:00
parent c77c28c28e
commit 6c71c0e5d2
+112 -7
View File
@@ -53,6 +53,20 @@ Components 1+2 form the producer/consumer protocol pair (channels in,
RFC 1928 inside); component 3 is standalone and composes with any
RFC 1928 server. Keep them structurally separate in the crate layout.
**Scope resolution (2026-09-14, post-POC): component 3 moves to the
alknet rewrite.** The QUIC-runtime client wrapper's natural home is
alknet — it is alknet's own ancestor (ADR-090 + the quinn-proxy POC
live there), it neither produces nor consumes channels, and the
planned alknet rewrite is where downstream clients (iroh privacy
posture) will actually consume it. That pins alksocks' scope to the
producer/consumer pair only: **socks + channels.** Consequence: the
OQ-SK-06 noq research (and its would-be POC #3) transfers to alknet's
Phase 0; the quinn 0.11 variant is already proven there
(quinn-proxy POC). This crate's client-side story reduces to the
channels-native consumer session (`Socks5Stream::use_stream` over a
`BiStream` — POC #1) plus the wrapper-aware associate session (POC
#2) and the optional front doors.
Guiding principles, inherited from the alk* family:
1. **"ALPN as a service," not "a server."** The SOCKS5 service is a
@@ -737,6 +751,17 @@ Questions:
This is a research question first (read noq's endpoint/driver loops;
write the trait-shape comparison), then possibly a POC (OQ-SK-07 #3).
**Resolved by the scope decision (2026-09-14, §Vision): transferred to
the alknet rewrite.** The client wrapper is alknet's ancestor-shaped
component (ADR-090 + quinn-proxy POC already live there), consumes no
channels, and has no consumers planned inside this crate — so its
research questions (noq trait-shape comparison, dependency posture,
ECN/MTU carry-over) move to alknet's Phase 0 wholesale. This crate's
client-side story is complete without it: the channels-native consumer
session is POC-validated (POC #1's `use_stream` over a `BiStream`,
POC #2's associate session). The noq POC (OQ-SK-07 #3) is dropped from
this crate's POC list accordingly.
### OQ-SK-07: POC scope for what remains unvalidated
Candidates, in rough priority order (per the SDD process's "validate
@@ -789,6 +814,10 @@ promising approaches"):
(the `create_sender`/`Box` shape changes) before the client spec is
written. (The quinn variant is already proven by the quinn-proxy
POC — re-validating it here is optional.)
**Dropped from this crate (2026-09-14, the scope decision):** the
client wrapper transfers to the alknet rewrite (§Vision) — OQ-SK-06
and this POC go with it. This crate's POC list is complete
(#1, #2, #4 all passed).
4. **fast-socks5 wasm check** — minimal crate, `cargo check --target
wasm32-unknown-unknown`, confirm/inflect OQ-SK-04. Cheap; can fold
into #1's worktree. **Run 2026-09-13 — fork-minimality verified**
@@ -895,11 +924,87 @@ Candidate reading for the research specialist (to be expanded):
sense for alksocks?") now reduces to the adapter-story input
- [ ] OQ-SK-05 (target policy) — folded into the OQ-SK-01 decision;
scope-gate convention pinned in Phase 1
- [ ] OQ-SK-06 (noq client) — research pass (trait-shape comparison,
dependency posture) + POC #3 if the research is not decisive
- [x] OQ-SK-06 (noq client) — **resolved by the scope decision
(2026-09-14, §Vision): transferred to the alknet rewrite**
(the client wrapper is alknet's ancestor-shaped component,
consumes no channels, has no consumers planned here); its
research questions move to alknet's Phase 0; this crate's
channels-native client story is POC-complete without it
- [x] Targeted POC(s) run + summaries in `docs/research/`
(OQ-SK-07 #1 + #4 complete, `poc-connect-wasm-findings.md`;
#2 UDP ASSOCIATE complete, `poc-udp-associate-findings.md`;
#3 noq remains)
- [ ] Converge: recommended approach written up, ready to hand to the
Architect for Phase 1
(all three complete: #1 + #4 in `poc-connect-wasm-findings.md`,
#2 in `poc-udp-associate-findings.md`; #3 dropped with the
scope decision)
- [x] Converge: recommended approach written up, ready to hand to the
Architect for Phase 1 (§Convergence, 2026-09-14)
## Convergence — the recommended approach
This is Phase 0's output: a clear WHAT/WHY with validated approaches,
ready for the Architect.
### The crate, scoped
**alksocks = the SOCKS5 producer/consumer protocol pair on alkcall
channels (socks + channels, nothing else).** The three-component split
resolved: components 1+2 are this crate; component 3 (the quinn/noq
`AsyncUdpSocket` client wrapper) moves to the alknet rewrite, where
its ancestor work (ADR-090, quinn-proxy POC) already lives. No local
binds except the two explicit, optional, feature-gated assembly shapes
(producer's local backend; consumer's front door).
### The recommended approach (validated pieces → crate shape)
- **Protocol base: fast-socks5's explicit typestate API** — the
`T: AsyncRead + AsyncWrite + Unpin` genericity carries both
substrates (POC #1: a channels `BiStream` drops in directly; the
local backend would feed a `TcpStream`). Drive the typestate
directly for UDP ASSOCIATE (`run_udp_proxy_custom` is unusable —
unconditional kernel bind; file the upstream ask).
- **Wire shape: the handle swap** (POC #2-validated) — one ALPN
(`alk/socks5`), one open op (`channels/socks5/sub`), trivial
params; the RFC conversation is the data plane; CONNECT =
`pump_bidi` pass-through post-reply (POC #1); ASSOCIATE = the
`select!`-driven datagram stage over `[len: u16 BE][addr + data]`
frames (alktunnels' length codec; RSV/FRAG only at front doors;
channel EOF ends the association; per-datagram addressing, no flow
table).
- **Egress seam: the dial-callback shape, extended** — `DialFn` for
CONNECT (POC #1's command-read interception point) +
`EgressFactory` for ASSOCIATE (POC #2), both function-not-trait,
both returning boxed halves. Local-socket and composed
(per-target alktunnels udp-tunnels) providers are assembly-layer
policies; the composed variant's per-destination open cost is
measured (~0.8 ms) and its per-target-ACL-for-free benefit is
validated.
- **Auth: "both," split by the door** (OQ-SK-02's settled posture) —
producer side: the alkcall identity seam only (the in-band
conversation runs NoAuth); the consumer's front door owns RFC 1929
for vanilla clients and maps credentials to the channel identity.
- **Layering: the alktty/alktunnels precedents** — substrate-agnostic
protocol modules at the crate core; feature-gated backend modules
(local binds) never imported by the protocol layer; producer +
consumer + params modules mirroring POC #1/#2's structure; the
fork posture for fast-socks5 is vendoring the `T`-generic subset
into this crate gated behind a `net`-shaped feature (POC #4:
~120 cfg lines, wasm-clean subset functionally complete).
- **Open for Phase 1 ADRs** (inputs collected, decisions pending):
the demux-vs-phase extensibility trade (evidence now favors the
phase model — no in-band control vocabulary was needed across both
POCs); the params-format/ALPN naming ADRs (wire-stable once
published, no consumers exist yet); `SOCKS5_OPEN_SCOPE` convention;
the door's credential→identity mapping shape; the wasm decision
(Case 1 vs Case 2) once the adapter story is confirmed.
### What Phase 0 could NOT answer (handed to Phase 1)
- The datagram-framing ADR (the one-way door) — now has empirical
ground: framing validated, LRU/dedup semantics pinned, open cost
measured. Decide with the first consumer in sight.
- The wasm root question (OQ-SK-04) — mechanism de-risked; the
adapter-story input is a product decision, not a research one.
- Target-policy shape (OQ-SK-05) — the dialer-refuses shape is
evidence-backed; the declarative-allowlist variant stays a Phase 1
choice.
- BIND — RFC-refused in both POCs, out of scope until a use case
exists (the no-bind principle makes it structurally odd here; a
producer-side BIND wants the accept-side shape, not a dial).