tasks: the batch-2 decomposition — the remaining assembly-layer tasks

This commit is contained in:
glm-5.3-flash committed 2026-09-25 11:07:48 +00:00
1 parent a867f0cb81
commit f3c371310c
7 files changed
+747

No files matched your search

@@ -0,0 +1,93 @@
---
id: implementation/assembly-integration-suite
name: Assembly — the cross-cutting integration suite + docs sync
status: pending
depends_on: [implementation/door-udp, implementation/local-backend-producer, implementation/composed-accept]
scope: moderate
risk: medium
impact: phase
level: implementation
---
## Description
The batch-2 closing task (the core-integration-suite's assembly-layer
analogue): the cross-cutting suite that pins the whole assembly
story, plus the docs sync that records what landed. The per-component
tasks own their verticals; this task owns the compositions and the
spec-vs-implementation reconciliation.
Deliverables:
1. **Cross-cutting integration tests** (`tests/assembly-integration.rs`):
- **The full `-D` story end to end**: a vanilla tun2proxy-shaped
client → the cred-bearing TCP door → the channels-native
session → the producer with `local` providers → real echo
targets — CONNECT + UDP (the RFC-header'd datagram through the
UDP door) + BIND (the vanilla BIND conversation through the
door) all through one door process against one producer
registration (the one-registration-three-commands regression,
now through the doors).
- **The composed story end to end**: the producer with
`composed::direct_egress` + `composed::bindfirst_accept` —
CONNECT through the direct op, ASSOCIATE through the lazy
table, BIND through the bind-first listener (the ADR-008
composed variant set, all three commands).
- **Mixed assembly**: one producer registration serving
channels-native sessions AND door clients AND composed seams
concurrently (the producer/consumer-roles-not-sides posture —
both shapes on one connection).
- **Teardown matrix, assembly edition**: door-client disconnect
tears the association/channel down (no leaks — the channel
count witnesses); the idle bounds fire; fresh sessions succeed
immediately after each teardown (no connection poisoning).
- **The wasm gates re-verified**: default + `--features composed`
wasm checks green (the `local` feature is non-wasm by design —
verify it *fails* to build on wasm, the alktty posture, and
record the observed error in Notes).
2. **Docs sync** (the architecture-sync check):
- `docs/architecture/README.md` Current State + the doc statuses
reflect the landed assembly layer (the "no `local`/`composed`
code exists yet" lines updated).
- `lib.rs` crate docs updated (the "not yet present" paragraph —
the doors/providers are landed now).
- Any spec drift the implementation surfaced reconciled in the
same change — never silently diverged (the batch-1 discipline).
3. **Full verification sweep** (the batch gate's input):
`cargo test`, `cargo test --all-features`, clippy
`--all-targets --all-features -- -D warnings`, `cargo fmt
--check`, both wasm checks (default + composed), and `cargo doc
--no-deps` (docs changed).
## Acceptance Criteria
- [ ] The full `-D` story (vanilla client → door → session →
producer → real target) pinned end to end for all three
commands
- [ ] The composed variant set pinned end to end (all three commands)
- [ ] Mixed assembly (doors + channels-native + composed) concurrent
on one producer — pinned
- [ ] The assembly teardown matrix pinned (door disconnects, idle
bounds, fresh-session-after-teardown)
- [ ] All four verification targets green (default, all-features,
wasm default, wasm composed)
- [ ] Docs synced (README Current State, lib.rs crate docs, any
drift reconciled)
## References
- docs/architecture/overview.md (the feature gates + module map —
the landed-shape check), consumer.md §The Front Doors, producer.md
§Producer-Side Local Backend, §Wiring
- docs/architecture/decisions/005/007/008/009/014
- The batch-1 precedent: `tests/core-integration.rs` (the suite
shape), `tasks/implementation/core-integration-suite.md` (the
task shape)
## Notes
> To be filled by implementation agent
## Summary
> To be filled on completion
+122
View File
@@ -0,0 +1,122 @@
---
id: implementation/composed-accept
name: Composed — the bind-first accept (the listener on a listen resource)
status: pending
depends_on: [implementation/composed-egress]
scope: moderate
risk: medium
impact: component
level: implementation
---
## Description
The `composed` feature's accept provider (ADR-008's composed
variants; ADR-007 variant 2): the BIND accept whose listener lives on
an alktunnels 0.2.0 bind-first listen resource
(`register_tunnel_bindfirst_openable` + `BindFirstInjections`) —
per-listener ACL for free, hub-relayable. **Vanilla-fidelity with no
stand-ins since the graduation** (ADR-007): 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).
Deliverables:
1. **`src/composed/` additions** (feature `composed`) — the composed
accept, named per producer.md §Wiring:
- `bindfirst_accept(...)` → `BindAcceptFactory`: the POC #5
`composed_bind_accept` shape **updated to the graduation** —
the POC's pinned workaround (reply#1 first, the tunnel open
rides the accept future; the factory carries an expected
`bind_addr`) is **retired** (ADR-007 §Consequences). The landed
shape: the factory opens the bind-first listen channel
(`TunnelListener::open` — the reply projection surfaces
`bound`), reply#1 advertises the real `bound`, and the accept
future correlates the forwarded sessions (the `listen` token →
the adopted forwarded sessions the assembly correlates; the
forwarded op's `peer` param is reply#2's peer).
- **The correlation shape is the design center** (the POC's
upstream-ask ledger retired by the graduation): the factory
returns `BindAccept` whose accept future waits for the ONE
forwarded session matching the listener's token. The
correlation registry is assembly-side bookkeeping (the
alktunnels `TunnelListener` docs: "forwarded sessions arrive as
ordinary adopted sessions on the manager; the listener does not
own the receipt queue"). The provider owns: the
`register_tunnel_forwarded_openable` serving side (the opener
half — the SOCKS5 producer's connection must serve the
forwarded op), the token→waiter map, and the per-forwarded
pump. Design the wait shape against the landed alkcall 0.8.0
adopt surface (`manager.adopt_channel`) and record it in Notes.
- **The Sync-bound wrinkle** (shared with composed-egress): the
accepted halves must be `Box<dyn AsyncRead + Send + Sync +
Unpin>` (`BindTargetHandle`'s payloads); alktunnels' `Taken
Halves` yields `Send + Unpin` only. The owner-wrapper rides
**inside the handle payloads** (egress.rs's documented posture)
— the wrapper owns the adopted channel session so dropping the
handle drops everything (the teardown-at-birth discipline,
compiler-enforced). This is the load-bearing wrapper: design it
once (with composed-egress's needs), pin it with a test.
- **Peer-address fidelity**: the forwarded op's `peer` param is
reply#2's real peer (the graduation's win); the wrapper-aware
sentinel (`0.0.0.0:0`) is the in-band default's shape, not this
variant's.
- **The establishment bound covers only the open-op round trip**
(ADR-007): the accept wait lives in the teardown-governed pump
phase — the quiet wait's `select!` drop of the accept future IS
the client-gone teardown; the listener (inside the future)
closes with it. The forwarded-op correlation must survive a
client-gone drop without leaking the listener (the accept
future owns it end-to-end — the seam contract).
- Module doc: names the ADRs (007/008), the graduation's
vanilla-fidelity win, the correlation shape, the owner-wrapper.
2. **Tests** (`tests/composed-accept.rs`): the full composed BIND
story over channels — a bind-first tunnel producer
(`register_tunnel_bindfirst_openable` + injections), the SOCKS5
producer with `composed::bindfirst_accept(...)`, a consumer BIND
session: reply#1 carries the real `bound`, an app server dials
the far listener, reply#2 carries the real peer, the pass-through
data plane round-trips; the client-gone teardown releases the
listener (drop-witness); two concurrent BIND sessions get
distinct tokens; a forwarded-open failure is per-accept isolated.
## Acceptance Criteria
- [ ] `cargo test` passes; `cargo test --all-features` passes
- [ ] `cargo check --features composed --target wasm32-unknown-
unknown` clean (hard gate)
- [ ] `cargo clippy --all-targets --all-features -- -D warnings`
clean; `cargo fmt --check` clean
- [ ] The graduation's shape lands (reply#1 = the real `bound`;
reply#2 = the forwarded op's real `peer`) — no expected-addr
stand-in — pinned by test
- [ ] The owner-wrapper rides inside the handle payloads (Sync
halves; teardown structural) — pinned by test
- [ ] The client-gone teardown releases the listener (the accept
future owns it end-to-end) — pinned by drop-witness test
- [ ] The forwarded-op serving side + correlation land; per-accept
isolation — pinned by tests
## References
- docs/architecture/decisions/007-bind-in-base.md (variant 2 + the
graduation), 008-provider-defaults.md, 004 (the accept seam's
contracts), 006 (the command gate)
- docs/architecture/producer.md §Wiring
(`composed::bindfirst_accept`), §The Three Seams
- POC reference: `/workspace/alksocks-bind-poc/src/accept.rs`
(`composed_bind_accept` — the superseded-in-shape workaround, the
owner-wrapper's first witness)
- Landed surfaces: `src/egress.rs` (`BindAccept`/`BindTargetHandle`
— the contracts), alktunnels 0.2.0 (`register_tunnel_bindfirst_
openable`, `BindFirstInjections`, `TunnelListener`,
`register_tunnel_forwarded_openable`, `ForwardedHandler`)
## Notes
> To be filled by implementation agent
## Summary
> To be filled on completion
+123
View File
@@ -0,0 +1,123 @@
---
id: implementation/composed-egress
name: Composed — the direct-op egress (lazy destination→tunnel table)
status: pending
depends_on: []
scope: moderate
risk: medium
impact: component
level: implementation
---
## Description
The `composed` feature's egress provider (ADR-008's composed
variants): the ASSOCIATE egress that dials each destination through
an alktunnels 0.2.0 direct-op channel (`TunnelSession::open_direct`,
`Substrate::Udp`) — per-target ACL for free (each destination rides
its own `tunnel:direct`-governed channel), the lazy
destination→tunnel table LRU-evicted at a cap (POC #2's
`ComposedEgress` shape).
Deliverables:
1. **`Cargo.toml`** — `composed = ["dep:alktunnels"]` (the only
feature that adds the alktunnels dependency, ADR-009); alktunnels
pinned `"0.2.0"` (the sibling pin posture — convention 11's
deliberate-bump rule applies). Verify the composed graph stays
wasm-clean (alktunnels' default crate is wasm-clean; the composed
providers use only its ungated channels surface — the wasm check
with `--features composed` is a hard gate).
2. **`src/composed/mod.rs`** (feature `composed`) — the composed
providers, named per producer.md §Wiring:
- `direct_egress(...)` → `EgressFactory`: the POC #2
`ComposedEgress` shape ported to the landed seam types — a lazy
destination→tunnel table: each new destination opens one
alktunnels UDP-tunnel channel (`open_direct`, `Substrate::Udp`),
a relay task per tunnel drains inbound datagrams into a shared
queue, the write half stays in the table (LRU-capped; dropping
a write half EOFs the tunnel producer-side; the session's Drop
reaps the channel entry — alktunnels ADR-005).
- **The Sync-bound wrinkle (the batch-1 review's seam contract vs
alktunnels' surface)**: the landed `TargetHandle`/`BindTarget
Handle` halves are `Box<dyn AsyncRead + Send + Sync + Unpin>`,
but alktunnels' `TakenHalves` yields `Send + Unpin` only. The
composed providers need an **owner-wrapper inside the handle
payloads** (egress.rs's documented posture: "keep-alive state a
substrate needs rides inside the halves — a provider-side owner
wrapper"). For the egress the wrapper is the table entry's
session (the egress owns it — not a handle payload); for the
composed *accept* (the sibling task) the wrapper is load-bearing
— design it here, share it.
- **The readiness contract** (load-bearing): `next_reply` parks on
the shared inbound queue (`recv().await` — Pending until a
relay task delivers); a closed queue (all relays ended) returns
`Err` — never a hot `Ok(None)` (the POC's closed-queue arm; the
stage's zero-yield defense, review 002 R-1, is the backstop,
not the design). Replies carry *inner* bytes (`addr + data` —
the reply address is the entry's recorded destination;
framing-once).
- **Per-datagram `send` is awaited inline** (the stage
interleaves sends with reply drains) — a slow egress applies
backpressure to the association, not to other channels
(wire.md's note: "the batch-2 composed-egress author inherits
this bound knowingly"). The table's open cost rides the send
path (lazy open on first datagram — the POC's measured ~0.8 ms).
- The resource/target mapping: `open_direct` takes a
`SubstrateAddr` — the destination's `TargetAddr` renders to it
(IP forms direct; domain form passes through unresolved —
producer-side resolution, the ssh `-D` semantic; ADR-012).
Constructor config carries the LRU cap (the 256-channel default
cap bounds fan-out — ADR-010's posture; the cap is config, not
wire).
- Module doc: names the ADRs (008/009), the per-target-ACL
benefit, the readiness contract, the framing-once invariant,
the backpressure bound.
3. **Tests** (`tests/composed-egress.rs`): the POC #2 shape end to
end over channels — a tunnel-producing peer (the alktunnels
registry shape, `register_tunnel_direct_openable`), the composed
egress against it, datagram round-trips through the tunnel
(multiple destinations → multiple tunnels; the LRU eviction at
cap pinned by test); the readiness contract re-pinned (park-then-
datagram, no spin); the closed-queue → association-end shape;
framing-once (no double prefix — the POC #2 bug class).
## Acceptance Criteria
- [ ] `cargo test` passes; `cargo test --all-features` passes
- [ ] `cargo check --features composed --target wasm32-unknown-
unknown` clean — the composed graph is wasm-clean (hard gate)
- [ ] `cargo clippy --all-targets --all-features -- -D warnings`
clean; `cargo fmt --check` clean
- [ ] `alktunnels` pinned `"0.2.0"`; the dep rides `composed` alone
(default graph unchanged — `cargo tree` spot-check)
- [ ] The lazy table + LRU eviction land per the POC shape — pinned
by test
- [ ] The readiness contract honored (real async park; closed queue
→ `Err`, never a hot `Ok(None)`) — pinned by test
- [ ] Framing-once (replies are inner bytes; no double prefix) —
pinned by test
- [ ] Per-datagram send backpressure documented on the constructor
## References
- docs/architecture/decisions/008-provider-defaults.md (the composed
variants), 009-feature-gates.md, 003 (the handle swap), 010 (the
cap), 012 (resolution)
- docs/architecture/producer.md §Wiring (`composed::direct_egress`),
§The Three Seams
- POC reference: `/workspace/alksocks-udp-poc/src/egress.rs`
(`ComposedEgress` — the table, the relay tasks, the closed-queue
posture)
- Landed surfaces: `src/egress.rs` (the seam contracts + the
owner-wrapper posture), alktunnels 0.2.0
(`TunnelSession::open_direct`, `TakenHalves`,
`register_tunnel_direct_openable`)
## Notes
> To be filled by implementation agent
## Summary
> To be filled on completion
+130
View File
@@ -0,0 +1,130 @@
---
id: implementation/door-tcp
name: Front doors — the TCP door (raw-pipe + RFC 1929 cred-bearing) and the BIND door
status: pending
depends_on: [implementation/local-providers]
scope: moderate
risk: medium
impact: component
level: implementation
---
## Description
The consumer's TCP front door (feature `local`): the ssh `-D` local
exposure — a real `socks5://127.0.0.1:port` endpoint that vanilla
clients (curl, browsers, tun2proxy) point at. Two variants (ADR-005),
plus the BIND door's reply#2 fidelity. The door is the
vanilla↔ACL boundary; the RFC codec lives only here.
Deliverables:
1. **`src/door.rs`** (feature `local`) — the door surface per
consumer.md §The Front Doors + overview.md §Module Map:
- **The NoAuth door** (POC #1's raw-pipe shape): bind a real
`TcpListener`; per accepted connection, pipe it raw onto a
channel `BiStream` (`copy_bidirectional`, **zero protocol
translation**). The producer runs the whole RFC conversation
through the pipe — the door adds nothing. This is the
loopback-only / wrapper-aware shape.
- **The cred-bearing door** (RFC 1929): the door terminates the
local RFC conversation itself — `accept_password_auth`-shaped
(the forked surface: `Socks5ServerProtocol::start` →
`negotiate_auth(&[PasswordAuthentication])` →
`read_username_password` → check → `accept`/`reject`), then
maps the accepted credential through the **static config
table** (`user:pass →` channel identity/endpoint selection,
ADR-005) and **re-originates the SOCKS conversation in-band**
via the consumer sessions (`Socks5ChannelSession::connect` /
`BindSession::bind`), piping the vanilla client's post-auth
bytes onto the session's pipe. **Do NOT pipe raw after auth**:
the vanilla client's command bytes were consumed by the door's
own RFC 1929 half; the in-band path re-speaks them from the
parsed target. The SOCKS password never crosses the channel as
a SOCKS password; the channel identity never reaches the
vanilla client.
- **The identity mechanism**: the credential→identity mapping
yields an alkcall `Identity` (scopes included); the door opens
the producer channel with it. The landed consumer sessions take
`&ChannelClient` — the door's per-credential identity rides the
open-op call's `auth_token` payload (CF-005 precedence (1):
payload `auth_token` → `identity_provider` resolution) or a
per-credential `ChannelClient` — decide at implementation
against the landed alkcall 0.8.0 surface
(`CallConnection::call_with_payload` carries `auth_token`;
`ServingConfig.identity_provider` resolves it producer-side)
and record the choice in Notes. The door checks credentials
itself (no forked `AuthMethod` impl needed — ADR-005).
- **The BIND door**: same listener shape; a vanilla BIND
conversation rides `BindSession` (reply#1's advertised address
passes through to the vanilla client unchanged — the
conversation is the client's own; the door adds nothing,
consumer.md §The BIND door's reply#2 fidelity). The BIND door
rides the same door variant split (NoAuth raw-pipe vs
cred-bearing re-origination).
- **The accept path owns the idle defense** (ADR-014): an
accepted connection that sends no first byte within a
configured window is closed door-side (an accepted socket is
not a channel — the cap regime does not bound it); plus
per-credential/per-peer session caps and accept-queue bounds as
constructor config. Keep the v1 shape simple: an idle-first-byte
timeout + a max-concurrent-sessions cap are the two knobs.
- **Door variant selection** is constructor config (ADR-005).
- Builder shape per consumer.md §Wiring: `FrontDoor::builder()
.credentials(cred_table).bind("127.0.0.1:1080").build()` →
`serve(channel_client)` (the NoAuth door omits `.credentials`).
`serve` runs the accept loop; per-connection failures are
logged + isolated (the loop continues — the alktunnels
per-accept isolation posture).
2. **Tests** (`tests/door-tcp.rs`): a vanilla RFC 1928 client (the
differential-suite raw-client pattern) through the NoAuth door →
CONNECT round-trip end to end (the POC #1 curl shape); the
cred-bearing door: good creds → RFC 1929 success → CONNECT
round-trip; bad creds → RFC 1929 rejection, no channel opened
(assert via the identity witness / channel count); the BIND door:
a vanilla BIND conversation through the door (the POC #5 door
shape); the idle-first-byte timeout closes a silent connection;
the session cap refuses beyond the bound; per-connection failure
isolation (one dead client doesn't kill the door).
## 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 (door code is `local`-gated)
- [ ] The NoAuth door is a literal raw pipe (zero protocol
translation) — pinned by test
- [ ] The cred-bearing door terminates RFC 1929 locally and
re-originates in-band (never pipes raw post-auth) — pinned by
test (a vanilla client's command bytes must not reach the
producer as greeting bytes)
- [ ] The credential→identity mapping is the static config table
(ADR-005); the password never crosses the channel as a SOCKS
password — pinned by test (the producer's establisher witness
sees the mapped identity, never the password)
- [ ] The idle-first-byte timeout + session cap land as constructor
config — pinned by tests
- [ ] Per-connection failure isolation — pinned by test
## References
- docs/architecture/decisions/005-auth-split-by-door.md (the door
auth posture), 014-idle-timeout-posture.md (the door's accept-path
defense), 003 (the RFC codec at doors only)
- docs/architecture/consumer.md §The Front Doors, §Wiring
- docs/architecture/overview.md §Module Map (`door.rs`)
- POC references: `/workspace/alksocks-connect-poc/tests/
connect_poc.rs` (`front_door_raw_pipe_vanilla_client`), `/workspace/
alksocks-bind-poc/tests/bind_poc.rs` (`bind_vanilla_front_door`)
- Landed surfaces: `src/consumer.rs` (the session types),
`src/rfc1928/server.rs` (`accept_password_auth` — the forked
cred-bearing shape), alkcall CF-005 (`auth_token` precedence)
## Notes
> To be filled by implementation agent
## Summary
> To be filled on completion
+91
View File
@@ -0,0 +1,91 @@
---
id: implementation/door-udp
name: Front doors — the UDP door (the RFC codec boundary)
status: pending
depends_on: [implementation/door-tcp]
scope: narrow
risk: medium
impact: component
level: implementation
---
## Description
The consumer's UDP front door (feature `local`): a real UDP socket
exposing an ASSOCIATE association to vanilla clients — the POC #2
test-8 shape. The RFC UDP-header codec lives ONLY at this boundary
(ADR-003); the in-band wire carries `{addr, data}` frames.
Deliverables:
1. **`src/door.rs` additions** (feature `local`) — the UDP door:
- Bind a real UDP socket (the relay socket — the only place a
real address exists; the vanilla client `sendto()`s it).
- Open the association in-band via `AssociateSession::associate`
(the reply's sentinel BND.ADDR is received and never used —
the handle is the channel, ADR-003).
- **The datagram pump** (two pumps, joined — the ADR-050
two-pump contract): socket→channel decodes the RFC header
(`parse_udp_request`), **FRAG ≠ 0 → refuse** (drop the
datagram; the v1 posture — fragmentation cannot be expressed
in-band), sends `{addr, data}` in-band
(`send_datagram`); channel→socket encodes the reply header
(`new_udp_header`) and sends the RFC-header'd datagram back to
the client's source address. Zero payload translation.
- **Association lifetime = channel lifetime** (ADR-003): the
door's association ends when the channel ends; the door's UDP
socket closes with it. A per-client idle bound (no datagrams
within a window → close the association) is the door-side
defense (ADR-014) — constructor config, mirroring the TCP
door's knobs.
- The door's bind address is constructor config (consumer.md:
"the UDP door's bind address is assembly-layer configuration").
- Surface shape: follow the TCP door's builder (`FrontDoor`
additions or a sibling `UdpFrontDoor` — decide at
implementation against the landed `door.rs` shape and record
in Notes; the spec leaves the exact constructor shape open).
2. **Tests** (`tests/door-udp.rs`): the POC #2 test-8 shape end to
end — a real UDP echo target, the producer with `local::udp_
egress()`, the door, a vanilla RFC-header'd datagram in → the
echo back RFC-header'd (the vanilla client simulated at the RFC
boundary); FRAG ≠ 0 refused (dropped, association stays healthy);
the empty-datagram invariant through the door (a zero-length
payload round-trips, never EOF — F-2); the idle bound closes an
idle association; a fresh association succeeds after teardown.
## 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 (door code is `local`-gated)
- [ ] The RFC UDP-header codec appears only at the door boundary
(in-band stays `{addr, data}` frames) — pinned by test
- [ ] FRAG ≠ 0 refused at the door — pinned by test
- [ ] The empty datagram (zero-length payload) round-trips through
the door, never EOF — pinned by test (the F-2 invariant at the
vanilla boundary)
- [ ] Association lifetime = channel lifetime; teardown leaves the
connection healthy — pinned by test
- [ ] The idle bound is constructor config — pinned by test
## References
- docs/architecture/decisions/003-udp-handle-swap.md (the RFC codec
at doors only), 014-idle-timeout-posture.md
- docs/architecture/consumer.md §The Front Doors (the UDP door),
§Wiring
- POC reference: `/workspace/alksocks-udp-poc/tests/udp_poc.rs`
(`udp_front_door_vanilla_client_round_trip` — the pump shape)
- Landed surfaces: `src/consumer.rs` (`AssociateSession` —
`send_datagram`/`recv_datagram`/`split`), `src/rfc1928/mod.rs`
(`new_udp_header`/`parse_udp_request`), `src/wire.rs` (the frame
codec)
## Notes
> To be filled by implementation agent
## Summary
> To be filled on completion
@@ -0,0 +1,93 @@
---
id: implementation/local-backend-producer
name: Producer-side local backend — the producer's front door (vanilla clients → a locally-served SOCKS5 resource)
status: pending
depends_on: [implementation/local-providers]
scope: narrow
risk: low
impact: component
level: 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
> To be filled by implementation agent
## Summary
> To be filled on completion
@@ -0,0 +1,95 @@
---
id: review-assembly-layer
name: Review — the assembly layer (the batch-2 gate)
status: pending
depends_on: [implementation/assembly-integration-suite]
scope: narrow
risk: low
impact: project
level: review
---
## Description
Review checkpoint closing batch 2 (the assembly layer: `local`
providers, the front doors, the producer-side local backend,
`composed` providers). After this gate the crate is feature-complete
against the Phase 1 spec set; the remaining work is release polish
(publish prep, the OQ-SK2-08 tuning note).
Review against the architecture, in this order:
1. **The never-binds posture** (the crate's defining requirement):
the protocol layer still binds nothing — every kernel bind lives
in `local`-gated code, is an explicit constructor decision, and
the default crate's dependency graph carries no `tokio/net`
(`cargo tree` spot-check). The doors' binds are the doors' own
(assembly-layer, ADR-009).
2. **Seam-contract compliance by the providers** (ADR-004): the
readiness contract honored by both egress providers (real async
parks; no hot `Ok(None)`); ownership rides the handle
structurally (the composed owner-wrapper inside the payloads; no
`_` bindings of owning values); the accept future owns its
resources end-to-end (both accept variants); framing-once (the
providers speak inner bytes; no double prefix).
3. **Door fidelity** (ADR-005/ADR-003): the NoAuth door is a literal
raw pipe; the cred-bearing door terminates RFC 1929 locally and
re-originates in-band (never pipes raw post-auth); the password
never crosses the channel as a SOCKS password; the RFC UDP-header
codec appears only at the door boundary; FRAG ≠ 0 refused.
4. **The graduation's shape** (ADR-007 variant 2): the composed
accept advertises the real `bound` and the real `peer` — no
expected-addr stand-in; the POC's superseded workaround is not
resurrected.
5. **Convention compliance** (AGENTS.md): no `unwrap`/`expect`/
panics outside tests; `thiserror` surfaces; no comments in code
except doc comments + pinned non-obvious-correctness notes;
naming; module structure (feature-gated backend modules never
imported from the protocol/adapter/client modules — convention
15's last line); producer/consumer vocabulary.
6. **Feature-gate hygiene** (ADR-009): `local` brings only
`tokio/net` (+ `socket2` if justified); `composed` brings only
alktunnels; the default graph unchanged; the wasm checks green
for default + composed; `local` on wasm fails to build (the
alktty posture — verify the failure is the expected one).
7. **POC-fidelity spot-check**: for two or three of the trickiest
assembly behaviors (the composed egress's LRU eviction + closed-
queue posture, the cred-bearing door's re-origination, the
bind-first correlation), diff the crate's behavior against the
POC crates' findings — the POCs are the empirical record; drift
is a finding (except where an ADR supersedes the POC's shape —
the bind-first graduation is the known one).
8. **Docs sync**: the specs match what landed (the integration
task's reconciliation verified; any residue is a finding).
## Notes
> To be filled by the review agent — see the Summary and the Findings.
## Acceptance Criteria
- [ ] The never-binds posture verified (protocol layer + default
graph)
- [ ] Seam-contract compliance verified for every provider
- [ ] Door fidelity verified (both variants + the UDP boundary)
- [ ] The graduation's shape verified (no stand-ins)
- [ ] Convention compliance swept
- [ ] Feature-gate hygiene verified (all four verification targets)
- [ ] POC-fidelity spot-check of the trickiest assembly behaviors
- [ ] Docs sync verified; findings recorded; the gate to release
polish opens
## References
- docs/architecture/overview.md, wire.md, producer.md, consumer.md
(the specs under review)
- docs/architecture/decisions/003/004/005/006/007/008/009/010/012/014
- POC crates: `/workspace/alksocks-{connect,udp,bind}-poc` (the
fidelity sources)
- AGENTS.md (conventions 1–17; the verification commands)
- The batch-1 review precedent:
`tasks/implementation/review-protocol-core.md`
## Summary
> To be filled on completion