--- id: implementation/core-test-harness name: "Core: integration test harness (duplex transport + identities + in-process seams)" status: completed depends_on: [implementation/core-seam-types, implementation/core-params-errors] scope: moderate risk: medium impact: phase level: implementation --- ## Description Build `tests/harness.rs`, the producer↔consumer wiring over a `tokio::io::duplex` carrying the channels 8-byte chunk format, so every subsequent core task tests end-to-end without the `local` feature. This is the alkcall-0.8.0-first-consumption task: the POCs ran 0.7.1; the harness proves the 0.8.0 establishment surface (`register_openable_with_establisher`, `Establishment`, `ChannelClient::open_channel`) works for this crate's shape before the producer task builds on it. Template: `/workspace/@alkdev/alktunnels/tests/harness.rs` (the sibling's harness, same topology, adapted to this crate's seams). Topology (the alktunnels harness shape): - **producer** (connect side of the channels connection; runs `ChannelClient::from_connection_with_serving`): serves the socks5 open op + generic channel ops on its serving registry. Its manager allocates the channel IDs (`ChannelSide::Connect`, since the serving side allocates and the initiator adopts). - **consumer** (accept side): the `ChannelsAdapter` + capturing install hook. The hook captures the consumer's channel-0 `CallConnection` and `ChannelManager`. - **The POC #1 wiring requirement is load-bearing here** (producer.md §Harness wiring requirement): the accept-side install hook must call `channel0_conn.set_identity(resolved)` before building the `CallConnection`; without it, scope-gated opens fail closed with `FORBIDDEN "authentication required"` (correct behavior, implicit wiring). The harness makes the requirement explicit and tested. Deliverables: 1. **Identities**: `consumer_identity()` (`socks5:open` only), `bind_identity()` (`socks5:bind` only, the command-gate probe), `both_scopes_identity()` (both grants, explicitly and never implied, ADR-006), plus a `TestIdProvider` resolving them (the alktunnels harness shape). 2. **In-process seam closures** (no sockets; the protocol layer is substrate-agnostic, so the harness dials in-process): - `echo_dial()`: a `DialFn` returning duplex-halves wired to an in-process echo server (the POC #1 shape); plus `refusing_dial()` (a failing dial for the refusal-mapping tests). - `echo_egress()`: an `EgressFactory` whose egress echoes datagrams back (the POC #2 shape, readiness-contract-clean: parks on a channel `recv()`, `Pending` when nothing is ready). - `echo_accept()`: a `BindAcceptFactory` whose accept resolves immediately with in-process halves (the POC #5 shape); plus a failing variant. 3. **The producer registration helper**: a `spawn_producer(...)` function wiring `register_socks5_openable` (or its task-6 successor) with the harness seams, the single place the producer-side wiring lives, so the per-command tasks only write tests. 4. **A raw RFC client helper**: a hand-rolled minimal RFC 1928 client over any `AsyncRead + AsyncWrite` (greeting → method selection → command with target → reply read) following the differential suite's established pattern, so producer tasks test without depending on the consumer session types (which land later). 5. **One smoke test** proving the full loop: harness up → producer registered → consumer opens a channel → raw RFC CONNECT conversation → echo round-trip. (This test needs a minimal CONNECT producer path; if the producer task hasn't landed, stub the handler with the POC #1 `serve_socks5_over` shape inline in the harness, and the producer task then replaces it. Alternatively sequence this task to land immediately before the producer task and let the smoke test be the producer task's first test; the implementer's judgment; the acceptance bar is the harness compiling with the identities + seams + helpers tested.) ## Acceptance Criteria - [x] Harness topology works over alkcall 0.8.0 (the establishment surface consumed successfully; the first-consumption risk retired) - [x] Identity wiring explicit: `set_identity` in the install hook; a scope-less identity gets `FORBIDDEN` (fails closed), tested - [x] All three identities + all in-process seam closures unit-tested - [x] The raw RFC client helper round-trips a greeting + command + reply against a hand-rolled server over a duplex - [x] No `local` feature, no sockets; everything in-process - [x] `cargo test` green; clippy/fmt clean; both wasm checks clean (the harness is dev-only; verify it stays out of the wasm build) ## References - Template: `/workspace/@alkdev/alktunnels/tests/harness.rs` (the topology + identity shapes) - POC harnesses: `/workspace/alksocks-connect-poc/src/harness.rs`, `/workspace/alksocks-udp-poc/src/harness.rs`, `/workspace/alksocks-bind-poc/src/harness.rs` - docs/architecture/producer.md §Harness wiring requirement (the `set_identity` requirement) - docs/architecture/decisions/006-access-control.md (the scope identities) ## Notes > Filled by the implementation agent (2026-09-23). - `tests/harness.rs`: the producer↔consumer wiring over one `tokio::io::duplex` carrying the channels 8-byte chunk format, in the task's topology: **producer = connect side** (`ChannelClient::from_connection_with_serving`; its manager allocates the odd channel IDs), **consumer = accept side** (the `ChannelsAdapter` + capturing install hook; the hook captures the consumer's channel-0 `CallConnection` + `ChannelManager`). - The load-bearing POC #1 wiring requirement is explicit and tested: the hook calls `channel0_conn.set_identity(resolved)` before building the `CallConnection`; the transport identity is set on the dialing (producer) connection before `from_connection_with_serving` (CF-005 (b) primary path). The scope-less identity test asserts the `FORBIDDEN` code and that no channel survives on either side (fail-closed). - Identities: `consumer_identity()` (`socks5:open` only), `bind_identity()` (`socks5:bind` only), `both_scopes_identity()` (both, explicit, per ADR-006), `TestIdProvider` + `TEST_AUTH_TOKEN` (the token-resolver path, unit-tested both directions). - In-process seams (no sockets, no `local`): `echo_dial()` (echo-per-dial, exercised through `pump_bidi`, the producer data (echo-per-dial, exercised through `pump_bidi`, the producer data plane's shape), `refusing_dial(err)` (variant-preserving per-call reconstruction, as the seam errors are not `Clone`), `echo_egress()` (a `ChanEgress` whose `next_reply` parks on `mpsc::UnboundedReceiver::recv()`: `Pending` when idle, with the readiness contract verified with a 50ms timeout probe), `refusing_egress(err)`, `echo_accept()` (reply#1 advertises a static in-process BND.ADDR, `ECHO_BIND_PORT` 2121; the accept future resolves immediately with echo-pipe halves), `refusing_accept_factory(err)` (pre-reply#1) and `refusing_accept(err)` (post-reply#1). - `register_socks5_openable_minimal` + `socks5_session_establisher`: the producer-side wiring helper over alkcall 0.8.0's `ChannelCore::register_openable_with_establisher` (spec from `socks5_open_spec()`, `Establishment::default()` for a session gate only; the dial is in-stream). The establisher witnesses the per-call opener identity id (CF-006) into an `IdentityWitness`, asserted end-to-end in the smoke test. - `RegisterMode` (`MinimalConnect(dial)` / `NoSocks5`) controls what `wire_with` registers post-hoc (W2) on the producer's serving registry; generic channel ops always register. - The smoke test's producer path is the minimal CONNECT stand-in (`make_minimal_connect_handler` → `serve_connect_minimal`: the forked typestate's `accept_no_auth` → `read_command` → CONNECT-only dispatch → the injected `DialFn` → `reply_success` → `pump_bidi` inline, R-02). The producer task replaces this; the registration shape (spec/establisher/handler/auth/timeout) stays. - `RawRfcClient`: hand-rolled greeting (`05 01 00` → `05 00`) + request encoder (all three commands, all address forms via `TargetAddr::to_be_bytes()`) + reply reader (v4/v6/domain BND.ADDR forms; returns `(code, addr_bytes)`). Round-trip-tested against a hand-rolled byte-level server over a duplex, success + refusal reply codes; the harness is producer-independent (no consumer session types). - Wire note: the smoke test calls the open op via `consumer_call.call(OP_SOCKS5_OPEN, Socks5Params::as_json())` and adopts on `consumer_manager` (the alktunnels `wire_with` + `open_reverse_channel` shape) rather than `ChannelClient::open_channel`: in this topology the open op is served by the producer's registry and reached from the accept side's channel-0 call surface; `open_channel` remains the consumer-session surface for the consumer task's session types. - The harness is `tests/harness.rs` only (dev-only), verified out of the wasm build by the targeted checks below. ## Summary > Filled on completion (2026-09-23). Built `tests/harness.rs`, the integration test harness: the producer↔consumer topology over one `tokio::io::duplex` (producer = connect side/serving; consumer = accept side/adapter + capturing hook), the alkcall 0.8.0 establishment surface consumed for the first time by this crate (`register_openable_with_establisher`, `Establishment::default`, the consumer-side call + adopt dance), and the first-consumption risk retired with 14 green tests. - The three identities + `TestIdProvider` (all unit-tested). - All in-process seam closures: `echo_dial`/`refusing_dial` (variant- preserving), `echo_egress` (readiness contract verified: a 50ms timeout probe proves `next_reply` parks, never a hot `Ok(None)`) / `refusing_egress`, `echo_accept` (immediate resolve + echo) / `refusing_accept_factory` / `refusing_accept` (two-stage refusal shapes, mapping-table-verified through `to_reply_error()`). - The producer registration helper (`register_socks5_openable_minimal` + `socks5_session_establisher` + `RegisterMode`), the single place the producer-side wiring lives; the CF-006 identity witness rides it and is asserted. - `RawRfcClient`: the raw RFC 1928 client helper over any `AsyncRead + AsyncWrite`, round-tripped against a hand-rolled byte-level server (success + refusal replies). - The smoke test: harness up → producer registered → consumer open + adopt → raw RFC CONNECT → echo round-trip through `pump_bidi`, with the establisher witness asserted mid-flight. Plus the fail-closed probe (scope-less identity → `FORBIDDEN`, no channel anywhere) and the odd-ID allocation/adopt sanity probe. Verification: `cargo test` (127 green: 76 lib + 14 harness + 37 rfc1928-differential), `cargo test --all-features` green, `cargo clippy --all-targets -- -D warnings` clean, `cargo fmt --check` clean, `cargo doc --no-deps` zero warnings, both wasm checks (`cargo check` + `cargo clippy` on `wasm32-unknown-unknown`) clean, and the harness stays out of the wasm build.