0ceecf582b7ebc118d5c547e2c63181a00cd0808
22
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0ceecf582b |
docs: remove em dashes from repo prose and forbid them going forward
- Rewrote the remaining em dashes outside docs/reviews/ (preserved as historical record, matching the alktty precedent): tests/ (313), fuzz/ (34 plus one shell comment), tasks/ (671 plus 12 kept in fences), docs/research + sdd_process + plans (525 plus 25 in fences), docs/architecture/overview.md fence labels (13), README, CHANGELOG, AGENTS.md, fuzz/README.md (176), and the .opencode/agents prompts (110) - Rewrote fenced content too: ASCII tree and module-map label separators, Rust snippet comments, and template placeholders; wire data, JSON/BAST examples, and transcripts survive verbatim - Rewrote 6 assert/expect message literals in tests/ and 2 format strings in fuzz/shared/src; no test data changed - Added Project Convention 18: no em dashes in prose (doc comments, markdown including docs/ and tasks/, agent prompts, commit messages); fence content rule, en-dash clause rule, and the no-spaced-hyphen prohibition follow alktty's convention 15 Follows |
||
|
|
f42ce47d17 |
test(producer): the forwarded gate — gate miss/pass, residual race, scope-gate ordering (plan 003 W1)
- New tests/producer_forwarded_gate.rs (the establisher variant's pins;
the base shape's tests/producer_forwarded.rs untouched):
- gate miss: typed channel:open_failed with details.reason ==
unknown_resource, no phantom channel, the pump handler never runs
(the gate's invocation witness IS set — the miss is at the gate)
- gate pass: the base serving flow unchanged — parsed params
witnessed verbatim + full byte round-trip through the
establisher-registered variant
- residual race: gate pass + route gone at handler time → the
handler's error path reaps via teardown-at-birth (the base
correlation-miss semantics re-pinned under the established shape)
- scope-gate ordering: tunnel:forwarded-less identity is FORBIDDEN
and the gate closure is never invoked (ACL precedes the
establishment phase)
- the establisher malformed-params delta pinned: schema gates
unchanged (INVALID_INPUT); an unknown top-level field becomes a
typed channel:open_failed (reason handler_error) and the parse
precedes the gate
- No harness changes (wire_with + Topology.consumer_registry as-is)
- Verification: cargo test green (14 suites, 0 failures); clippy
--all-targets -D warnings native + wasm-target clippy clean (wasm
--all-targets cannot compile on this baseline — pre-existing,
mio/socket2 dev-deps do not target wasm; see the task Notes);
cargo fmt --check clean; cargo check --target
wasm32-unknown-unknown clean; --all-features test run green
Tasks: tunnels/corrmiss-gate-tests (status → completed)
|
||
|
|
1365272146 |
release: v0.3.0 — the dependency-major (alkcall 0.9.0)
- Cargo.toml: alkcall 0.8.1 -> 0.9.0 (crates.io, the stub-sweep
release) and 0.2.1 -> 0.3.0 (breaking dep bump + public alkcall
re-exports -> minor, matching the 0.7.1 -> 0.8.0 ride on 0.2.0)
- fuzz/shared/Cargo.toml: alkcall 0.9.0 (path-linked alktunnels must
see one alkcall version)
- src/producer.rs + tests/producer_direct_op.rs: the only code contact
with the 0.9.0 breaking surface — Establishment no longer derives
Debug; two expect_err calls -> .err().expect, one untyped
panic!("{other:?}") fall-through -> typed Err/Ok arms
- CHANGELOG: 0.3.0 section + compare links
- README: the 0.3.0 note
- docs/architecture/overview.md: dep tree alkcall 0.9.0
Verification: cargo test (136 default / 163 with local), fuzz/shared
corpus replay (0 pass, empty target set — no regressions), clippy
--all-targets -D warnings (native + wasm32-unknown-unknown), fmt
--check, wasm32 check, and cargo publish --dry-run --allow-dirty all
green
|
||
|
|
e3c6f6c9f8 |
fuzz: wave 2 — session_pumps stateful two-pump target
The wave-2 stateful target from docs/research/fuzzing.md 3.3: op sequences (arbitrary SessionSequence) over one live channels stack per exec - MuxRunner -> raw chunk stream -> the real run_demux_loop_for_client demux -> ChannelManager per side (producer Connect / consumer Accept over one duplex transport, the reverse POC's topology). The harness mirrors TunnelSession's data-plane shapes: datagram planes (codec reader + pending, the DatagramHalves mirror) and stream planes (pump_bidi against in-process peer halves, the pump_against mirror). Invariants (3.3): no-panic/no-wedge (5s reconcile join); two-pump shutdown-on-completion; lossless p2c copy count; EOF-sentinel teardown semantics (drain-before-EOF, map empties); FIFO datagram reconciliation through the real demux path (stateful framing walk, the wave-1 walk carried across ops); the 1.3 DatagramHalves.pending backlog bound (<= routed-but-unpulled remainder); decoder-state retained-bytes bound re-asserted per op. Harness-model findings (all six wave findings are harness defects, not crate bugs - the fuzzer-as-harness-checker pattern): - duplicate adopt is a legal ChannelExists no-op (documented table) - the p2c-EOF pump join is the DESIGNED half-close shape; the pump completes once the c2p source EOFs too (teardown), not before - route_payload routes into the LOCAL manager (demux direction); the producer pump writes the channel send half - the first campaign's bytes never reached the consumer; rewritten to the real protocol shape (Open allocates, session-index addressing, stateful framing model, Pull op, EOF sentinels only from teardown/sender-drop) - clear_all's return is the opener-ledger records, NOT the reaped ids (adopted channels carry no ledger entry - the alkcall 7.7 lesson re-learned); the map-empties assert is the ground truth Campaign (10-min detached, 6 runner): 228,676 execs, 32 fork jobs, oom/timeout/crash 0/0/0, exit 0. Coverage 3427 edges / 17.5k features still expanding at budget end. No crate bugs found - the two-pump contract, EOF-sentinel semantics, and the pending backlog bound are demonstrated properties. Crate addition: TunnelSession::datagram_backlog() (the DatagramHalves.pending diagnostic the 3.3 bound asserts against, in the retained_bytes() style) + a suite test pinning its rest states. Verification: main suite 136 passed; corpus replay (8 committed seeds + 2 semantic fixtures) green; clippy -D warnings + fmt clean (main, shared, wasm); wasm check clean; cargo fuzz build clean. |
||
|
|
98afb092ce |
test: G-4 unix accept loop + G-5 unnamed fail-loud guards
- G-4: drive UnixListenerHalves::accept_loop — bind_unix + temp path,
the loop exits on the queue-closed handler error after one unix
client accept (the unix parity twin of the TCP accept-loop test;
closes the largest single uncovered block, local/mod.rs 591-609).
- G-5: pin ADR-008 §3's two fail-loud sentences — bind_unix("") is
the kernel autobind (no filesystem path) so local_addr fails loud
(578-583), and a socket2 autobind client against a NAMED listener
is refused by accept_pair's unnamed-peer guard (626-633; Linux —
abstract addresses/autobind are Linux-shaped, mio maps any
abstract-namespace peer to unnamed).
Verification: cargo test --features local green (162 tests);
clippy --all-targets -D warnings clean (native + wasm32-unknown-
unknown); cargo fmt --check clean.
|
||
|
|
7991bf6ad0 |
test: G-2 graceful-close far teardown + N-2 write-happy-peer posture
- G-2: observe TunnelListener::close's explicit shutdown sentinel at the far accept dispatcher — the accept loop cancels, the pushed accept is never consumed, the serving-side listen channel reaps (ADR-008 §1's teardown contract, graceful half). - N-2 residue: pin the write-happy-peer teardown posture — a non-empty read on the idle listen stream tears the listener down (the fail-closed Ok(_) arm; driven via the raw client surface since the typed listener holds its write half idle by contract). Verification: cargo test --features local green (163 tests + 2 doc-tests); clippy --all-targets -D warnings clean (native + wasm32-unknown-unknown); cargo fmt --check clean. |
||
|
|
57a728dc4b |
fix: bind-first dispatcher reads substrate from the plan (review 002 M-1)
- BindFirstPlan gains substrate: Substrate, stamped at establisher
time from the one successful TunnelParams parse (one parse, one
source of truth; the plan-payload change is pre-consumer)
- make_bindfirst_accept_handler drops the second params re-parse and
the silent unwrap_or(Substrate::Tcp) fallback entirely — the
dispatcher reads plan.substrate; no fallback branch remains, so a
future drift cannot silently stamp Tcp into a UDP listen tunnel's
forwarded opens (the raw-vs-framed data-plane mismatch class)
- tests: forwarded_opens_carry_plan_stamped_substrate pins the plan
as the substrate's source over a real UDP bind-first wire flow
(a substrate-recording forwarded stub observes substrate: udp);
the unit downcast + establisher-flow tests pin the stamp
- docs: producer.md (the plan carries {listener, token, substrate};
the forwarded open's substrate also comes from the plan),
ADR-008 §1 (the stamp + the removed fallback)
- task review-002-m1-substrate-fallback: completed (Notes filled)
Verification: cargo test (145 pass), cargo test --all-features (153),
clippy --all-targets -D warnings (default + all-features), fmt
--check, doc --no-deps, wasm32 check + clippy -D warnings.
|
||
|
|
474844e563 |
feat: TunnelListener — the listen-flow consumer session (ADR-008 §4)
- src/consumer.rs: `TunnelListener` — the listen-flow sibling of `TunnelSession`: `open` rides `open_channel_with_reply` (the U-2 surface, alkcall 0.8.0) and projects the bind-first `bound`/`listen` reply fields via the lenient `listen_reply_fields`; `bound()` / `listen()` present them (the Option posture — None for dial/ accept-first producers and old producers). Teardown ownership (ADR-005's posture, listen-flavored): `close` (graceful — `shutdown` on the held write half, the explicit EOF sentinel, then the sync reap) and `Drop` (the held write half drops → the mux pump's implicit-EOF chunk — the far accept dispatcher's teardown signal — then the sync reap; never leaks). The held stream is the write half (`MpscSendStream`) only, IDLE and private — a listen channel has no data-plane role. No `Clone` (compile_fail doc-test). `forwarded()` decided: NOT provided — the receipt queue is assembly-side bookkeeping; the type hands the assembly the token. - src/lib.rs: re-exports `TunnelListener`; crate docs mention the listen-flow session. - src/producer.rs: doc-fix drive-by — two pre-existing broken intra-doc links (cargo doc is now 0-warning clean). - tests/harness.rs: `wire_listener_bindfirst` + `ListenerTopology` — the opener-dials shape (client + serving on the opener; bind-first serving + the call-plane forwarded-open caller on the producer). - tests/consumer_listener.rs: 5 integration tests — the bind-first gate (reply fields + forwarded round-trip through the REAL forwarded-openable registration + the teardown cascade), the Option posture on a dial producer, close/Drop reaping (the leak-free guarantee), the adopt-path pin (`adopt` gains nothing — a forwarded channel presents the ordinary session semantics), and the scope interaction (`tunnel:open`-only opener opens the listen channel; forwarded opens to it fail FORBIDDEN). - CHANGELOG: Unreleased → Added. Verification: cargo test 132 passed (157 --all-features; doc tests 2/2 incl. both compile_fail asserts), clippy --all-targets -D warnings clean (native + wasm32), fmt clean, wasm32-unknown-unknown check passes (the default crate stays wasm-clean), cargo doc --no-deps clean. |
||
|
|
514e0c1f25 |
feat: the local bind-first halves (shape 3 substrate half, ADR-008 §1)
- `bind_tcp_first` + `TcpListenerHalves::accept_pair`: the real-socket BindFn source (port 0 resolved via local_addr — the `bound` reply field's data) and the per-accept (peer, halves) pair (the peer rides accept()'s return — the forwarded op's `peer` carrier) - unix bind-first parity shipped (OQ-TN-14): `bind_unix` + `UnixListenerHalves` (local_addr / queue-feeding accept_loop / bind-first accept_pair; an unnamed peer fails loud — the forwarded `peer` param requires an address) + `bind_unix_first` - assembly conveniences: `local_bind_first()` (tcp/unix dispatch; udp fails loud — no accept on a UDP socket, the shape is not defined there, never a fake listener) + `local_bindfirst_accept_loop()` (downcasts the opaque listener; a foreign type fails loud) - direct-op DialFn reuse verified + pinned: `local_dial` shared unchanged (the UDP framed wrap composes inside the closure); `direct_udp_through_local_dial_round_trips_framed` drives a REAL udp echo through the direct op's establisher path - tests/local_bindfirst.rs: tcp e2e over real sockets (bound fidelity — the test dials the reported address; two clients → two forwarded channels pumping independently, real distinct peers), unix parity (real path in `bound`, named-peer client via socket2, real peer path on the forwarded open), udp refusal, downcast-miss probe - accept-first shapes (accept_loop + AcceptQueue) untouched Verified: cargo test (default) + --all-features green (45 lib; 151 integration), clippy -D warnings clean (native all-features + wasm32), fmt clean, wasm32 check passes (default crate stays wasm-clean), cargo doc clean (no new warnings). Tasks: tunnels/local-bind-halves (status completed; Notes + Summary filled). CHANGELOG: Unreleased → Added. |
||
|
|
3fb26d9de6 |
feat: the forwarded op's serving side + the lenient reply projection (ADR-008 as amended)
- `ForwardedHandler` — the injected assembly handler (parsed params + channel `BiStream`); the assembly owns the pump, correlating by the `listen` token against its waiting-listener bookkeeping - `make_forwarded_op_handler` — the built-in no-establisher wrapper: deny-unknown params parse (a malformed open fails loud → early return → teardown-at-birth reaps the channel, no phantom), `accept_bi` FIRST (R-02 accepted-flag telemetry), the injected handler's `JoinHandle` awaited inline (R-02) - `register_tunnel_forwarded_openable` — `register_openable` shape (no establisher; the data plane starts immediately), scope gate `tunnel:forwarded` (ingress, never implied by `tunnel:open`/ `tunnel:direct`) - `listen_reply_fields` — the lenient, infallible reply projection (unknown extra reply fields tolerated; a malformed shape logs loud and surfaces both-`None`), shared with `TunnelListener`, delegating to `parse_listen_reply` - Tests: 4 unit (reply projection) + 4 integration (serving-half e2e round-trip, FORBIDDEN gate, malformed-params teardown at both gates, correlation-miss teardown) Verified: cargo test green (126 default; 146 --all-features), clippy --all-targets -D warnings clean (native + wasm32), fmt clean, wasm32-unknown-unknown check passes, cargo doc --no-deps clean |
||
|
|
7d23a89791 |
feat: the bind-first establisher + accept dispatcher (shape 3, ADR-008 as amended)
- BindFn + BoundListener: the platform-boundary bind seam (the
protocol never binds, OQ-TN-04); the opaque listener rides the plan
- bindfirst_establisher: registry namespace gate -> token mint
(non-zero-seeded AtomicU32, per-open fresh, per-producer unique,
token 0 never minted — the wrap shape unit-pinned) -> bind ->
`bound` + `listen` reply fields -> the BindFirstPlan plan; the
typed errors pass through (bind failure dial_failed, exhaustion
resource_shortage, registry miss unknown_resource)
- BindFirstAcceptLoop: the type-erased accept closure (receives the
opaque listener; yields (peer, halves) per accept; Err is
loop-critical)
- ForwardedOpener: the per-accept forwarded-open caller — the call
plane (call_with_payload on channel 0 + manager.adopt_channel; the
reverse POC's open_reverse_channel shape)
- make_bindfirst_accept_handler: the accept dispatcher — accept_bi
first (R-02 telemetry), the idle listen-stream EOF sentinel cancels
the loop + tears the listener down (select!), per-accept pump_bidi
tasks tracked by the handler's JoinHandle (awaited on loop exit —
a forwarded pump never outlives its listener), per-accept failure
isolation (log + drop; v1 no retry), loop-critical accept errors
end the loop
- register_tunnel_bindfirst_openable: same spec + tunnel:open scope
gate (the per-registration establisher posture); the injected
seams ride BindFirstInjections {bind, accept_loop, open_forwarded}
(8-arg sketch -> the bundle; clippy too_many_arguments) + the
optional CF-006 identity witness (witness_establisher wrapper)
- harness: RegistrationMode::BindFirst + wire_bindfirst; the consumer
hook gained the accept-side identity propagation + a shared serving
registry (Topology.consumer_registry exposed); the forwarded-op
serving stub (make_forwarded_stub_handler — correlate by token,
accept the BiStream, pump_bidi against the correlated halves; the
opener-side registration lands with tunnels/forwarded-receipt)
- tests: 6 new integration tests (the full flow e2e — the acceptance
gate incl. the byte round-trip through the whole path, the
dial-open neither-field projection, EOF teardown, per-accept
isolation, typed establishment errors, token discipline) + 6 unit
tests (the mint discipline, the establisher flow at the
Establishment surface, the witness wrapper)
Verified: cargo test green (118 default; 138 --all-features), clippy
--all-targets -D warnings clean (native + wasm32), fmt clean,
wasm32-unknown-unknown check passes, cargo doc --no-deps clean.
|
||
|
|
0789ab62cb |
feat: the direct op (channels/tunnel/direct, ADR-007) end to end
- params.rs: OP_TUNNEL_DIRECT / TUNNEL_DIRECT_SCOPE constants;
SubstrateAddr (untagged {host,port} | {path}; also the ADR-008 §3
peer shape); TunnelDirectParams with render_target();
tunnel_direct_spec() — Sub-typed, External, alk/tunnel ALPN
marker, scope ["tunnel:direct"] (never implied by tunnel:open),
per-substrate if/then target sub-schema (malformed targets fail
the registry's schema gate as INVALID_INPUT before the
establisher). 7 unit tests.
- producer.rs: direct_establisher (+ witness variant) — parse,
render the target to the registry backing-string form, dial via
the SAME injected DialFn; no registry lookup (unknown_resource
can never fire); typed errors per ADR-007 §4 minus the registry
row; CF-006 witness seam. register_tunnel_direct_openable reuses
the base pump handler (plan-flow unchanged).
- consumer.rs: TunnelSession::open_direct — separate constructor
(two scopes are two capabilities), plain open_channel (dial
establishers never bind), identical session/data-planes/teardown.
- tests/producer_direct_op.rs: 15 integration tests — tcp + udp e2e
round-trips, malformed-target rejection (no phantom session),
dial-failure pass-through, NOT_FOUND posture, scope separation
both ways (FORBIDDEN, raw-call + session-level), CF-006 witness
(raw + session paths), parse backstop, target-rendering pin.
Harness: Direct registration mode, wire_direct* topologies,
direct_identity/both_scopes_identity.
- CHANGELOG: Unreleased → Added.
Verified: cargo test green (93 native, 94 --all-features); clippy
-D warnings clean (native + wasm32); fmt clean; wasm32 check passes
(default crate stays wasm-clean); cargo doc clean.
Task: tasks/tunnels/direct-op.md (status: completed)
|
||
|
|
5792efba39 |
chore: raise rust-version floor to 1.88
The 1.85 claim is now false at the dependency level: the lockfile pins alkcall 0.7.x, and alkcall 0.7.1 raised its MSRV floor to 1.88, so no 1.85 toolchain can resolve this dependency graph regardless of what Cargo.toml declares. Raising the floor keeps the claim honest and aligns with the ecosystem floor. Verified: on a 1.85 toolchain with --ignore-rust-version, the build fails in the graph (icu_locale_core 2.3.0, MSRV 1.86, via alkcall's jsonschema -> idna chain) before any alktunnels code is reached — the raise is dependency-driven, and unlike alktty, two clippy lints promoted since 1.85 surfaced here, both test-only and behavior-identical: - tests/harness.rs: unreachable_code — the hanging-establisher probe ends in a typed Err(EstablishmentError::HandlerError) instead of an unreachable!() cast (the sleep never completes; the per-op timeout cancels the task, so behavior is unchanged) - tests/end_to_end.rs: uninlined_format_args — channel_id inlined into the format! string Version-line bump deferred (crate is unpublished); no CHANGELOG here yet. Verification: - 1.88: test 71 default / 91 all-features pass, clippy --all-targets --all-features -D warnings, wasm32-unknown-unknown check + clippy clean (target component installed) - stable 1.94: test 71/91 pass, clippy --all-targets --all-features -D warnings, fmt, doc --no-deps, wasm32 check + clippy clean |
||
|
|
63dbdb64a5 |
test: coverage weak spots — the UdpHalf sender-block recipe found (option 1)
- DatagramReader::feed is infallible (the oversize bound is enforced at frame time; the same zombie category as 4014ba6's InvalidLength). The Vec<Result> shape, the local adapter's dead InvalidData mapping, and consumer.rs's `?` drop. - The sender-block machinery test (root-gated veth fixture, auto-skip without root): the peer address unassigned + the neighbor table flushed pins the connected sender in a persistent WouldBlock/ENOBUFS state; the test drives the WouldBlock stash, the readiness-Pending arms, AND the accepted-byte high-water Pend in one behavior, then resolves (addr assign + peer bind) and asserts the queued datagrams drain and deliver. Jumbo MTU (65535) keeps IP fragmentation out of the way; the block-state probe runs on a throwaway socket (an oversized probe's partially-built skb chain stays corked on EAGAIN and poisons the socket). - pump_queue_to_socket's stash arm now matches ENOBUFS(105) alongside WouldBlock — the blocked-neighbor state reports 105, and both mean "the socket cannot take this datagram now". - connect_udp's resolve-failure arm covered (an RFC 6761 .invalid target maps to dial_failed; rootless). - The no-witness tunnel_establisher wrapper covered (a harness DialNoWitness mode + a full open round-trip through the wrapper). Negative result pinned in the task doc: tokio 1.53 structurally masks UDP ICMP errors out of the poll drive (poll_recv_ready/try_io mask ERROR out of the read direction), so the recv-side ECONNREFUSED mapping arm is unreachable through the pump's drive — it joins the defensive list. Coverage: 92.86% -> 93.91% lines (50 -> 41 uncovered, all deliberate). Verification: cargo test + cargo test --all-features (91 tests), clippy --all-targets --all-features, fmt, wasm32 check+clippy, cargo doc — all clean. |
||
|
|
8f14fd1913 |
test: coverage weak spots — datagram EOF, UDP take_halves framing, reverse pump_against, queue contracts
Closes the behavioral test gaps the 2026-09-09 coverage review found (88.28% → 92.86% lines; details in tasks/tunnels/coverage-weak-spots.md): - Datagram EOF paths (consumer.rs:377-382, 255) — previously zero-covered documented contracts: a clean stream EOF resolves `Ok(None)` (the pump-less session's shutdown signal), a mid-datagram EOF fails loud with `TruncatedDatagram` (OQ-TN-13), both over a new closable UDP echo dial (`closing_udp_echo_dial`, both shapes). - UDP `take_halves` (consumer.rs:201-205) — pins the N-3 framing contract: taken halves carry raw `[len: u16 BE]` wire bytes, and the data-plane accessors fail `ChannelTaken` after the take. - Reverse UDP `pump_against` (consumer.rs:289) — the framed side round-trips end-to-end (payload + the F-2 empty datagram `len=0` survives the pump), the N-11 boundary contract. - `AcceptQueue::push` after close returns `Err(handle)`, closed pop resolves `None`, and the accept loop's queue-closed mapping ends the loop with the handler error (producer.rs:279-288, local accept_loop). - `establishment_reason` wrapper (params.rs) — unit test over the typed surface (open_failed reason / non-establishment / AdoptFailed); plus the schema-gate preemption probe in producer_open_op (missing substrate is `INVALID_INPUT` before the establisher runs). - `UdpHalf` write machinery (local/mod.rs) — four poll-driven tests: queue accept + flush drain to the wire + shutdown best-effort, repeated flush cycles, the unconnected-socket error mapping (deterministic EDESTADDRREQ), and the shutdown-drain-swallows- errors posture. Host probes recorded in the task doc: no UDP sender-blocking shape is reachable on this host (loopback/veth drop silently; the fresh-socket try_send quirk is timing-tolerated, never asserted), so the stash/high-water paths stay uncovered by host limitation, not by oversight. Verified: cargo test (default + --all-features), clippy --all-targets -D warnings, fmt --check, wasm32 check + clippy, llvm-cov 92.86%. |
||
|
|
8cf4cc8bb5 |
fix: UdpHalf is the FRAMED adapter (U-1) — U-2 bind posture, U-3 lazy scratch
Review 001 remediation (tasks/tunnels/fix-udp-framed-adapter): - U-1 [major]: UdpHalf now composes the ADR-003 codec at the substrate boundary — poll_write de-frames channel bytes (incremental DatagramReader) and sends each payload as one raw datagram; poll_read frames raw target datagrams and hands frame bytes to the pump. Targets see payload-only bytes; empty datagrams round-trip (the F-2 fix end-to-end); >8 KiB datagrams arrive as ONE datagram. Mechanics the verification gates forced beyond the sketch: poll_write accepts all offered bytes (tokio copy re-offers a full window unsatisfied — partial-frame holds deadlock >8 KiB frames), backpressure is an accepted-byte high-water mark, socket drain pumps on write/flush/shutdown; >caller-buffer frames stage across reads (the truncation probe's assertion surface updated per the re-validation correction). - U-2 [minor]: connect_udp resolves the target and binds the unspecified address of the matching family (v6 targets dial from [::]:0); bind_tcp gains the wildcard-exposure doc warning. - U-3 [minor]: the 65535 recv scratch allocates lazily on first recv (full-size read kept — try_recv truncates silently; the MTU-shrink idea confirmed unsafe in re-validation). - N-1: the module/connect_udp/UdpHalf docs now describe the composed codec truthfully. Gates (permanent tests in tests/local_halves.rs): recording-target suite (payload-only at the target, empty-datagram round-trip, 9000- byte single datagram, unframed-target resolution), the 65507-byte max-datagram staging probe, and the v6 dial gate. Docs: ADR-003 §Amendment (the boundary mechanics), wire.md truncation bullet, consumer.md adopt sketch (substrate param). Review 001 resolution notes on U-1/U-2/U-3/N-1; task marked completed. Verified: 63 default / 76 local tests ×3 repeat runs; clippy -D warnings (all-targets + wasm32), fmt --check, doc --no-deps (warning-free), wasm check, publish dry-run. |
||
|
|
a0f939c6df |
review: phase-gate review 001 — spec conformance of the v1 implementation
Findings + review doc: docs/reviews/001-implementation-review.md (the alkcall house pattern). Closes tunnels/review-core-crates and tunnels/review-impl. Findings: - U-1 [major, NOT fixed] — the local UDP adapter is not the framed adapter ADR-003 mandates: codec-framed bytes reach real targets, empty datagrams are EOF-shaped at the adapter (tunnel teardown), >8 KiB datagrams split, unframed target-initiated datagrams are dropped. Executable-pinned by real-socket probes (temporary test, deleted after the run). Remediation task: tunnels/fix-udp-framed-adapter. - C-1 [major, fixed] — AcceptQueue::pop lost wakeup in the check→register window (open op hangs to the establishment bound); fixed via the tokio notify_waiters contract (Notified created before the check each iteration) + multi-thread regression test. - N-4/N-5/N-7/N-8/N-9/N-10/N-11 [minor, fixed] — serialize TunnelParams in open_reverse_channel; stale doc ref; broken doc link; root re-exports (TunnelParams/Substrate/tunnel_open_spec/OP_TUNNEL_OPEN); README (publish dry-run was failing on the missing readme); harness comment; pump_against UDP framing doc note. - N-2 retracted by executable falsification (codec buffering is bounded by construction); N-6 non-finding with rationale. Verified: 63 tests default / 70 with --features local, 3x repeat-run clean both configs; clippy -D warnings (all-targets + wasm32); fmt; doc warning-free; wasm32 check; cargo publish --dry-run passes. |
||
|
|
2efac9b609 |
feat: end-to-end suite — 18-test consolidation over the shared harness
- tests/end_to_end.rs: 1 MiB backpressure, deadline→timeout (ADR-049 §2), CF-006 witness (transport + token), odd-ID asserts + distinctness (ADR-047 §5), full CF-005 precedence chain (ServingConfig override, distinct-id token probe, fail-closed), R-01 concurrency ×3, listen FIFO + typed-error table, datagram batching + 7-byte chunk-split codec probe + reverse codec session + TCP/UDP concurrent, teardown matrix arms (close-with-pump / join copy counts / Drop-with-pump / failed-adopt), no-phantom-channel + R-02 data-plane-tracking asserts - tests/harness.rs: RegistrationMode::Timeout (hanging establisher, per-registration bound), wire_serving_identity (CF-005 (a) shape) - task doc: status completed, notes + summary Verified: cargo test (68), cargo test --features local (75), 3× repeat-run clean both configs, clippy -D warnings (all-targets, local, wasm32), fmt --check, wasm32 check |
||
|
|
65fb69db61 |
feat: local-socket-halves — the local feature (TCP/UDP/unix halves functions)
- src/local/mod.rs (feature = "local" -> tokio/net): dial_tcp (5s timeout), connect_udp (ephemeral bind + connect + the FRAMED UdpHalf adapter), dial_unix (OQ-TN-14 in v1), bind_tcp + TcpListenerHalves::accept_loop (the listen shape's assembly half), local_dial() (one DialFn covering all three substrates). - UdpHalf truncation fails loud (OQ-TN-13): poll_read recvs into a scratch buffer first and size-checks against the caller's buffer — an over-size datagram is io::ErrorKind::InvalidData, never a silent truncation (tokio's poll_recv-into-ReadBuf would truncate). - No socket type appears outside src/local/ (convention 16); the default crate stays wasm-clean; stdio bridging is NOT here (alktty owns process stdio). - Tests (tests/local_halves.rs, 7, feature-gated): TCP/UDP/unix dial round-trips through the real establisher path, the 1400-byte MTU datagram, the truncation fail-loud probe, dial-refusal -> dial_failed, listen producer over a real TCP listener end-to-end. Verified: cargo test green (default + --all-features, 44 + 7), clippy -D warnings (default + all-features + wasm32), fmt clean, cargo check --target wasm32-unknown-unknown passes. |
||
|
|
09d32d5aa6 |
feat: producer-listen — listen establisher (shape 2) + AcceptQueue contract
- AcceptFn: the injected accepted-connection source (the assembly layer owns the listener + accept loop; the protocol never binds — OQ-TN-04). - AcceptQueue: the protocol-side queue contract (async push/pop/ close; FIFO always-before-take ordering; close-while-waiting resolves None). Empty-queue posture is assembly-owned (a late accept is legitimate; resource_shortage is the closure's mapping; the wrapper's 10s establishment timeout is the backstop). - listen_establisher: same open op, same params, same typed errors — registry namespace gate → accept() → Establishment::new(plan); the pump handler is untouched (plan-flow with a different source). - register_tunnel_listen_openable: same spec/pump registration with the listen establisher (the honest shape vs a dial/accept enum: the establisher is the only difference; one establisher per op id per session registry — documented). - Tests (tests/producer_listen.rs, 6): listen flow end-to-end, FIFO ordering across two opens (R-01 plan-flow, listen-flavored), empty queue -> resource_shortage, closed listener -> dial_failed, unknown resource -> unknown_resource, late-push wait-then-resolve. - Harness: RegistrationMode enum + wire_listen. - TargetHandle gains a structural Debug impl (test ergonomics). Verified: cargo test green (44), clippy -D warnings (native + wasm32), fmt clean, wasm32 check passes. |
||
|
|
fb2389bd62 |
feat: consumer-session — TunnelSession (open/adopt, data planes, teardown)
- TunnelSession (src/consumer.rs): open (ChannelClient::open_channel) + adopt (ChannelManager::adopt_channel) construction, substrate- shaped data planes (raw halves for stream; ADR-003 codec + pending frame queue for UDP), pump_against (session-owned pump_bidi handle), and teardown ownership per ADR-005: close (abort + ungraceful reap), join (await + reap + copy counts, pump-less (0,0,reaped)), Drop (abort + sync reap, never leaks). No Clone (compile_fail doc-test). - open_reverse_channel on the CallConnection (the reverse path's two-step: call then adopt — the POC's honest shape). - Error surfaces (src/error.rs): TunnelOpenError (typed ADR-049 §4 surface via open_ref/establishment_reason), TunnelIoError (wrong- substrate, TruncatedDatagram fail-loud, ChannelTaken), ReverseOpenError. - Tests: tests/consumer_session.rs (12) over two harness topologies — forward (wire_forward/ForwardTopology: consumer connect-side pure client, producer serving via adapter) and reverse (the POC shape); teardown matrix, W4 half-close, out-of-band close + self-reap (W3), F-2 empty datagram, AdoptFailed-on-collision (bogus-id adopt parks by design — documented), concurrent sessions. Harness: all_substrate_dial. - Adopt takes substrate (task-note deviation: the data plane shape rode the open call; cannot be inferred from the ID). Verified: cargo test green (38), clippy -D warnings (native + wasm32), fmt clean, cargo check --target wasm32-unknown-unknown passes. |
||
|
|
2b35da4395 |
feat: producer-open-op — establisher + pump handler + registration
- tunnel_establisher: registry lookup -> injected DialFn (ADR-004) -> Establishment::new(plan) (R-01 plan flow); typed reason table (unknown_resource/dial_failed/resource_shortage/handler_error) - make_tunnel_pump_handler: accept_bi + plan downcast + pump_bidi awaited inline (R-02; no spawn-and-forget, no substrate types) - register_tunnel_openable: spec + establisher + pump + dial + CF-006 identity witness seam - TargetHandle +Sync halves (F-1 bound); Substrate + Hash for registry keys; tunnel_establisher_with_witness for the per-call identity probe - tests: duplex harness (reverse-POC topology) + 11 integration tests — round-trip, typed errors, FORBIDDEN fail-closed, CF-005 precedence + CF-006 witness, plan-flow concurrency (no race), W2 late registration, INVALID_INPUT schema rejection, both-sides sanity Verified: cargo test (14 unit + 11 integration), clippy -D warnings (native + wasm32), fmt --check, wasm32 check — all clean |