From cb553ea033115636931b35bc9c3f80eedf74e202 Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Sun, 20 Sep 2026 09:49:08 +0000 Subject: [PATCH] docs: record the upstream graduation landing (alkcall 0.8.0, alktunnels 0.2.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ask ledger's two alktunnels items landed 2026-09-18/20 — superseded in shape, not implemented as posed. The graduation (alktunnels tunnels-graduation.md -> ADRs 007/008) adopted the ssh forwarded-tcpip-shaped alternative over the per-ask carriers. phase-0.md: - settled-foundations: establishment gains the reply-projection note (ADR-049 amendment 3); the relay/hub note records the in-tree ChannelRelay/HubLegTemplate (ADR-051); identity-seam note marked current at 0.8.0 - OQ-SK-01: Option A updated — the direct op (channels/tunnel/direct, ADR-007) is the dynamic-target channel; POC #5's scope-gate + ADR-007 rationale point the same way (separation lives at the ACL) - OQ-SK-03: composed egress's vehicle is open_direct + Substrate::Udp; the ~0.8 ms measurement carries over; the stale POC-candidate hunch paragraph updated - OQ-SK-08: the landed note — ask #1 dissolved (bind-first establisher; the real bound rides the open reply; POC #5's open-rides-the-accept workaround + the 10s-bound finding retire), ask #2 landed at the wire's native moment (forwarded op's {listen, substrate, peer} params), correlation token per ADR-008 Amendment 1 - convergence: egress seam composes against 0.2.0; the upstream-asks bullet records the landing per crate; the BIND hand-off bullet notes variant 2 is now fully vanilla-fidelity - checklist OQ-SK-08 item + POC #5 register entry point at the landed note poc-bind-findings.md: ledger statuses appended (superseded in shape; evidence stands as the alktunnels-0.1.0 record) AGENTS.md: alkcall pin 0.7.1 -> 0.8.0 (reply projection noted); architecture context: alktunnels v0.2.0 entry added, alkcall 0.8.0 surface listed Verified against the repos: alkcall 0.8.0 changelog (U-1/U-2/ADR-051), alktunnels 0.2.0 changelog + ADR-007/008 + exports (bindfirst_establisher producer.rs:641, register_tunnel_bindfirst_ openable :924), fast-socks5 still 1.0.0 with no net feature (the courtesy tail). Docs-only change; no build surface affected. --- AGENTS.md | 30 ++-- docs/research/phase-0.md | 211 +++++++++++++++++++++++------ docs/research/poc-bind-findings.md | 40 +++++- 3 files changed, 230 insertions(+), 51 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c7ba39f..6fdf615 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -177,20 +177,21 @@ implementation agents. `Identity`, `IdentityProvider`, `AccessControl`, `OwnershipProvider`, `HandlerError`, `StreamError` come from `alkcall::core`. Do not vendor copies into this crate. alkcall is - v0.7.x — breaking changes are expected at this major-zero stage; + v0.8.x — breaking changes are expected at this major-zero stage; this is an early consumer, so we find and fix issues upstream - rather than working around them. Pin `alkcall = "0.7.1"` and bump + rather than working around them. Pin `alkcall = "0.8.0"` and bump deliberately. The establishment surface (alkcall ADR-049 + - amendment 2) is load-bearing for this crate: SOCKS5 session opens + amendments) is load-bearing for this crate: SOCKS5 session opens use `register_openable_with_establisher` so a refused/auth-failed session is a typed `channel:open_failed` call error, never a phantom channel; the establisher returns the negotiated handle via `Establishment::new(plan)` (typed-opaque `ChannelPlan` — payloads - must be `Send + Sync`). The 0.7.0 identity surface (ledger - CF-005/CF-006) is load-bearing for auth mapping: the per-call - opener identity arrives on the open-op hooks — map it to SOCKS5 - credentials/ACL there, not via any in-band SOCKS auth unless - clients need RFC-facing username/password. + must be `Send + Sync`; 0.8.0 adds the optional reply projection — + `with_reply_field(s)`, ADR-049 amendment 3). The 0.7.0 identity + surface (ledger CF-005/CF-006) is load-bearing for auth mapping: + the per-call opener identity arrives on the open-op hooks — map it + to SOCKS5 credentials/ACL there, not via any in-band SOCKS auth + unless clients need RFC-facing username/password. 12. **Access control** — the producer's openable channels get authorization for free via `ChannelCore::register_openable`, which @@ -288,11 +289,20 @@ is expected and is exactly what the OQ tracks. producer/consumer protocol crate on alkcall channels; the backend inversion point, wasm-clean default crate, and feature-gated local backend precedents. - - **alkcall** — `/workspace/@alkdev/alkcall` (v0.7.1, crates.io). The + - **alkcall** — `/workspace/@alkdev/alkcall` (v0.8.0, crates.io). The substrate: call protocol + channels multiplexing. This crate consumes `alkcall::core` types and the channels `ChannelCore`/`ChannelClient`/`register_openable_with_establisher` - surface, same as alktty/alktunnels. + surface, same as alktty/alktunnels. 0.8.0 adds the establisher + reply projection (`with_reply_field`, ADR-049 amendment 3), + flavor-form open-op ids in discovery (ADR-047 amendment 3), and + the in-tree `ChannelRelay`/`HubLegTemplate` (ADR-051) — the hub + re-produce machinery the SOCKS5 hub story composes on. + - **alktunnels** — v0.2.0 (ADRs 007/008): the direct op + (`channels/tunnel/direct` — dynamic-target egress; OQ-SK-01 + Option A's vehicle), the bind-first establisher + forwarded op + (the composed BIND substrate; OQ-SK-08 variant 2), and + `TunnelListener`. - **fast-socks5** — `/workspace/fast-socks5` (v1.0.0, MIT, we own upstream). The SOCKS5 implementation to wrap: explicit typestate server API (`Socks5ServerProtocol`), generic over diff --git a/docs/research/phase-0.md b/docs/research/phase-0.md index 8e39ddd..514c13a 100644 --- a/docs/research/phase-0.md +++ b/docs/research/phase-0.md @@ -1,7 +1,9 @@ --- status: draft -last_updated: 2026-09-15 (POC #5 BIND passed — OQ-SK-08 POC-validated, -POC register closed, alktunnels ask ledger pinned at two items) +last_updated: 2026-09-20 (the graduation landed upstream: alkcall 0.8.0 +— reply projection, flavor-form discovery, ADR-051 relay; alktunnels +0.2.0 — ADR-007 direct op + ADR-008 bind-first/forwarded, superseding +the BIND ask ledger's shape; ask-ledger statuses updated) --- # alksocks — Phase 0 (Exploration) @@ -134,28 +136,43 @@ not start from zero. It inherits: two-pump shape; `alkcall::channels::pump_bidi` (ADR-050) is pinned upstream and alktunnels-validated. Use it; do not hand-roll. - **The establishment story** — session opens use - `register_openable_with_establisher` (alkcall ADR-049 + amendment 2): + `register_openable_with_establisher` (alkcall ADR-049 + amendments): the establisher runs as an awaited, bounded establishment phase; a refused session is a typed `channel:open_failed` call error (`reason ∈ dial_failed / unknown_resource / resource_shortage / handler_error / timeout`), never a phantom channel. The negotiate/ auth half of the RFC conversation still runs in-stream over the `BiStream` — establishment covers only the producer's accept policy, - not the RFC handshake. + not the RFC handshake. **Updated 2026-09-18 (alkcall 0.8.0, ADR-049 + amendment 3): the establisher can project reply fields** — + `Establishment::with_reply_field(s)` merged into the open reply, + consumable via `ChannelClient::open_channel_with_reply`. This is the + carrier alkcall review 008 built for alktunnels' bind-first `bound` + address; a SOCKS5 establisher that ever needs to return open-time + metadata has the same seam (additive; absent fields leave the reply + byte-identical). - **The producer/consumer model** — producer registers openable channels (authorization for free via `AccessControl`); consumer opens them via `ChannelClient` (alkcall ADR-037, ADR-043). Connection direction is independent of service direction. - **The relay/hub story** — a SOCKS5 resource traverses alkcall hub relays like any produced resource; the hub's terminate-and-re-produce - proxy (alktunnels' refinement of alkcall ADR-042) applies per hop, and - a SOCKS5 resource is exactly the "further downstream" shape alktunnels - described. No socks5-specific relay work. -- **The identity seam** — alkcall 0.7.0's CF-005/CF-006: the per-call + proxy applies per hop, and a SOCKS5 resource is exactly the "further + downstream" shape alktunnels described. No socks5-specific relay + work. **Updated 2026-09-18: the relay machinery is in-tree** — + alkcall 0.8.0 ships `ChannelRelay` + `HubLegTemplate` (ADR-051, the + review-008 bonus), so a hub leg is an install-template composition, + not assembly-layer hand-rolling; relayed replies carry extra fields + through per-hop-truthfully (the bind-first `bound` flows + end-to-end), and flavor-form ops (the direct op's + `channels/tunnel/direct` shape) re-produce as channels, not + forwarding stubs. +- **The identity seam** — alkcall's CF-005/CF-006 (0.7.0): the per-call opener identity arrives on the open-op hooks. Auth mapping (identity → SOCKS5 credentials/ACL, if any) happens there, not via in-band SOCKS auth, unless RFC-facing username/password is needed for vanilla SOCKS5 - clients (OQ-SK-02). + clients (OQ-SK-02). Still current at alkcall 0.8.0 — the 0.8.0 + additions (reply projection, relay) are additive and do not touch it. - **The backend inversion point pattern** — substrate-specific types confined to feature-gated backend modules, injected at the assembly layer (alktty `TtyBackend` / alktunnels no-trait precedent). @@ -376,6 +393,16 @@ target dial must happen *somewhere*. Options: channel naming the target, and the two-pump data plane is tunnel-channel ↔ SOCKS5-`BiStream`. Maximum composition ("further downstream" made literal), but adds a runtime dependency and a hop. + **Updated 2026-09-16 (alktunnels 0.2.0, ADR-007): the dynamic-target + channel exists** — the direct op (`channels/tunnel/direct`, + `{substrate, target}` params, the ssh `direct-tcpip` shape, + `tunnel:direct` a separate ACL grant). Option A no longer needs a + pre-registered per-target resource: the SOCKS5 handler's dial + callback opens one direct-op channel per CONNECT target (per the + graduation research, this is exactly what the direct op was + graduated *for*). The udp substrate rides the same op — which also + simplifies OQ-SK-03's composed-egress vehicle below (one op, both + substrates). - **Option B: dial directly** — the producer dials the target itself (`TcpStream` behind the `local` feature, or an injected dial callback). No alktunnels dependency; the dial policy (allowlists, @@ -395,7 +422,14 @@ whatever is decided must not *prevent* A when composing. Hunch: a dial callback/trait at the protocol layer, with alktunnels and local-TCP providers feature-gated — but alktunnels' no-trait precedent warns against a trait unless two real implementations converge; decide with -the Phase 1 spec. +the Phase 1 spec. POC #1 resolved the shape empirically in this +crate's layer (dial callback at the command-read interception point, +boxed halves, no trait); the A/B choice above it is now an +assembly-layer policy question — POC #5's scope-gate finding +(`socks5:bind` ≠ `socks5:open`) plus ADR-007's own rationale +("dial `postgres-primary`" vs "dial anything" are different grants) +both point the same way: separation lives at the ACL, not in the +protocol. ### OQ-SK-02: Auth mapping — identity seam vs in-band SOCKS auth @@ -567,6 +601,19 @@ what gets each datagram to `addr`? governing the udp-tunnel resource governs egress per destination (dissolving most of OQ-SK-05 for the composed path), and hub relaying is per-target terminate-and-re-produce. + **Updated 2026-09-16 (alktunnels 0.2.0, ADR-007): the composed + form's vehicle is now the direct op** — `TunnelSession::open_direct` + with `Substrate::Udp` dials a real UDP target through the same + dynamic-target op (`channels/tunnel/direct`), pinned by alktunnels' + own `open_direct` + `Substrate::Udp` test (a real UDP echo through + the establisher path). The lazy + destination→tunnel table's shape is unchanged — each entry is a + direct-op channel instead of a named-resource channel; the + per-destination ACL question shifts to the `tunnel:direct` grant + + the dialer's target policy (the ACL governs the capability, the + dial callback governs per-destination checks). POC #2's ~0.8 ms + per-destination open-cost measurement carries over (same op + mechanics; the direct op adds no round trip). Both variants are dial-callback policy (OQ-SK-01's shape) — nothing in the fork or the protocol layer sees the difference. **No upstream ask @@ -609,17 +656,18 @@ 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, updated 2026-09-13 by the handle swap): +Hunch (datagram stage, updated 2026-09-13 by the handle swap; +POC #2-validated 2026-09-14; composed-egress vehicle updated +2026-09-16 — see variant 2 above): wrapper-aware client halves ride the same-channel `{addr, data}` frame shape (one channel per association, per-datagram addressing in-band, channel EOF = association lifetime); vanilla clients get UDP ASSOCIATE only via the optional local front-door bridge. The remaining choice is -producer-egress policy (local socket vs per-target alktunnels +producer-egress policy (local socket vs per-target direct-op composition) — a dial-callback question owned by OQ-SK-01, with the composed variant's ACL benefit making it the hunch for the base crate's -composed story. But this is still 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). +composed story. The datagram-framing ADR (the one-way door) is Phase +1's, with the first consumer in sight. ### OQ-SK-04: fast-socks5 wasm posture (blocks the wasm-clean invariant) @@ -915,7 +963,9 @@ Candidates, originally in rough priority order (the order is historical 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). + alktunnels ask ledger is pinned at two items. **Ledger statuses + updated 2026-09-20** — see OQ-SK-08's landed note below: both + items superseded in shape by the bind-first/forwarded graduation.) POC placement conventions (inherited from alktunnels): a POC that needs code from this repo runs in a worktree/branch (`.worktrees/research/ @@ -927,11 +977,12 @@ regardless of where the code lives. ### OQ-SK-08: BIND — the accept-side command (use case found) **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?). +`poc-bind-findings.md`); the ask ledger landed, superseded in shape +(2026-09-20, alkcall 0.8.0 + alktunnels 0.2.0 — see the landed note +below).** 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 @@ -958,7 +1009,8 @@ framing change — no one-way-door exposure). **The mechanism — accept-side resources (the `-R` far-side listener shape).** The consumer's `-R` path in alktunnels (`register_tunnel_listen_openable` + `listen_establisher` + -`AcceptQueue`, `alktunnels/src/producer.rs:207,392`) proves accept-side +`AcceptQueue`, alktunnels 0.1.0's `producer.rs:207,392`; line numbers +shifted in 0.2.0 with the bind-first addition) proves accept-side resources exist without forced binds; SOCKS5 BIND is their mirror image: the producer-side listener (network B — the app server must reach it) is the accept resource, each popped accept two-pumps against @@ -1008,6 +1060,56 @@ scope gate (`socks5:bind`, checked at command dispatch from the per-call opener identity) distinguishes ingress from egress and is POC-validated. +**The asks landed — superseded in shape (2026-09-20, alkcall 0.8.0 + +alktunnels 0.2.0).** The graduation (alktunnels +`docs/research/tunnels-graduation.md` → ADRs 007/008) took the +ledger's *two asks* as evidence and adopted the ssh +`forwarded-tcpip`-shaped alternative over the per-ask carriers: + +- **Ask #1 (learn the bound address without consuming an accept) is + dissolved, not implemented.** The bind-first establisher + (`bindfirst_establisher`, ADR-008 §1) binds during the + establishment phase — fast, no app-server dependency — so the + open-op reply natively carries the real OS-chosen bound address + (the `bound` reply field, projected via alkcall 0.8.0's + `Establishment::with_reply_field`, ADR-049 amendment 3; consumed + via `open_channel_with_reply` / `TunnelListener::bound()`). There + is no accept to avoid consuming: establishment = bind. This + *replaces* POC #5's pinned workaround ("reply#1 first, the tunnel + open rides the accept future") — the accept-first sequencing was a + workaround for the accept-first establisher, and the bind-first + shape removes the workaround's reason to exist. It also dissolves + POC #5's establishment-bound finding: the accept wait now lives in + the teardown-governed pump phase, so FTP-length BIND waits stop + resolving as spurious 10s-establishment refusals (ADR-008's stated + second win). The old accept-first surface (`listen_establisher` + + `AcceptQueue`) stays for the accept-as-open shape; both live in + alktunnels 0.2.0. +- **Ask #2 (peer metadata on accepted handles) landed at the wire's + native moment.** The forwarded op (`channels/tunnel/forwarded`, + ADR-008 §3) carries `{listen, substrate, peer}` params per accept — + the peer address rides the open-op params (deny-unknown, + schema-gated) rather than a `TargetHandle` field; `ForwardedOpener` + + `make_forwarded_op_handler` are the serving side. `TargetHandle` + itself gained no peer field — the graduation chose the + forwarded-op shape over the §3 option-i carrier, superseding the + ledger's original framing. +- **Correlation key refinement (ADR-008 Amendment 1, 2026-09-18):** + the key binding forwarded opens to listeners is a producer-minted + u32 token (`listen` reply field + forwarded param), not the + originally-planned channel-ID key (unimplementable against the + landed handler surface). + +Consequence for this crate's variant 2: the composed BIND path is +now *fully vanilla-fidelity* with no interim stand-ins — the factory +no longer carries an expected `bind_addr`, reply#1 advertises the +real `bound`, reply#2 carries the real peer, and no establishment +bound threatens long BIND waits. POC #5's findings remain accurate as +the record of what was validated against alktunnels 0.1.0; the +Phase 1 shape composes against 0.2.0 (`register_tunnel_bindfirst_ +openable` + `BindFirstInjections`, or `TunnelListener` on the +opener side). + **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 @@ -1171,8 +1273,10 @@ Candidate reading for the research specialist (to be expanded): 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 + pinned); **the ask ledger landed 2026-09-18/20 — superseded in + shape by the bind-first/forwarded graduation (see OQ-SK-08's + landed note)**; 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 @@ -1228,11 +1332,16 @@ binds except the two explicit, optional, feature-gated assembly shapes - **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. + both returning boxed halves. Local-socket and composed 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. **Updated 2026-09-20: the composed providers + compose against alktunnels 0.2.0** — the direct op + (`channels/tunnel/direct`, ADR-007) is the dynamic-target channel + for CONNECT, and `open_direct` + `Substrate::Udp` is the composed + ASSOCIATE vehicle (both substrates, one op, one grant: + `tunnel:direct`); the hub-leg for relaying them is alkcall 0.8.0's + in-tree `ChannelRelay`/`HubLegTemplate` (ADR-051). - **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 @@ -1255,16 +1364,30 @@ binds except the two explicit, optional, feature-gated assembly shapes BIND's base-crate inclusion + default accept variant (OQ-SK-08); 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), - 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. +- **Upstream asks — landed (the first-real-consumer sweep paid off).** + The graduation (`alktunnels docs/research/tunnels-graduation.md`, + alkcall review 008) took every ask at once, in one-version bumps: + - **alkcall 0.8.0**: the establisher reply projection + (`Establishment::with_reply_field(s)` + `open_channel_with_reply`, + ADR-049 amendment 3 — the `bound` carrier) and flavor-form + open-op ids in discovery (ADR-047 amendment 3 — + `channels/tunnel/direct`-shaped ops re-produce through hub relay + as channels); plus the bonus in-tree `ChannelRelay` + + `HubLegTemplate` (ADR-051) — the hub re-produce machinery is + in-tree, not assembly-layer. + - **alktunnels 0.2.0 (ADRs 007/008)**: the direct op (dynamic-target + egress — reshapes OQ-SK-01 Option A and OQ-SK-03's composed + egress), the bind-first establisher + forwarded op (dissolving + both BIND asks in shape — see OQ-SK-08's landed note), + `TunnelListener`, and the `local` bind-first substrate half. + - **fast-socks5 (still open, courtesy)**: the `net` feature gate + (POC #4's ~120-line change), the `run_udp_proxy_custom` + unconditional-bind seam, and the `new_reply` publication (POC #5 + re-encoded the reply wire form by hand). The BIND ledger never + had a fast-socks5 ask — the typestate fit with zero changes. + No asks remain blocking for this crate; the fast-socks5 items are + the courtesy tail (we own the upstream; POC #4's fork posture + covers them if wasm picks Case 1). ### What Phase 0 could NOT answer (handed to Phase 1) @@ -1290,4 +1413,12 @@ binds except the two explicit, optional, feature-gated assembly shapes 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. \ No newline at end of file + nothing here is a one-way door. **Updated 2026-09-20:** the + upstream substrate for the composed variant landed — alktunnels + 0.2.0's bind-first establisher + forwarded op (ADR-008) *dissolves* + both ask-ledger items in shape (the real `bound` rides the open + reply natively; the peer rides the forwarded params; the accept + wait is pump-phase, so no establishment bound). The pinned + "open rides the accept future" sequencing is retired as the + accept-first workaround it was; the Phase 1 ADR composes variant 2 + against 0.2.0 with full vanilla fidelity. \ No newline at end of file diff --git a/docs/research/poc-bind-findings.md b/docs/research/poc-bind-findings.md index 6c3e076..a52c229 100644 --- a/docs/research/poc-bind-findings.md +++ b/docs/research/poc-bind-findings.md @@ -1,6 +1,7 @@ --- status: complete -last_updated: 2026-09-15 +last_updated: 2026-09-20 (ledger statuses updated — the graduation +landed; both alktunnels asks superseded in shape) --- # alksocks POC findings — channels-native SOCKS5 BIND @@ -197,6 +198,43 @@ end-to-end (the same ownership lesson as POC #2's egress, mirrored). ## Upstream-ask ledger (the one-version alktunnels bump) +**Ledger statuses (2026-09-20 — the graduation landed).** The user's +standing note held: alktunnels 0.2.0 (ADRs 007/008, over alkcall +0.8.0) shipped in one version bump — but adopted the ssh +`forwarded-tcpip`-shaped alternative over the per-ask carriers, so +both asks are **superseded in shape**, not implemented as posed: + +1. **Listen-addr inspection without consuming an accept — dissolved.** + The bind-first establisher (`bindfirst_establisher`, ADR-008 §1) + binds during establishment, so the open-op reply natively carries + the real OS-chosen bound address (the `bound` reply field via + alkcall 0.8.0's `Establishment::with_reply_field`, ADR-049 + amendment 3; consumed via `open_channel_with_reply` / + `TunnelListener::bound()`). There is no accept to avoid consuming; + this ask's stand-in (the factory's carried `bind_addr`) is retired, + as is the POC's "open rides the accept future" sequencing + workaround — and the 10s establishment-bound finding dissolves with + it (the accept wait lives in the pump phase now). The + accept-first surface (`listen_establisher` + `AcceptQueue`) remains + in 0.2.0 unchanged for the accept-as-open shape. +2. **Accepted handles carrying peer metadata — landed at the wire's + native moment.** The forwarded op (`channels/tunnel/forwarded`, + ADR-008 §3) carries `{listen, substrate, peer}` params per accept; + `TargetHandle` gained no peer field — the graduation chose the + forwarded-op shape over this item's option-i carrier. Correlation + is a producer-minted u32 token (Amendment 1, 2026-09-18), not the + originally-planned channel-ID key. +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. (Unchanged — the + courtesy tail; no fast-socks5 release has landed these as of + 2026-09-20.) + +The POC's evidence stands as the record of what was validated against +alktunnels 0.1.0; the Phase 1 shape composes variant 2 against 0.2.0 +(`register_tunnel_bindfirst_openable` + `BindFirstInjections`). + 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