- 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
7.9 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level |
|---|---|---|---|---|---|---|---|
| implementation/fix-producer-gate-policy | Fix: producer BIND gate policy + register coverage + stage doc lines (review-002 C-3, D-2, S-1, N-2) | completed | narrow | medium | component | implementation |
Description
Close review 002's producer-side API-shape hazard and the
unexercised assembly surface, plus the two doc-line residues that
ride the same files
(docs/reviews/002-protocol-core-implementation-review.md):
-
C-3 (minor):
serve_socks5_overderivescan_bindfromaccept.is_some()(src/producer.rs:630-648): factory-wired ⇒ BIND allowed, with no way to say "factory wired but gate closed" on the substrate-free entry. The pump handler (the channels path) resolves the gate from the per-call opener identity (ADR-006, which is correct); the substrate-free entry derives it from seam presence. For the batch-2localbackend, which is the documented consumer of this entry, the local door's BIND authorization has no seam to plug into except factory presence, which is exactly the shape that invites "listener configured" ⇒ "listener permitted" (the conflation ADR-006 exists to prevent).Fix per the review's sketch: widen the substrate-free entry's gate to an explicit policy input,
serve_socks5_over_with_gateor a builder field.bind_policy(bool)defaulting toaccept.is_some()for backward-compat with the landed tests. The pump handler's identity-derived gate is unaffected. One test: a producer with an accept factory + explicitcan_bind=falserefuses 0x02. -
D-2 (minor):
Socks5Producer::registerand the builder'segress/acceptsetters are unexercised public surface (0% covered; the harness registers via the free functionregister_socks5_openableinstead, and the builder test drives onlyserve_socks5_over). The spec'd assembly shape (producer.md§Wiring) is the one assembly path with no test, so a signature drift or delegation bug would pass the suite.Fix: one integration test wiring a full
Socks5Producer::builder().dial(...).egress(...).accept(...)through.register(...)and serving all three commands over the harness topology. This is the natural home for C-3's gate test. -
S-1 (minor): the datagram stage's residual hot-
Ok(None)posture, named as a decision. The R-1 yield-guard makes the stage livelock-safe; the residual is a misbehaving egress returningOk(None)at full speed = a yielding busy-wait (bounded by the egress's own correctness, unbounded by rate). Per the review: a posture note, not a backoff/budget (which would penalize the compliant path). One doc line onrun_datagram_stagenaming the residual. -
N-2 (observation): one line in wire.md §UDP ASSOCIATE. The per-datagram
egress.sendis awaited inline (the stage interleaves sends with reply drains); a slow egress applies backpressure to the association, not to other channels. The batch-2 composed-egress author inherits the bound knowingly.
Acceptance Criteria
- The substrate-free entry's BIND gate is an explicit policy
input (with-gate entry or builder field);
accept.is_some()remains only as the backward-compat default, never the sole gate - Test: accept factory wired + explicit
can_bind=false⇒ BIND refused 0x02; CONNECT/ASSOCIATE still serve - The pump handler's identity-derived gate is unchanged (the existing identity matrix stays green)
- Integration test: full builder (
dial+egress+accept) through.register(...)serving all three commands over the harness topology run_datagram_stage's doc names the hot-Ok(None)residual- wire.md §UDP ASSOCIATE carries the inline-send backpressure line
cargo testgreen; clippy/fmt clean; both wasm checks clean
References
- docs/reviews/002-protocol-core-implementation-review.md §C-3, §D-2, §S-1, §N-2
- docs/architecture/decisions/006-access-control.md (the gate posture, "listener configured" ≠ "listener permitted")
- docs/architecture/producer.md §Wiring (the spec'd assembly shape)
- docs/architecture/wire.md §UDP ASSOCIATE (the N-2 line's home)
- tasks/implementation/core-producer-bind.md (the landed gate)
Notes
- C-3 shape chosen: the builder field (
.bind_policy(bool)), not a separateserve_socks5_over_with_gateentry; the review's sketch allowed either, but a builder field keeps the entry's signature untouched (oneserve_socks5_overshape), rides the existing builder chain, and the policy is resolved once atbuild()(self.bind_policy.unwrap_or(self.accept.is_some()):Option<bool>on the builder,boolon the built producer). The free functionserve_socks5_over(already gate-explicit) and the pump handler (identity-derived, ADR-006) are untouched. - D-2 vehicle: a new
RegisterMode::ProducerBuilderintests/harness.rswiring the full builder chain (.dial().egress().accept()+ the optional.bind_policy()) throughSocks5Producer::register: the harness's registration block gains a third arm (sameChannelCore+register_openable_with_establishershape as the others, via the delegated method). The integration test (producer_builder_registers_and_serves_all_three_commandsintests/core-integration.rs) serves CONNECT + ASSOCIATE + BIND over the topology and asserts the identity witness (proving the builder-delegated establisher ran). C-3's gate tests live in the producer unit suite (builder_bind_policy_false_refuses_bind_ while_connect_associate_serve,builder_default_bind_policy_ follows_accept_presence): the substrate-free entry is a unit shape; the builder-through-register surface is the integration one. - The
bind_policy(false)test pins: 0x02 on BIND with the accept factory wired, the factory never called (no phantom listener), the in-streamAuthenticationRejectedsession error, and CONNECT + ASSOCIATE still serving the same producer. The default test pins the backward-compat reading: accept wired + policy unwired ⇒ BIND serves (factory ran); accept unwired + policy unwired ⇒ 0x02. - S-1/N-2 are doc lines only (per the review's recommendation:
posture notes, not backoff/budget code): a residual paragraph on
run_datagram_stage(hot-Ok(None)= a yielding busy-wait, bounded by the egress's own correctness, accepted-uncovered by design) and a bolded inline-send sentence on the wire.md §UDP ASSOCIATE producer-egress bullet.
Summary
All four items landed (C-3, D-2, S-1, N-2). Socks5ProducerBuilder
gained .bind_policy(bool), the substrate-free entry's explicit
BIND gate, defaulting to the accept seam's presence (the built
producer resolves it to a plain bool); Socks5Producer:: serve_socks5_over now consults the policy instead of
accept.is_some(). The pump handler's identity-derived gate is
byte-identical (the identity matrix stayed green untouched). Two
unit tests pin the policy (explicit-closed refuses 0x02 with the
factory un-called while CONNECT/ASSOCIATE serve; the
presence-default both ways). A new harness mode
(RegisterMode::ProducerBuilder) plus one integration test wires
the full builder through .register(...) and serves all three
commands end to end; the spec'd assembly shape is no longer
unexercised. Doc lines: run_datagram_stage names the hot-Ok(None)
residual (S-1); wire.md §UDP ASSOCIATE carries the inline-send
backpressure bound (N-2). Verification: cargo test 352 green (125
lib + 38 associate + 41 bind + 40 connect + 41 integration + 30
harness + 37 differential + 3 doctests, 2 ignored, since the two new
unit tests and the one new integration test added to the review's
348 baseline), cargo clippy --all-targets -- -D warnings clean,
cargo fmt --check clean, cargo doc --no-deps clean, both wasm
checks (cargo check + clippy -D warnings, wasm32) clean.