Files
glm-5.3-flash e196ed43e3 docs: remove em dashes from repo prose and forbid them going forward
- Rewrote em dash prose across the whole tree outside docs/reviews/
  (the historical reviews stay verbatim, matching the alktunnels and
  alktty precedent): src doc comments and inline comments (587),
  docs/architecture (319), docs/research + sdd_process (484), tests
  (361), tasks (899), fuzz (28), README/CHANGELOG/Cargo.toml (77),
  AGENTS.md (85), and the .opencode/agents prompts (109)
- Fenced content follows the precedent: comments, ASCII-diagram label
  separators, and template placeholders rewritten; wire data, JSON/BAST
  documents, byte diagrams, and transcripts survive verbatim; assert,
  expect, panic, error-display, and format message literals rewritten
  as prose; no code, fixture, or test data change
- src/rfc1928/LICENSE: the two dashes in alksocks' own provenance
  header rewritten; the MIT legal text untouched
- AGENTS.md adds Project Convention 18: no em dashes in prose; varied
  restructure, no spaced-hyphen substitutes, fenced prose rule, en
  dash numeric ranges exempt, docs/reviews/ exempt from the greps
- tasks/*.md frontmatter: the name values rewritten by the sweep are
  now quoted (a raw colon inside an unquoted YAML scalar parsed as
  null); taskgraph validate reports 37 tasks, no issues

Verification: cargo test; cargo clippy --all-targets -D warnings;
cargo fmt --check; cargo doc --no-deps; fuzz corpus replay
(cargo test --manifest-path fuzz/shared/Cargo.toml); wasm check and
clippy for wasm32-unknown-unknown with -D warnings; em dash grep over
the repo returns matches only in docs/reviews/ and the bast.md BAST
JSON data fence
2026-11-03 19:31:53 +00:00

4.4 KiB

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, with 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)