- docs/architecture/: README index, overview, wire, producer, consumer, vendor, bast specs + open-questions.md (the OQ-SK-01..09 ledger promoted; new questions as OQ-SK2-06..) - ADRs 001-012: ALPN/op/params, protocol base, the handle swap, the dial-callback shape, auth split by the door, access control (open gate + BIND command gate), BIND in base, provider defaults, feature gates, channel capacity, the inline vendor, target policy - deferred OQs (SK2-06, SK2-07) with tracker tasks in tasks/architecture/ per the sdd_process deferred-OQ protocol - review pass applied: frame-length joint bound (65766 corner), accept_no_auth vs skip_auth corrected against fast-socks5 source, composed-feature wasm posture, cross-reference fixes Verified: cross-references (all ADR/OQ refs resolve), doc sizes under split threshold, Phase 0 fidelity spot-checks
91 lines
4.4 KiB
Markdown
91 lines
4.4 KiB
Markdown
# ADR-007: BIND in the Base Crate — the Phase Model Extended
|
|
|
|
## Status
|
|
|
|
Accepted (2026-09-20, Phase 1; POC #5 validated end to end
|
|
2026-09-15)
|
|
|
|
## Context
|
|
|
|
fast-socks5 parses `Socks5Command::TCPBind` but every handler
|
|
refuses it; no bind machinery exists. RFC 1928 BIND is the
|
|
accept-side command (the `-R` far-side listener shape): the client
|
|
asks for a listener on the producer's network; the producer replies
|
|
#1 with the bound address, waits for exactly one inbound accept, and
|
|
replies #2 with the peer address before the data plane. The use case
|
|
that makes it real (FTP active mode is the canonical vanilla reason)
|
|
plus the composed variant's hub story were found in Phase 0
|
|
(OQ-SK-08), and POC #5 validated the whole shape end to end — both
|
|
accept variants, the scope gate, the failure mapping, the vanilla
|
|
front door.
|
|
|
|
Support is purely additive: the same ALPN, same op, same params, no
|
|
framing change; RFC-refusal of BIND remains valid for old consumers
|
|
(a producer that doesn't implement BIND just keeps refusing it).
|
|
Nothing here is a one-way door.
|
|
|
|
## Decision
|
|
|
|
- **BIND ships in the base crate** (protocol layer), phase-model
|
|
style: request → reply#1 carrying `BND.ADDR` → quiet wait (any
|
|
control byte after reply#1 is protocol garbage; the session ends —
|
|
exactly two exits: the accept, or the control stream's EOF/
|
|
garbage/error) → exactly one inbound accept → reply#2 carrying the
|
|
peer address → raw pass-through data plane (zero per-packet
|
|
headers in-band, same as CONNECT; only the ASSOCIATE datagram
|
|
stage is framed).
|
|
- **The accept seam is `BindAcceptFactory`** (ADR-004). Two
|
|
provider-backed accept variants (plus the wrapper-aware in-band
|
|
default when neither is wired), all POC #5-validated:
|
|
1. **Local backend binds for real** (feature-gated) — the faithful
|
|
RFC shape: a real network-B address in reply#1 (the listener's
|
|
`local_addr`), a real peer in reply#2, vanilla front-door
|
|
clients work. **This is the default accept variant** for the
|
|
optional local backend.
|
|
2. **Composed (alktunnels listen resource)** — the listener lives
|
|
on a bind-first listen resource (`register_tunnel_bindfirst_
|
|
openable` + `BindFirstInjections`, alktunnels 0.2.0); per-
|
|
listener ACL for free, hub-relayable. **Vanilla-fidelity with
|
|
no stand-ins** since the graduation: the factory no longer
|
|
carries an expected `bind_addr` — reply#1 advertises the real
|
|
OS-chosen `bound` (the open-op reply's `bound` field,
|
|
alkcall 0.8.0 reply projection); reply#2 carries the real peer
|
|
(the forwarded op's `peer` param).
|
|
3. **Wrapper-aware only** (zeros in reply#1, sentinel reply#2) —
|
|
retained for completeness as the in-band default when neither
|
|
provider is wired: the handle-swap reading applied to BIND
|
|
(the reply is RFC-compatibility surface; the data plane is the
|
|
channel).
|
|
- **The establishment bound covers only the open-op round trip.**
|
|
The accept wait lives in the teardown-governed pump phase — FTP-
|
|
length BIND waits stop resolving as spurious establishment
|
|
refusals (the bind-first graduation's stated second win;
|
|
`poc-bind-findings.md` §Establishment-bound, superseded in shape).
|
|
- **Failure mapping is RFC-faithful end to end**: factory failure
|
|
(pre-reply#1) → reply#1 with the mapped reply code; accept-stage
|
|
failure (post-reply#1) → reply#2 with the mapped code, no data
|
|
plane. No phantom listeners, no phantom data planes.
|
|
|
|
## Consequences
|
|
|
|
- The RFC 1928 command surface is complete: CONNECT + BIND +
|
|
ASSOCIATE, all three on one op (POC #5 test 7 regression-guarded
|
|
the other two).
|
|
- The composed accept path composes against alktunnels 0.2.0 — the
|
|
bind-first establisher + forwarded op (ADR-008) are the substrate;
|
|
the POC's pinned workaround (reply#1 first, the tunnel open rides
|
|
the accept future) is retired with the graduation.
|
|
- Old consumers see no change (the refusal was always the default);
|
|
the support is additive and gated by `socks5:bind` (ADR-006).
|
|
|
|
## References
|
|
|
|
- OQ-SK-08 (the full reasoning + the graduation's shape
|
|
supersession), OQ-SK-01 (the accept seam's shape), ADR-004
|
|
(the accept seam), ADR-006 (the command gate)
|
|
- POC #5 `poc-bind-findings.md` (the validation record; the
|
|
superseded-in-shape ask ledger)
|
|
- alktunnels ADR-007/ADR-008 (the direct op; bind-first establisher
|
|
+ forwarded op), alkcall ADR-049 amendment 3 (the reply
|
|
projection), alktunnels graduation research
|
|
(`alktunnels/docs/research/tunnels-graduation.md`) |