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
- 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.
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%.
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.
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.
- 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.
- 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.