tasks: the batch-2 decomposition — the remaining assembly-layer tasks
This commit is contained in:
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user