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).
This commit is contained in:
glm-5.3-flash committed 2026-09-25 14:23:18 +00:00
1 parent 2413646b6a
commit 29569d9afd
5 files changed
+854 -3

No files matched your search

+42 -3
View File
@@ -1,7 +1,7 @@
---
id: implementation/local-backend-producer
name: Producer-side local backend — the producer's front door (vanilla clients → a locally-served SOCKS5 resource)
status: pending
status: completed
depends_on: [implementation/local-providers]
scope: narrow
risk: low
@@ -86,8 +86,47 @@ Deliverables:
## Notes
> To be filled by implementation agent
- **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
> To be filled on completion
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.