0ceecf582b7ebc118d5c547e2c63181a00cd0808
12
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 |
||
|
|
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. |
||
|
|
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%. |
||
|
|
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 |
||
|
|
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 |