composed: the bind-first accept — the listener on a listen resource

Lands the composed accept (ADR-007 variant 2 / ADR-008): the BIND
accept whose listener lives on an alktunnels 0.2.0 bind-first listen
resource, at the graduation's vanilla fidelity.

- `composed::bindfirst_accept` — the BindAcceptFactory: opens the
  listen channel (reply#1 advertises the open-op reply's real
  `bound`, the 0.8.0 reply projection — no expected-addr stand-in),
  inserts the minted `listen` token into the correlation registry,
  and returns the accept future parked on the token's oneshot.
- `composed::register_forwarded_serving` — the forwarded-op serving
  half on the SOCKS5 producer's connection: the shared handler
  correlates by token and owns the middle pump (either-EOF
  completion; a three-party per-direction shutdown cascade would
  deadlock).
- The Sync-bound wrinkle: `SyncHalves`/`OwnedWrite` (mutex
  Sync-proof, no unsafe) over the forwarded halves; the owner
  (`ForwardedOwner`, the TunnelListener) rides inside the write
  payload — dropping the handle drops the session, halves, and
  listener together (teardown structural, compiler-enforced).
- `tests/composed-accept.rs`: the full story (real bound, real peer,
  raw pass-through round trip), client-gone teardown drop-witness,
  distinct tokens for concurrent BINDs, per-accept isolation of a
  FORBIDDEN forwarded open, late-forwarded correlation miss.
- The producer-side adopted forwarded entry rides the per-adopter
  contract (transport-EOF reap), as upstream's bind-first suites
  concede; the no-leak probe reflects that shape.

Verification: cargo test (default + --all-features), wasm
check/clippy (hard gate), clippy --all-targets --all-features -D
warnings, fmt --check — all clean.
This commit is contained in:
glm-5.3-flash committed 2026-09-25 22:13:17 +00:00
1 parent 3128d2bd4f
commit 4e1673eefc
5 files changed
+1284 -24

No files matched your search

+71 -3
View File
@@ -1,7 +1,7 @@
---
id: implementation/composed-accept
name: Composed — the bind-first accept (the listener on a listen resource)
status: pending
status: completed
depends_on: [implementation/composed-egress]
scope: moderate
risk: medium
@@ -115,8 +115,76 @@ Deliverables:
## Notes
> To be filled by implementation agent
- **The correlation shape** (the design center): a
`CorrelationRegistry` (`Arc<tokio::sync::Mutex<HashMap<u32,
oneshot::Sender<ForwardedSession>>>>`) keyed on the `listen` token.
The factory inserts the token → oneshot right after
`TunnelListener::open`; the provider's shared forwarded handler
(`make_forwarded_handler`, registered via
`register_forwarded_serving` on the SOCKS5 producer's connection)
removes the entry on the arriving forwarded open and delivers
`ForwardedSession { far duplex halves, peer }`. A dropped receiver
(client-gone) or a miss makes the handler drop the forwarded
channel stream — teardown-at-birth, R-02 (the inline `pump_bidi`
await in `register_openable`'s wrapper then reaps the entry).
- **The Sync-bound wrinkle**: alktunnels' halves are `Send + Unpin`
only; the landed `BindTargetHandle` payloads carry `+ Sync`. Two
adapters pin the no-unsafe Sync proof (`Mutex<T: Send>` is `Sync`;
contention never occurs — one pump task at a time): `SyncHalves`
(read half) and `OwnedWrite` (write half + the owner inside).
- **The owner-wrapper rides inside the payloads**
(`ForwardedOwner`): the write payload owns the `TunnelListener` —
dropping the handle drops the listener → the listen channel EOFs →
the far teardown cascades (teardown structural,
compiler-enforced). The edition-2021 disjoint-capture trap (an
earlier draft captured `listener.channel_id`, dropping the
listener at the factory's return — EOF at birth) is why the whole
struct is rebound into the accept future.
- **The three-party bridge pump**: the forwarded handler bridges the
forwarded `BiStream` to a local duplex pair whose far end travels
into the accept future's payloads. The bridge completes on EITHER
direction's EOF (a `pump_bidi`-then-drop, not a per-direction
shutdown cascade — a three-party half-close cycle would deadlock:
each pump's completion-shutdown needs the next hop's EOF, and the
chain closes on itself). Dropping everything EOFs the SOCKS5
producer-side pump, ends the per-accept pump, and tears the
forwarded channel down.
- **Test concession (producer-side adopted forwarded entry)**: the
producer's `open_forwarded` caller adopts the opener-allocated
forwarded channel id on the producer-side manager; that entry's
reap rides the per-adopter contract + transport EOF, NOT the
listener's teardown (alktunnels ADR-005; upstream's own bind-first
suites make the same concession — `producer_bindfirst.rs`
`no_stray_channels` documents the listen channel, and the
forwarded entries are adopted sessions owned by their adopter).
`assert_only_channels` therefore treats odd producer-side ids as
the adopted-forwarded shape.
- **Forwarded-open failure semantics**: a FORBIDDEN forwarded open is
per-accept isolated AT THE TUNNEL LAYER (the producer's accept loop
drops that connection and keeps serving; the alktunnels
producer-suite pin). The SOCKS5 accept future stays parked — the
SOCKS5 producer never learns of the tunnel-layer failure — so no
reply#2 arrives; the quiet wait is unaffected. Pinned by test.
## Summary
> To be filled on completion
Landed `composed::bindfirst_accept` + `composed::register_forwarded_serving`
(the composed accept, ADR-007 variant 2 / ADR-008) on the alktunnels 0.2.0
bind-first listen resource, at the graduation's vanilla fidelity: reply#1
advertises the open-op reply's real `bound` (the alkcall 0.8.0 reply
projection; no expected-addr stand-in), reply#2 carries the forwarded op's
real `peer` param. The correlation registry (token → oneshot) is assembly
bookkeeping; the provider owns the forwarded-op serving half, the token→waiter
map, and the per-forwarded middle pump (either-EOF completion shape). The
Sync-bound wrinkle is met by `SyncHalves`/`OwnedWrite` (mutex Sync-proof, no
unsafe) with the owner (`ForwardedOwner`, the `TunnelListener`) riding inside
the write payload — dropping the handle drops everything (teardown structural,
compiler-enforced). `tests/composed-accept.rs` pins: the full story (real
`bound`, real `peer`, raw pass-through round trip), the client-gone teardown
(drop-witness: the accept future owns the listener end-to-end; no leaked
listen channel), two concurrent BIND sessions' distinct tokens, per-accept
isolation of a FORBIDDEN forwarded open, and the late-forwarded correlation
miss dropping the channel at birth. Verified: `cargo test`, `cargo test
--all-features`, `cargo check/clippy --target wasm32-unknown-unknown` (hard
gate), `cargo clippy --all-targets --all-features -- -D warnings`,
`cargo fmt --check` — all clean.