Files
alksocks/tasks/implementation/local-backend-producer.md
T
glm-5.3-flash 29569d9afd local: the producer-side local backend — the producer's front door
- ProducerDoor (feature local, src/local/producer_door.rs): a real
  TcpListener accept loop handing each accepted connection to the
  landed Socks5Producer::serve_socks5_over — the NoAuth in-stream RFC
  conversation with the local seam providers dialing/egressing/accepting;
  no new protocol-layer code.
- The accept path owns ADR-014's two knobs: the idle-first-byte peek
  (non-consuming) and the max-concurrent-sessions cap (try_acquire —
  an accept beyond the bound is refused door-side).
- ProducerDoorGuard: the listener's lifetime is the caller's — graceful
  close() awaits the loop's end; Drop aborts best-effort (the
  alktunnels TunnelListener teardown posture). In-flight sessions are
  never force-closed.
- Per-connection failures are logged + isolated (the loop continues).
- The BIND gate on this path is the builder's bind_policy (no open-op
  identity here; the pump handler's identity-derived gate does not
  apply) — pinned by the 0x02-refusal test.
- tests/local-backend-producer.rs (9): vanilla CONNECT round-trip on
  real sockets, faithful-RFC BIND (real reply#1/reply#2 addresses),
  UDP ASSOCIATE through the real egress, domain resolution at the
  seam, idle timeout, session cap, failure isolation, bind_policy(false).

Verification: cargo test + --all-features green; clippy clean (default,
all-features, wasm32 targets); cargo fmt --check clean; wasm checks
green (backend is local-gated).
2026-09-25 14:23:18 +00:00

7.0 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
implementation/local-backend-producer Producer-side local backend — the producer's front door (vanilla clients → a locally-served SOCKS5 resource) completed
implementation/local-providers
narrow low component implementation

Description

The producer-side local backend (feature local): the same door shape in the other direction — the producer serves from a real listener, so vanilla clients reach a locally-served SOCKS5 resource (producer.md §Producer-Side Local Backend). This is the shape where the SOCKS5 service rides a real TcpListener instead of (or alongside) channels: each accepted TCP connection is handed to the landed substrate-free entry Socks5Producer::serve_socks5_over directly — the RFC conversation runs on the accepted socket, the seams dial locally.

Deliverables:

  1. src/local/ additions (feature local) — the producer-side listener shape:
    • A serve_local-shaped constructor on the local module (exact name at implementation; producer.md names no symbol — record the choice in Notes): takes a Socks5Producer (the landed builder type), a bind address, and the door-policy knobs (the idle-first-byte timeout + max-concurrent-sessions cap — the same two knobs the consumer door carries, ADR-014's accept-path defense; an accepted socket is not a channel).
    • The accept loop: bind the listener, per accepted connection spawn producer.serve_socks5_over(stream); per-connection failures are logged + isolated (the loop continues). The listener's lifetime is the caller's (a close/Drop shape — the alktunnels TunnelListener teardown posture).
    • This is a front door for the producer side: the door surface (NoAuth raw pipe vs RFC 1929 cred-bearing, ADR-005) is the consumer door's code shape. For the producer-side backend the NoAuth shape is the primary one (the producer IS the RFC conversation here — serve_socks5_over runs accept_no_auth in-stream); a cred-bearing variant would wrap accept_password_auth before serve_socks5_over — land the NoAuth shape; the cred-bearing producer-side door is a documented follow-up unless a test drives it (record the decision in Notes).
    • The BIND command gate on this path is the builder's bind_policy (the landed Socks5ProducerBuilder::bind_policy — the substrate-free entry's gate); document that the per-caller gate (the pump handler's identity-derived one) does not apply here — this path has no open-op identity.
  2. Tests (tests/local-backend-producer.rs): a vanilla RFC 1928 client against the locally-served producer — CONNECT round-trip through local::tcp_dial() seams (the whole stack on real sockets); BIND through the local backend (the faithful-RFC variant: real reply#1/reply#2 addresses); the idle-first-byte timeout; the session cap; per-connection failure isolation; the bind_policy(false) gate refuses BIND RFC-faithfully (0x02).

Acceptance Criteria

  • cargo test passes; cargo test --all-features passes
  • cargo clippy --all-targets -- -D warnings clean; cargo fmt --check clean
  • Default wasm checks stay green (backend code is local-gated)
  • The backend serves via Socks5Producer::serve_socks5_over (the landed substrate-free entry) — no new protocol-layer code
  • The accept path owns the idle defense + session cap (the two knobs) — pinned by tests
  • Per-connection failure isolation — pinned by test
  • The bind_policy gate is honored on this path — pinned by test

References

  • docs/architecture/producer.md §Producer-Side Local Backend, §Wiring
  • docs/architecture/decisions/005-auth-split-by-door.md (the door surface), 014-idle-timeout-posture.md, 006 (the bind gate)
  • Landed surfaces: src/producer.rs (Socks5Producer/ Socks5ProducerBuilder::bind_policy/serve_socks5_over), src/local/ (the providers from local-providers)
  • POC reference: /workspace/alksocks-connect-poc/src/producer.rs (the serve_socks5_over shape the POC validated)

Notes

  • Symbol choice: the constructor is ProducerDoor (+ ProducerDoorBuilder/ ProducerDoorGuard) in src/local/producer_door.rs — the consumer door's sibling name (FrontDoor/UdpFrontDoor), not serve_local: the surface is a builder + accept-loop object (serve() binds, serve_on(listener) is the test/assembly seam over an existing listener), matching the consumer door's shape rather than a free function.
  • Door surface decision: the NoAuth shape landed; the cred-bearing producer-side door is a documented follow-up (no test drives it). On this path the producer IS the RFC conversation (serve_socks5_over runs accept_no_auth in-stream) — there is no door-side protocol half to re-originate; a cred-bearing variant would wrap accept_password_auth before handing the stream to serve_socks5_over.
  • Guard shape (the alktunnels TunnelListener teardown posture): serve/serve_on return a ProducerDoorGuard; close() ends the accept loop gracefully and awaits the loop task; a Drop aborts it best-effort (sync, never awaits). In-flight sessions are never force-closed by the guard — they own their sockets (per-connection isolation cuts both ways).
  • BIND gate documentation: the per-caller gate (the pump handler's identity-derived one) does not apply here — this path has no open-op identity; the builder's bind_policy is the whole gate, pinned by the bind_policy(false) test (0x02 refusal, CONNECT still serves).
  • No protocol-layer code: the accept loop rides Socks5Producer::serve_socks5_over directly; the only additions are accept-loop assembly (peek idle gate, Semaphore session cap, try_acquire_owned door-side refusal) and the watch-signaled guard.

Summary

Landed the producer-side local backend (feature local): ProducerDoor (src/local/producer_door.rs, exported from the crate root) — a real TcpListener front door handing every accepted connection to the landed Socks5Producer::serve_socks5_over (NoAuth in-stream RFC conversation; local::tcp_dial() / udp_egress() / bind_accept() dial locally). The accept path owns ADR-014's two knobs (idle-first-byte peek + session-cap semaphore, door-side refusals); per-connection failures are logged + isolated; the ProducerDoorGuard carries the listener's lifetime (graceful close() / best-effort abort Drop). Pinned by tests/local-backend-producer.rs (9 tests): vanilla CONNECT round-trip on real sockets, faithful-RFC BIND (real reply#1/reply#2 addresses), UDP ASSOCIATE through the real egress, domain-target resolution at the seam, the idle timeout, the session cap (refusal while live sessions serve), failure isolation, and the bind_policy(false) gate. Verified: cargo test + --all-features green, clippy clean (default/all/wasm targets), cargo fmt --check clean, wasm checks stay green.