Files
alksocks/docs/architecture/decisions/007-bind-in-base.md
T
glm-5.3-flash 9587670138 docs: Phase 1 architecture — specs, ADRs 001-012, open-questions tracker
- 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
2026-09-20 18:41:56 +00:00

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`)