docs: POC #5 (channels-native SOCKS5 BIND) findings

POC #5 run 2026-09-15 (alksocks-bind-poc, standalone, 9 tests) —
OQ-SK-08 POC-validated; the POC register (OQ-SK-07) is closed.

- The BIND phase model extends end to end on one channel: request →
  reply#1 (BND.ADDR) → quiet wait → one inbound accept → reply#2
  (peer) → pump_bidi pass-through (0 B overhead, like CONNECT)
- Both accept variants implemented: local (real TcpListener, faithful
  RFC) + composed (alktunnels listen-tunnel — per-listener ACL for
  free, hub-relayable)
- The address-first-vs-accept-first deadlock resolved in shape:
  reply#1 first, the tunnel open rides the accept future
- The socks5:bind scope gate distinguishes ingress from egress
  (RFC-faithful ConnectionNotAllowed refusal; CONNECT unaffected)
- Vanilla front door re-validated for the BIND conversation (raw pipe,
  zero translation); CONNECT + ASSOCIATE regression-guarded on the
  same registration — the RFC 1928 command surface is complete
- fast-socks5: zero BIND ask (typestate-driven; reply_success carries
  reply#1); courtesy ask: publish the private new_reply encoder
- alktunnels ask ledger pinned at two items for the one-version bump:
  (1) listen-addr inspection without consuming an accept, (2) accepted
  handles carrying peer metadata — both POC-evidenced, both additive

Verification: cargo test (9/9), clippy --all-targets -D warnings,
fmt --check clean in alksocks-bind-poc.
This commit is contained in:
glm-5.3-flash committed 2026-09-15 06:09:35 +00:00
1 parent aac63d17fc
commit cb4d58a2bf
2 files changed
+312 -23

No files matched your search

+56 -23
View File
@@ -1,7 +1,7 @@
---
status: draft
last_updated: 2026-09-15 (consistency pass: POC register in OQ-SK-07,
client-wrapper transfer residue cleaned, convergence checklist fixed)
last_updated: 2026-09-15 (POC #5 BIND passed — OQ-SK-08 POC-validated,
POC register closed, alktunnels ask ledger pinned at two items)
---
# alksocks — Phase 0 (Exploration)
@@ -822,7 +822,7 @@ one-place accounting of all numbered POCs:
| 2 | UDP ASSOCIATE (handle-swap shape) | **Passed 2026-09-14** | `poc-udp-associate-findings.md` |
| 3 | noq `AsyncUdpSocket` impl | **Dropped 2026-09-14** — transferred to alknet with OQ-SK-06 | — |
| 4 | fast-socks5 wasm fork-minimality | **Passed 2026-09-13** — folded into #1's worktree, shares #1's findings file | `poc-connect-wasm-findings.md` §POC #4 |
| 5 | BIND (accept-side command) | **Added 2026-09-14 with OQ-SK-08 — pending** | (OQ-SK-08) |
| 5 | BIND (accept-side command) | **Passed 2026-09-15** | `poc-bind-findings.md` |
Numbering is historical (do not renumber — findings files and commit
messages reference these numbers). Detailed descriptions follow.
@@ -909,7 +909,13 @@ Candidates, originally in rough priority order (the order is historical
reply#1). Validates: the accept-side data plane (the mirror of the
dial callback), reply#1/#2 sequencing on one channel, and the
alktunnels listen-addr ask's shape. Completes the RFC 1928 command
surface (all three commands) in the crate.
surface (all three commands) in the crate. **Run 2026-09-15 —
passed** (`alksocks-bind-poc`, 9 tests; findings in
`poc-bind-findings.md`: the phase model extends end to end, both
accept variants implemented, the scope gate works, the
address-first-vs-accept-first deadlock is resolved in shape
(reply#1 first, the tunnel open rides the accept future), and the
alktunnels ask ledger is pinned at two items).
POC placement conventions (inherited from alktunnels): a POC that needs
code from this repo runs in a worktree/branch (`.worktrees/research/
@@ -920,9 +926,12 @@ regardless of where the code lives.
### OQ-SK-08: BIND — the accept-side command (use case found)
**Status: new (2026-09-14, post-POC #2).** Both POCs refused BIND as
RFC-correct; the findings review surfaced a real use case and a
mechanism, so "out of scope until a use case exists" has been met.
**Status: POC-validated (2026-09-15, POC #5 —
`poc-bind-findings.md`).** The shape space below is written as it
stood pre-POC; the POC resolved the design question (variants 1 + 2
implemented; the sequencing pinned) and pinned the ask ledger. The
remaining decision is Phase 1's ADR (ship in base crate? default
accept variant?).
**The gap, precisely:** fast-socks5 parses `TCPBind`
(`Socks5Command::TCPBind`, `lib.rs:91`) but every handler refuses it
@@ -931,7 +940,9 @@ bind machinery exists. The typestate flow (`read_command()` returning
`cmd` + `TargetAddr`) means the wrapper drives the command dispatch
itself — implementing BIND is wrapper-side code, **zero upstream
ask** (unlike UDP ASSOCIATE's `run_udp_proxy_custom` bind, which sits
inside an upstream handler).
inside an upstream handler). **POC-confirmed:** `reply_success`'s
`sock_addr` parameter carries reply#1's `BND.ADDR` verbatim; the only
courtesy ask is publishing `new_reply` (the reply encoder is private).
**The shape (convergent precedent: iroh-socks5's channels-native
BIND, `docs/research/iroh-socks5-eval.md` §BIND):** one channel per
@@ -984,6 +995,19 @@ sketched behind the same accept callback — the composed variant's
value (hub story, per-listener ACL) is the reason to measure it, and
its blocker is the address-inspection ask.
**POC #5's resolution (2026-09-15 — `poc-bind-findings.md`):** both
variants implemented and passing. The hunch was right, and variant 2's
sequencing is now pinned: **reply#1 first, the tunnel open rides the
accept future** (the first cut opened the listen-tunnel before
reply#1 and deadlocked into the establishment timeout — the app
server cannot dial until reply#1 tells it the address; the quiet wait
is exactly where the accept-pop block belongs). The ask ledger grew
to two items (address inspection; peer metadata on accepted handles —
reply#2's fidelity half), both POC-evidenced, both additive. The
scope gate (`socks5:bind`, checked at command dispatch from the
per-call opener identity) distinguishes ingress from egress and is
POC-validated.
**Security boundary note:** BIND is an *inbound* listener — the
accept-side twin of arbitrary egress. Whatever ACL governs the socks5
resource now also governs who can mint listeners on the producer's
@@ -1144,8 +1168,11 @@ Candidate reading for the research specialist (to be expanded):
virtual-subnet proposal (dial-callback translation, no wire
change) recorded for Phase 1
- [ ] OQ-SK-08 (BIND) — new (2026-09-14): the gap + use case +
accept-side mechanism written; resolve via POC #5 + the Phase 1
ADR (ship in base crate? default accept variant?)
accept-side mechanism written; **POC #5 run 2026-09-15 —
passed** (the phase model extends end to end; both accept
variants implemented; the scope gate works; the ask ledger is
pinned); the Phase 1 ADR (ship in base crate? default accept
variant?) has empirical ground under it
- [ ] OQ-SK-09 (channel capacity) — new (2026-09-14): the proxy-
workload analysis written (wire change rejected; config is the
mechanism; in-channel port multiplexing considered and
@@ -1160,10 +1187,10 @@ Candidate reading for the research specialist (to be expanded):
- [ ] Targeted POC(s) run + summaries in `docs/research/`
(the convergence-blocking POCs are complete — see the OQ-SK-07
register: #1 + #4 in `poc-connect-wasm-findings.md`, #2 in
`poc-udp-associate-findings.md`; #3 dropped with the scope
decision. Left unchecked because **#5 (BIND), added 2026-09-14
with OQ-SK-08, is still pending** — it gates Phase 1's BIND ADR,
not Phase 0's convergence.)
`poc-udp-associate-findings.md`, #5 (BIND) in
`poc-bind-findings.md`; #3 dropped with the scope decision.
The register is closed — no pending POCs remain for this
crate's Phase 0.)
- [x] Converge: recommended approach written up, ready to hand to the
Architect for Phase 1 (§Convergence, 2026-09-14)
@@ -1229,11 +1256,15 @@ binds except the two explicit, optional, feature-gated assembly shapes
the proxy-workload channel-cap recommendation (OQ-SK-09); the
virtual-subnet translation-layer shape (OQ-SK-05 addendum).
- **Upstream asks (we own both upstreams; file early, land there):**
fast-socks5 — the `net` feature gate (POC #4's ~120-line change)
and the `run_udp_proxy_custom` unconditional-bind seam. alktunnels —
listen-addr inspection without consuming an accept (OQ-SK-08
variant 2's blocker). First-real-consumer asks, the alkcall
E-01/E-02 precedent.
fast-socks5 — the `net` feature gate (POC #4's ~120-line change),
the `run_udp_proxy_custom` unconditional-bind seam, and the
courtesy `new_reply` publication (the reply encoder is private;
POC #5 re-encoded it by hand). alktunnels — the one-version bump
ledger (POC #5's evidence, `poc-bind-findings.md` §Upstream-ask
ledger): (1) listen-addr inspection without consuming an accept
(OQ-SK-08 variant 2's address half), (2) accepted handles carrying
peer metadata (reply#2's fidelity half). First-real-consumer asks,
the alkcall E-01/E-02 precedent.
### What Phase 0 could NOT answer (handed to Phase 1)
@@ -1251,10 +1282,12 @@ binds except the two explicit, optional, feature-gated assembly shapes
recommendation stay Phase 1 decisions. In-channel port
multiplexing considered and rejected there.
- BIND — RFC-refused in both POCs; **a use case and a mechanism were
found in the 2026-09-14 findings review** — OQ-SK-08 records the
gap, the accept-side-resource shape (mirroring alktunnels' `-R`
far-side listener), the address-first-vs-accept-first design
question, and POC #5. Deciding whether BIND ships in the base
found in the 2026-09-14 findings review, and POC #5 (2026-09-15)
validated the shape end to end** — OQ-SK-08 records the gap, the
accept-side-resource shape (mirroring alktunnels' `-R` far-side
listener), the address-first-vs-accept-first resolution (reply#1
first, the tunnel open rides the accept future), the two-item ask
ledger, and the scope gate. Deciding whether BIND ships in the base
crate (and which accept variant is default) is a Phase 1 ADR —
support is additive (old consumers still get the RFC refusal), so
nothing here is a one-way door.