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:
1 parent
aac63d17fc
commit
cb4d58a2bf
2 files changed
+312
-23
No files matched your search
+56
-23
@@ -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.
|
||||
@@ -0,0 +1,256 @@
|
||||
---
|
||||
status: complete
|
||||
last_updated: 2026-09-15
|
||||
---
|
||||
|
||||
# alksocks POC findings — channels-native SOCKS5 BIND
|
||||
|
||||
POC #5 from phase-0 §OQ-SK-07, run 2026-09-15. Crate:
|
||||
`alksocks-bind-poc` (standalone, `/workspace/alksocks-bind-poc/`), against
|
||||
alkcall 0.7.1 (crates.io), alktunnels 0.1.0 (crates.io, `local` feature —
|
||||
the listen surface is complete in the published version),
|
||||
fast-socks5 1.0.0 (crates.io). 9 tests, all pass; `cargo clippy
|
||||
--all-targets -- -D warnings` and `cargo fmt --check` clean.
|
||||
|
||||
Validates OQ-SK-08's design shape: **one channel per BIND session; the
|
||||
phase model extends (request → reply#1 with `BND.ADDR` → quiet wait →
|
||||
exactly one inbound accept → reply#2 with the peer address → raw
|
||||
pass-through data plane); both accept variants behind the accept
|
||||
callback; the `socks5:bind` scope gate.** fast-socks5 parses the
|
||||
command but refuses it (`lib.rs:384-386`, `server.rs:813-816`); the
|
||||
wrapper drives the typestate directly and owns the whole BIND shape —
|
||||
**zero fast-socks5 upstream ask for BIND itself** (the typestate's
|
||||
`read_command()` returns `(cmd, target)` and the wrapper branches;
|
||||
`reply_success`'s `sock_addr` parameter is the reply#1 carrier;
|
||||
`pump_bidi` is the data plane).
|
||||
|
||||
## What was validated
|
||||
|
||||
1. **The BIND phase model — confirmed end to end.** The RFC
|
||||
conversation and the data plane ride the same channel `BiStream`:
|
||||
the typestate runs `accept_no_auth → read_command` (BIND arrives
|
||||
as `Socks5Command::TCPBind`), reply#1 carries `BND.ADDR`, the
|
||||
quiet wait selects the one inbound accept against the control
|
||||
stream, reply#2 carries the peer address, and `pump_bidi`
|
||||
(inline — R-02) two-pumps the accepted connection against the
|
||||
channel. Zero per-packet headers in-band (the CONNECT pass-through
|
||||
conclusion extends literally — BIND's post-reply#2 stage is a
|
||||
plain byte stream).
|
||||
2. **The local accept variant (variant 1, the faithful RFC shape) —
|
||||
confirmed.** A real `TcpListener` per BIND session: reply#1
|
||||
carries the listener's *actual* `local_addr` (a real network-B
|
||||
address); the "app server" dials the advertised address; reply#2
|
||||
carries the *real* peer address (`accept()`'s peer, byte-exact —
|
||||
the test asserts the app's ephemeral port); the data plane echoes
|
||||
both directions. Vanilla front-door clients work (test 6).
|
||||
3. **The composed accept variant (variant 2) — confirmed, with the
|
||||
sequencing finding below.** The listener lives on an alktunnels
|
||||
listen-tunnel resource (`register_tunnel_listen_openable` +
|
||||
`AcceptQueue` + a real far-side `TcpListener`); the SOCKS5
|
||||
producer opens the tunnel *during the quiet wait* and the popped
|
||||
accept's halves become the data-plane peer. The full stack
|
||||
round-trips: SOCKS5 consumer → SOCKS5 producer → composed accept →
|
||||
`alk/tunnel` listen channel → tunnel producer → real listener →
|
||||
the app server's connection → back through the pump. Per-listener
|
||||
ACL for free (the tunnel resource's ACL governs who can mint the
|
||||
listener), hub-relayable — the variant's whole point.
|
||||
4. **The scope gate — confirmed.** The pump handler reads the
|
||||
open-op `AuthContext`'s identity and requires `socks5:bind` before
|
||||
serving a BIND; an identity without it gets the RFC-faithful
|
||||
`ConnectionNotAllowed` (0x02) refusal — while CONNECT still works
|
||||
for the same identity (the gate distinguishes ingress listeners
|
||||
from proxy egress; one identity having egress does not imply
|
||||
ingress). The gate is the open-op identity seam consumed at
|
||||
command-dispatch time — no new ops, no params change, no
|
||||
one-way-door exposure.
|
||||
5. **The quiet wait's semantics — confirmed.** A control byte after
|
||||
reply#1 is protocol garbage (the ASSOCIATE post-reply semantic
|
||||
BIND shares; RFC 1928 §3's "BIND: no further control data"
|
||||
reading); the session is refused with no data plane. A client
|
||||
that BINDs and vanishes (EOF mid-wait) tears the listener down
|
||||
with its session — the accept future (which owns the listener)
|
||||
is dropped by the `select!` branch; a fresh dial to the
|
||||
advertised address fails with connection refused. The session's
|
||||
natural end (half-close post-data-plane) releases the listener
|
||||
the same way.
|
||||
6. **Failure mapping — confirmed RFC-faithful.** A factory failure
|
||||
(pre-reply#1: an unbindable port, a held port) maps to reply#1
|
||||
with the factory error's reply code (shortage → GeneralFailure);
|
||||
an accept-stage failure (post-reply#1) maps to reply#2 with the
|
||||
mapped refusal code and no data plane. No phantom listeners, no
|
||||
phantom data planes; refusals are typed end to end.
|
||||
7. **One registration, all three commands — regression-guarded.**
|
||||
CONNECT (POC #1's data plane, unchanged) and UDP ASSOCIATE (the
|
||||
POC #2 skeleton) still serve on the same `channels/socks5/sub` op
|
||||
after the BIND branch lands. The RFC 1928 command surface is now
|
||||
complete in the crate's shape: CONNECT + BIND + ASSOCIATE.
|
||||
8. **The vanilla front door — confirmed for BIND.** The raw-pipe
|
||||
bridge (POC #1's CONNECT front-door shape, reused verbatim) carries
|
||||
a full vanilla BIND conversation: an in-process RFC 1928 client
|
||||
(greeting → BIND command → reply#1 → reply#2 → data plane) through
|
||||
a `copy_bidirectional` pipe onto the channel. Zero protocol
|
||||
translation at the door; the RFC conversation passes through
|
||||
transparently (0 B pass-through, exercised for the BIND
|
||||
conversation this time).
|
||||
|
||||
## Structural findings (the load-bearing discoveries)
|
||||
|
||||
**The address-first-vs-accept-first deadlock — the POC's one real
|
||||
discovery, and the composed variant's sequencing is now pinned.**
|
||||
SOCKS5 BIND is address-first (reply#1 must carry `BND.ADDR` *before*
|
||||
the app server can dial), while alktunnels' listen establisher is
|
||||
accept-first (its establishment phase pops an already-accepted
|
||||
connection — the open op blocks until one arrives). The first cut
|
||||
opened the listen-tunnel in the factory call (before reply#1) and
|
||||
deadlocked into the establishment timeout: the app server cannot dial
|
||||
until reply#1 tells it the address, and the open cannot complete until
|
||||
it dials. **The resolution: reply#1 first, the tunnel open rides the
|
||||
accept future** — the quiet wait is exactly where the establisher's
|
||||
accept-pop block belongs. What remains upstream-blocked is *learning
|
||||
the bound address without consuming an accept* (ledger ask #1): the
|
||||
stand-in is the factory's carried `bind_addr` (the expected
|
||||
`local_addr` the far-side real listener reports).
|
||||
|
||||
Consequence for the ADR: variant 2 needs TWO upstream asks, not one —
|
||||
(1) listen-addr inspection without consuming an accept (the address
|
||||
half), and (2) accepted handles carrying peer metadata (the reply#2
|
||||
half). Both are additive surface additions, not changes; the ask
|
||||
ledger below records both with the evidence.
|
||||
|
||||
**Establishment-bound interaction (the composed variant's wait
|
||||
bound).** The accept future's tunnel open runs under the listen
|
||||
establisher's default 10s establishment bound — a BIND wait longer
|
||||
than that resolves as a refusal (reply#2, GeneralFailure via the
|
||||
mapped open error). Correct-by-backstop, but a real BIND deployment
|
||||
(FTP control sessions idle far longer than 10s) needs either a raised
|
||||
bound (the open-op's `timeout` parameter — a registration choice, no
|
||||
wire change) or the accept future structured so the bound covers only
|
||||
the open, not the wait (POC note: the bound covered the whole accept
|
||||
here; the crate's shape should bound only the open-op round trip).
|
||||
Phase 1 pins this with the registration posture.
|
||||
|
||||
**The teardown-at-birth lesson repeats — and the fix is structural.**
|
||||
The composed variant's first working cut dropped the
|
||||
`TakenHalves::session` (the channel-entry owner) while destructuring
|
||||
it — the tunnel EOF'd at birth and the data plane read `early eof`
|
||||
(POC #2's findings predicted exactly this: "the session must live
|
||||
inside the table entry; the *compiler* should enforce it"). The fix:
|
||||
`BindTargetHandle` carries `_session: Option<TunnelSession>` — the
|
||||
owner rides the handle and drops with it. This is now the third
|
||||
witness of the pattern (POC #2's egress table, alktunnels ADR-005's
|
||||
teardown contract, here); the crate's Phase 1 shape should make the
|
||||
plan payload own its channel handles structurally, never by let-binding.
|
||||
|
||||
**Peer-address fidelity is lost through the composed path** (ledger
|
||||
ask #2). The popped accept handle (`alktunnels::TargetHandle`) carries
|
||||
boxed halves only — the real peer address exists at the tunnel
|
||||
producer's listener; in-band the composed variant reports the sentinel
|
||||
(`0.0.0.0:0`) for reply#2 (the handle-swap reading applied to BIND:
|
||||
the reply is RFC-compatibility surface; the data plane is the
|
||||
channel). The local variant reports the real peer (no hop). Ledger
|
||||
item #2 is the fidelity ask.
|
||||
|
||||
**The quiet wait is `select!`-single-shot, not a loop** (unlike
|
||||
ASSOCIATE's stage): the accept resolves at most once, and the control
|
||||
stream's first byte ends the session (garbage). A loop was
|
||||
unnecessary — clippy's `never_loop` caught the first cut's vestigial
|
||||
`loop`; the phase model's clean statement is that BIND's quiet wait
|
||||
has exactly two exits (the accept, or the control stream's
|
||||
EOF/garbage/error). The data plane starts only from the accept
|
||||
branch.
|
||||
|
||||
**`BindAccept`'s future-ownership shape is the accept-side twin of
|
||||
the egress's readiness contract.** The accept future owns the
|
||||
listener (local variant) or the tunnel-open + popped halves (composed
|
||||
variant); the quiet wait's `select!` drops it on the control-stream
|
||||
branch — that drop IS the client-gone teardown. The crate's Phase 1
|
||||
equivalent: the accept callback's future must own its resources
|
||||
end-to-end (the same ownership lesson as POC #2's egress, mirrored).
|
||||
|
||||
## Wire/API notes for the wrapper
|
||||
|
||||
- fast-socks5's client `request()` reads exactly one reply — BIND's
|
||||
reply#2 is hand-read with fast-socks5's own public
|
||||
`read_address` codec (reply#1's codec applied again; no second
|
||||
codec hand-rolled). The consumer's reply#2 reader (`read_reply2`)
|
||||
is the Phase 1 client-session sketch.
|
||||
- `Socks5ServerProtocol::reply_success(sock_addr)` is reply#1's
|
||||
carrier — the `sock_addr` parameter carries `BND.ADDR` verbatim
|
||||
(the local variant's real listener; the composed variant's
|
||||
expected far-side address). No upstream change needed.
|
||||
- fast-socks5's `new_reply` helper is private; the BIND reply#2
|
||||
writer hand-rolls the 10-byte reply (VER, REP, RSV, ATYP, addr,
|
||||
port — 10 bytes v4 / 22 v6). Ledger ask #3 (fast-socks5, courtesy):
|
||||
publish `new_reply` (or a `reply(code, addr)` constructor) so
|
||||
embedders don't re-encode replies.
|
||||
- `ToTargetAddr::to_target_addr()` (not `From`) is the
|
||||
`SocketAddr → TargetAddr` conversion — `TargetAddr::from` does not
|
||||
exist (the POC hit this; the wrapper's accept layer will too).
|
||||
- The listen establisher's default 10s bound covers the composed
|
||||
variant's whole accept future in this POC's shape — see the
|
||||
establishment-bound finding above for the Phase 1 posture.
|
||||
- The scope gate is read from the `OpenHandler`'s `AuthContext`
|
||||
(CF-005/CF-006's per-call opener identity) — the same seam POC #1
|
||||
validated for session-gating, now consumed for a *command-class*
|
||||
distinction. `socks5:bind` as a scope string follows the
|
||||
`socks5:open` convention (Phase 1 pins the exact convention).
|
||||
|
||||
## Upstream-ask ledger (the one-version alktunnels bump)
|
||||
|
||||
The user's standing note: collect alktunnels additions so one version
|
||||
bump ships them all. The POC ran against published alktunnels 0.1.0
|
||||
(no fork needed — the listen surface is complete there), and the asks
|
||||
are additive surface, discovered in priority order:
|
||||
|
||||
1. **Listen-addr inspection without consuming an accept** (the
|
||||
address half of address-first-vs-accept-first). The plan carries
|
||||
`local_addr` + an inspect-only open, or a `listen/addr` query op
|
||||
(OQ-SK-08 variant 2's original blocker, now POC-evidenced: the
|
||||
factory's carried `bind_addr` stand-in is the exact shape the ask
|
||||
retires). Evidence: the composed accept's reply#1 advertises an
|
||||
*expected* address; without the ask, a producer whose listener
|
||||
binds an OS-chosen port cannot advertise it before the accept.
|
||||
2. **Accepted handles carrying peer metadata** (the reply#2 half).
|
||||
`AcceptFn`'s `TargetHandle` carries boxed halves only; the real
|
||||
peer address exists at the tunnel producer's listener. Evidence:
|
||||
the composed accept's reply#2 reports the sentinel; the RFC's
|
||||
"reply#2 carries the address of the incoming connection" is
|
||||
unfilled on the composed path. Shape: `TargetHandle` gaining an
|
||||
optional `peer: Option<SocketAddr>` (or a sibling
|
||||
`AcceptedHandle`) — additive, no wire change.
|
||||
3. **(fast-socks5, courtesy)** `new_reply` is private — publish it
|
||||
(or a constructor) so embedders don't re-encode the reply wire
|
||||
form. Also still open from POC #4: the `net` feature gate and the
|
||||
`run_udp_proxy_custom` unconditional-bind seam.
|
||||
|
||||
No asks block the POC's conclusions; 1–2 shape variant 2's *full*
|
||||
fidelity, and the POC's stand-ins (carried `bind_addr`; sentinel
|
||||
reply#2) are honest interim shapes.
|
||||
|
||||
## OQ status after this POC
|
||||
|
||||
- **OQ-SK-08 (BIND)**: POC-validated end to end — the phase model
|
||||
extends (reply#1 → quiet wait → accept → reply#2 → pass-through);
|
||||
both accept variants implemented (local faithful + composed
|
||||
listen-tunnel); the scope gate (`socks5:bind`) distinguishes
|
||||
ingress from egress; the address-first-vs-accept-first question is
|
||||
*resolved in shape* (reply#1 first, the open rides the accept
|
||||
future) with the two upstream asks pinned as the remaining blockers
|
||||
for variant 2's full fidelity. Remaining for Phase 1: ship-in-base-
|
||||
crate + default-accept-variant ADR (the support is additive — old
|
||||
consumers still get the RFC refusal — so nothing here is a
|
||||
one-way door); the establishment-bound posture; the exact
|
||||
scope-string convention.
|
||||
- **OQ-SK-07**: #1, #2, #4, #5 complete; #3 dropped (transferred with
|
||||
OQ-SK-06). The register is closed for this crate's Phase 0.
|
||||
- **OQ-SK-01 (dial policy)**: the accept seam is the dial-callback
|
||||
shape's mirror (`BindAcceptFactory` — function-not-trait, boxed
|
||||
halves, one instance per session). The no-trait precedent now holds
|
||||
for the accept plane too.
|
||||
|
||||
## POC placement
|
||||
|
||||
Standalone crate (global workspace, `/workspace/alksocks-bind-poc/`)
|
||||
per the placement conventions — it composes three published crates
|
||||
(alkcall 0.7.1, alktunnels 0.1.0, fast-socks5 1.0.0) and needs no code
|
||||
from this repo. Findings land here; the crate is the artifact.
|
||||
Reference in new issue
Block a user