- Rewrote the remaining em dashes outside docs/reviews/ (preserved as historical record, matching the alktty precedent): tests/ (313), fuzz/ (34 plus one shell comment), tasks/ (671 plus 12 kept in fences), docs/research + sdd_process + plans (525 plus 25 in fences), docs/architecture/overview.md fence labels (13), README, CHANGELOG, AGENTS.md, fuzz/README.md (176), and the .opencode/agents prompts (110) - Rewrote fenced content too: ASCII tree and module-map label separators, Rust snippet comments, and template placeholders; wire data, JSON/BAST examples, and transcripts survive verbatim - Rewrote 6 assert/expect message literals in tests/ and 2 format strings in fuzz/shared/src; no test data changed - Added Project Convention 18: no em dashes in prose (doc comments, markdown including docs/ and tasks/, agent prompts, commit messages); fence content rule, en-dash clause rule, and the no-spaced-hyphen prohibition follow alktty's convention 15 Follows6b20924(src doc comments) andcd0afe0(docs/architecture). Verification: cargo test (141 pass); cargo clippy --all-targets -D warnings; cargo fmt --check; cargo doc --no-deps; wasm check and clippy for wasm32-unknown-unknown with -D warnings; em dash grep over the repo returns matches only in docs/reviews/
9.3 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| tunnels/local-bind-halves | local feature, TCP bind halves + the bind-first assembly pieces | completed |
|
narrow | low | component | implementation |
|
Description
Extend the local feature (non-wasm, tokio/net-gated;
tunnels/local-socket-halves established the pattern) with the
bind-first shape's substrate half: a real TCP bind function
(BindFn-compatible) and an accept loop producing
(peer, TargetHandle) pairs, plus the direct-op convenience dial
closure composition (the DialFn the direct op shares with the base
op, likely already satisfied by local_dial; verify, don't
duplicate).
The bind half
/// The real-socket BindFn source (the `local` feature's shape 3
/// backing): bind `addr` (port 0 = OS-chosen), report the REAL
/// bound address (the `bound` reply field's source, ADR-008 §2),
/// expose the accept loop that feeds the dispatcher.
pub async fn bind_tcp_first(addr: &str)
-> Result<BoundListener, TunnelEstablishError>;
pub fn local_bind_first() -> BindFn; // renders via bind_tcp_first
BoundListener.listenercarries theTcpListener(type-erased in the plan;tunnels/bindfirst-producer's shape note); the accept loop is assembly code composed ON it: the protocol crate stays socket-blind, so the loop lives here (local= substrate code, ADR-004):TcpListenerHalvesalready has an accept loop (accept_loopfeeding anAcceptQueue, the accept-first shape). The bind-first shape needs a DIFFERENT loop: per-accept it must produce(peer_addr, TargetHandle)for the accept dispatcher (which opens forwarded channels per accept), not feed a queue for the establisher to pop. Add a sibling method/struct (e.g.TcpListenerHalves::accept_pairsor a newBindFirstAcceptLoopadapter); decide the honest shape, document in Notes. The peer address rides fromaccept()'s return value (reply#2 fidelity's source, ADR-008 §3'speercarrier).- The direct-op
DialFn:local_dial()already renders(Substrate, backing)→ halves with the UDP framed adapter; the direct establisher calls the SAME closure with the rendered target string. Verify the UDP wrap happens INSIDE the closure (it does; review 001 U-1) so direct-udp gets the framed adapter free; add a test pinning direct-udp-through-local_dialframing if missing.
Tests (tests/, real sockets; the local suite's posture)
- Bind port 0 →
boundreports the REAL OS-chosen address (the reply#1 fidelity pin). - Two clients dial the bound address → two accepts → two forwarded channels (via the bind-first harness) → both pump independently (peer fidelity: each forwarded open carries the REAL accepted peer).
- Unix-domain bind (OQ-TN-14 parity):
bind_unix_first-equivalent. Decide whether unix bind-first ships in this task or is deferred with a note (the wire enum already carries unix; the local dial path exists, and the bind path is the same shape; prefer shipping both, matching dial parity).
Acceptance Criteria
bind_tcp_first+ the accept-pairs loop (peer + halves per accept) behindlocal- The bound address is the REAL OS-chosen one (port 0 resolved, pinned by test)
- Direct-op
DialFnreuse verified (no duplicate dial code; UDP framed-adapter wrap composes) - Unix bind-first parity decided + shipped or explicitly deferred with rationale
cargo test --all-featuresgreen; default-crate wasm32 checks still clean (the feature gate holds)- Clippy/fmt clean (both feature sets)
References
- docs/architecture/decisions/008-listen-metadata-forwarded-op.md (§1 shape 3, the bind-first establisher; §3 the peer carrier)
- docs/architecture/producer.md §The Registration API (
BindFnshape) - tasks/tunnels/local-socket-halves.md (the
localpattern + the existingbind_tcp/TcpListenerHalves/AcceptQueueaccept-first shapes) - tasks/tunnels/bindfirst-producer.md (the
BindFn/BindFirstAcceptLoopcontracts this implements) - OQ-TN-14 (unix ships with
localv1; stdio is out, as in alktty's)
Notes
- The task sketch's
bind_tcp_first(addr: &str)signature held: the committed [BindFn] contract takes(substrate, backing); the substrate dispatch lives inside [local_bind_first] (tcp → [bind_tcp_first], unix → [bind_unix_first], udp → fails loud), matching the type-erased closure precedent (the protocol crate stays socket-blind; the closure is the assembly side). - The accept-pairs shape: a
*_pairmethod, not a sibling loop struct: [TcpListenerHalves::accept_pair] (one accept per call →(peer, halves)). The task floatedaccept_pairsor aBindFirstAcceptLoopadapter struct; one method is the honest shape: the DISPATCHER owns the loop pacing (the committedBindFirstAcceptLoopcontract is one closure call = one accept), so the substrate half is just the single-accept step with the peer attached.local_bindfirst_accept_loop()is the adapter: it wraps the*_pairmethods with the downcast (the assembly side of the type-erased injection; a foreign listener type fails loud ashandler_error, pinned by test). The accept-FIRST [TcpListenerHalves::accept_loop] (queue-feeding) is untouched. - UDP bind-first is NOT here: refused loud (a decision the task
left open): a UDP socket has no accept, so the bind-first shape is
not DEFINED for udp; [
local_bind_first] mapsSubstrate::Udpto ahandler_errorrather than faking a listener (pinned byudp_bind_first_fails_loud). The per-peer associate shape (an accept-analogue for UDP) would be a wire/protocol design decision, an ADR if it ever ships. The direct/forward udp dial shapes cover the substrate. - Unix bind-first SHIPPED (the task's preferred posture,
OQ-TN-14 dial parity):
bind_unix+UnixListenerHalves(a sibling of [TcpListenerHalves]:local_addr, the queue-feedingaccept_loop, and the bind-firstaccept_pair) + [bind_unix_first]. The bind-firstaccept_pair's peer is the ACCEPTED client's bound path; an unnamed (autobind) client fails loud: the forwarded op'speerparam is required on the wire, an addressless peer cannot be represented (ADR-008 §3). The unix parity test binds the CLIENT to a named path first (socket2; std has noUnixStream::bind), making the peer representable. bind_unixmaps bind failure toResourceShortage(matching [bind_tcp]'s posture, the held-resource class), notDialFailed: the bind IS the dial for the establisher's error table (shape-3: unbindable/held →dial_failed-class rides the establisher's passthrough), but the substrate function's own class follows the sibling bind functions.- Direct-op
DialFnreuse: verified, no duplication: [local_dial] is unchanged and shared; the UDP wrap happens INSIDE the closure (review 001 U-1). The missing pin was added:direct_udp_through_local_dial_round_trips_framed(tests/ local_halves.rs) drivesTunnelSession::open_direct+Substrate::Udpthrough [local_dial] against a REAL udp echo socket, with the datagram boundary preserved through the direct op's establisher path.
Summary
Implemented the local feature's bind-first substrate half (shape 3,
ADR-008 as amended) per the task: src/local/mod.rs now has bind_tcp_first
(the real-socket [BindFn] source: port 0 resolved via local_addr,
the bound reply field's data), TcpListenerHalves::accept_pair (one
accept → (peer, halves), with the peer riding accept()'s return, the
forwarded op's peer carrier), bind_unix + UnixListenerHalves +
bind_unix_first (unix bind-first parity SHIPPED per OQ-TN-14; unnamed
peer fails loud, the forwarded peer param requires an address),
local_bind_first() (the [BindFn] convenience: tcp/unix dispatch,
udp fails loud: no accept on a UDP socket, the shape is not defined
there, never a fake listener) and local_bindfirst_accept_loop() (the
[BindFirstAcceptLoop] convenience: downcasts the plan's opaque
listener to the module's wrappers, one pair per call; a foreign
listener fails loud). The accept-FIRST shapes (accept_loop +
AcceptQueue) are untouched.
Tests: tests/local_bindfirst.rs (4, #![cfg(feature = "local")]),
covering the tcp e2e over REAL sockets (bind port 0 → the reply's bound is
the REAL OS-chosen address the test then DIALS; a fake bound would
strand the dial; two clients → two accepts → two forwarded channels →
independent interleaved pumps, each forwarded open carrying the REAL
accepted peer, distinct ports), unix parity (real socket path in
bound; a named-path client connects; the forwarded open carries the
REAL peer path), the udp refusal, and the downcast-miss probe. Plus
direct_udp_through_local_dial_round_trips_framed in tests/
local_halves.rs, the direct-op DialFn reuse pin (the framed udp
adapter composes through the direct op; review 001 U-1).
Verified: cargo test (default) and cargo test --all-features green
(46 lib + 151 integration total; the local suites 20 + 4), clippy
--all-targets --all-features -D warnings clean, fmt clean,
wasm32-unknown-unknown check passes (the default crate stays
wasm-clean; the bind-first half lives behind local, non-wasm by
design).