assembly: the cross-cutting integration suite + docs sync (batch 2 closes)

- tests/assembly-integration.rs (7 tests, local + composed): the full
  -D story (vanilla cred-bearing client -> TCP door + UDP door ->
  producer w/ local providers -> real echo targets; CONNECT + BIND +
  ASSOCIATE live, one-command-per-connection, teardown + fresh
  sessions), the composed story (direct_egress + bindfirst_accept +
  real TCP dial; all three commands; the lazy-table dial-count
  witness), mixed assembly (channels-native + door clients on one
  registration; composed seams beside native sessions), the assembly
  teardown matrix (UDP-door idle bound; channel reaped; fresh
  association after), the producer door driving all three commands,
  and the -D domain-target pin (resolution at the producer's seam,
  ADR-012).
- Fix the drift the suite surfaced: ComposedEgress::next_reply ended
  fresh (never-opened-table) associations at birth — the ended-relay
  Err now fires only once a tunnel existed and every relay ended; the
  fresh table parks (the readiness contract). Regression-pinned in
  tests/composed-egress.rs (fresh_association_parks_before_any_
  tunnel_is_opened).
- Harness: the composed-accept machinery single-sourced into
  tests/harness.rs (the composed suites' shapes + the five-gate
  composed identity); composed-accept.rs consumes the shared shapes.
- Docs sync: README Current State (the assembly layer landed; both
  suites named), lib.rs crate docs (the assembly-layer paragraph),
  consumer.md (the TokenClient door-only contract + the
  one-command-per-connection posture reconciled).
- Task assembly-integration-suite: completed; Notes + Summary filled
  (the local-on-wasm failure observed: mio's unsupported-wasm error).

Verification: cargo test (375), cargo test --all-features (615),
clippy --all-targets -D warnings (default + all-features), fmt --check,
doc --no-deps (0 warnings), both wasm checks (default + composed),
local-on-wasm fails as designed.
This commit is contained in:
glm-5.3-flash committed 2026-09-25 23:03:13 +00:00
1 parent 4e1673eefc
commit 1addedfdd0
9 files changed
+1921 -205

No files matched your search

+15 -2
View File
@@ -194,6 +194,14 @@ pub struct ComposedEgress {
/// The factory `Sender` alone does not close the queue (the
/// egress holds it) — the count is the design.
live_relays: usize,
/// Whether any tunnel has ever been opened. The lazy table starts
/// empty: a fresh association's `next_reply` parks until the first
/// `send` opens a tunnel (the producer's stage selects the channel
/// branch first on a quiescent association — the association
/// serves until a datagram arrives or the channel EOFs). The
/// ended-relay `Err` fires only once at least one tunnel existed
/// and every relay has ended (the closed-queue condition).
ever_opened: bool,
}
/// The shared inbound queue's message: a tunnel's datagram (keyed by
@@ -281,6 +289,7 @@ impl ComposedEgress {
inbound: rx,
inbound_tx: tx,
live_relays: 0,
ever_opened: false,
}
}
@@ -306,6 +315,7 @@ impl ComposedEgress {
// bug). The relay task owns the bare read half.
tokio::spawn(relay_read(taken.read, self.inbound_tx.clone(), key.clone()));
self.live_relays += 1;
self.ever_opened = true;
self.entries.insert(
key.clone(),
TableEntry {
@@ -368,11 +378,14 @@ impl Egress for ComposedEgress {
Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => break,
}
}
if self.live_relays == 0 {
if self.ever_opened && self.live_relays == 0 {
// Every relay ended (the tunnels ended): a closed
// relay set must NOT spin the stage's select (an
// instantly-ready None would starve the channel
// branch) — end the association instead.
// branch) — end the association instead. A fresh
// association (no tunnel ever opened) parks here —
// the channel branch governs until the first
// datagram arrives.
return Err(EgressError::General("all tunnel relays ended".into()));
}
match self.inbound.recv().await {
+20 -11
View File
@@ -40,21 +40,30 @@
//! sessions — [`Socks5ChannelSession`] (CONNECT),
//! [`AssociateSession`] (the framed datagram stage),
//! [`BindSession`] (the reply#1/reply#2 reader) — each owning its
//! channel's teardown; no `Clone`.
//! channel's teardown; no `Clone`. The cred-bearing front door's
//! re-origination rides the token-carrying surface (`TokenClient`
//! + the `*_with_token` constructor twins).
//!
//! `local` (the socket-backed assembly-layer providers —
//! [`crate::local::tcp_dial`] / [`crate::local::udp_egress`] /
//! [`crate::local::bind_accept`], ADR-008/009) is feature-gated: the
//! default crate is protocol-only and wasm-clean by construction, and
//! the composed providers ride `composed`
//! ([`crate::composed::direct_egress`] — the alktunnels direct-op
//! egress, ADR-008/009).
//! The assembly layer (feature-gated, ADR-008/009): `local` (the
//! socket-backed providers — `local::tcp_dial` / `local::udp_egress`
//! / `local::bind_accept` — plus the producer-side
//! `local::ProducerDoor`) and `door` (the consumer's front doors —
//! the NoAuth raw-pipe + RFC 1929 cred-bearing `FrontDoor`, the
//! `UdpFrontDoor` with the RFC codec at the door boundary only,
//! ADR-005/003/014) ride `local`; the composed providers
//! (`composed::direct_egress` — the alktunnels direct-op egress,
//! `composed::bindfirst_accept` — the bind-first accept,
//! ADR-007/008) ride `composed`. The default crate is protocol-only
//! and wasm-clean by construction.
//!
//! Architecture: `docs/architecture/overview.md` (spec set:
//! wire.md, producer.md, consumer.md, rfc1928.md); the cross-cutting
//! behavior is pinned end to end by `tests/core-integration.rs`
//! (one registration, all three commands; concurrency; the teardown
//! matrix; the identity/scope matrix; the readiness contract).
//! behavior is pinned end to end by `tests/core-integration.rs` (the
//! protocol core: one registration, all three commands; concurrency;
//! the teardown matrix; the identity/scope matrix; the readiness
//! contract) and `tests/assembly-integration.rs` (the assembly
//! layer: the full `-D` story through the doors, the composed
//! variant set, mixed assembly, the assembly teardown matrix).
#![forbid(unsafe_code)]