Files
alktunnels/CHANGELOG.md
T
glm-5.3-flash 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
2026-10-14 05:23:08 +00:00

17 KiB
Raw Blame History

Changelog

All notable changes to this crate are documented here. The format is based on Keep a Changelog, and this crate adheres to Semantic Versioning.

Unreleased

0.3.0 - 2026-10-14

The dependency-major release: the alkcall substrate bumped 0.8.1 → 0.9.0 (the stub-sweep release). No alktunnels API change, no wire change; the only code contact with the 0.9.0 breaking surface was test-side (Establishment no longer derives Debug — three expect_err/panic! sites restructured), which is why this is a minor bump rather than a patch: the semver-incompatible dependency bump plus this crate's public re-exports of alkcall types make it semver-visible to downstream consumers (matching the 0.7.1 → 0.8.0 ride on 0.2.0).

Changed

  • alkcall 0.8.1 → 0.9.0 (crates.io). The stub-sweep release: the marked-pub open contract (S-01), channel/control (S-02), channel/resources/subscribe (S-03), and op/register forward-through (S-04) replace the four always-failing stubs. None of the four stubs were on this crate's paths (no marked-pub registration, no control-handle subscription, no resource enumerator use, no generic op/register announcements), so no behavior change here. Of the 0.9.0 breaking list, Establishment dropping its Debug derive was the only surface this crate touched — two bindfirst_establisher unit tests' expect_err calls and one integration test's untyped panic!("{other:?}") fall-through restructured (the error type itself remains Debug; the Ok arm now formats via panic!("got Ok")). ManagerError::InvalidAdopt (additive), RelayError::PubTypedOpen removal, and the register_imported/register_imported_all return change are untouched by this crate's API use. 136 tests pass; wasm-clean verified.

0.2.1 - 2026-09-28

The hardening release: fuzzing adopted from the alkcall pattern (fuzz/ workspace, excluded from the published package) and the alkcall substrate bumped 0.8.0 → 0.8.1. No API break, no wire change.

Added

  • Fuzzing (fuzz/ workspace, docs/research/fuzzing.md) — three cargo-fuzz targets over this crate's own attacker-reachable surfaces (the envelope/chunk/manager layer is upstream and fuzzed there per alkcall 0.8.1): datagram_codec (the ADR-003 codec: no-panic, infallible decode, size bound, F-2 sentinel layering, re-frame round-trip, retained-bytes bound), params_parse (the serde params/reply parsers: no-panic on raw bytes and bounded structured values, clean typed failure, round-trips), and session_pumps (the wave-2 stateful target: op sequences over a live channels stack per exec, the TunnelSession data planes mirrored, no-wedge + shutdown-on-completion + lossless p2c + EOF-sentinel teardown + the pending backlog bound asserted). Invariant logic lives in fuzz/shared/ — a stable-toolchain crate replayed as plain cargo test, so the committed corpora (96 + 8 seeds, plus the campaign-promoted seed-900) stay executable without nightly. fuzz/ is excluded from the workspace and from the published package. Campaigns (detached, fork mode): ~25M+228k execs across waves 1–2, zero crashes/hangs/OOMs; all findings were harness-model defects, no crate bugs.

  • TunnelSession::datagram_backlog() — the UDP pending count (the datagrams decoded from a chunk beyond the one returned), the wave-2 harness's asserted backlog bound made caller-visible; None for stream sessions or after the halves were taken.

  • DatagramReader::retained_bytes() — the decoder-state byte count (leftover buffer + the in-flight partial datagram); post-feed bounded by one frame (≤ MAX_DATAGRAM_LEN + 2), pinned by the datagram_codec target.

Changed

  • alkcall 0.8.0 → 0.8.1 (crates.io). The bug-fix release: a duplicate adopt_channel/open_channel no longer destroys the live channel it collided with (found by alkcall's own manager_routing fuzz target) — directly relevant to this crate's reverse-path adopt flow. No alktunnels API or wire change in this bump; the full suite passes unchanged.

0.2.0 - 2026-09-20

The graduation release: the direct op + the bind-first/forwarded metadata flow (ADRs 007/008), over alkcall 0.8.0. Composed against by alksocks (the SOCKS5 layer) and the planned alknodes assembly.

Added

  • TunnelListener — the listen-flow consumer session (ADR-008 §4) — the listen-flow sibling of TunnelSession: TunnelListener::open (rides ChannelClient::open_channel_with_reply, alkcall 0.8.0's U-2 surface — the full reply arrives and the bind-first bound/ listen fields project through the lenient listen_reply_fields; adopt-free construction — the client already adopted the channel), bound()/listen() (the Option posture — None for dial/accept-first producers and old producers), and the listen channel's teardown ownership (ADR-005's posture extended to the listen flow): close (the graceful EOF sentinel via shutdown, then the reap) and Drop (the held write half drops → the mux pump's implicit-EOF chunk → the far accept dispatcher's teardown signal; sync reap — never leaks). The held BiStream's write half stays IDLE and private (a listen channel has no data-plane role; pumping one is meaningless). No Clone (compile-asserted). Forwarded sessions are NOT owned by the listener — they arrive as ordinary adopted-side TunnelSessions on the opener's manager (adopt gains nothing, pinned by test); the assembly correlates them via the listen() token (the receipt queue is assembly-side bookkeeping).

  • The local feature's bind-first substrate half (ADR-008 §1, shape 3) — the real-socket sources behind the committed BindFn/BindFirstAcceptLoop contracts (behind local; the default crate stays wasm-clean): bind_tcp_first (port 0 resolved via local_addr — the bound reply field's data) + TcpListenerHalves::accept_pair (one accept → (peer, halves) — 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, the queue-feeding accept_loop, the bind-first accept_pair — an unnamed (autobind) peer fails loud: the forwarded peer param requires an address) + bind_unix_first; the assembly conveniences local_bind_first() (tcp/unix dispatch; udp fails loud — a UDP socket has no accept, the bind-first shape is not defined there, never a fake listener) and local_bindfirst_accept_loop() (downcasts the plan's opaque listener to the module's wrappers; a foreign listener type fails loud as handler_error). The accept-FIRST shapes (accept_loop + AcceptQueue) are untouched. The direct-op DialFn reuse is verified + pinned: local_dial is shared unchanged (the UDP framed wrap happens inside the closure — review 001 U-1); a TunnelSession::open_direct + Substrate::Udp test drives a REAL UDP echo socket through the direct op's establisher path.

  • The forwarded op's SERVING side + the lenient reply projection (ADR-008 §3 as amended) — the listen-opener half, the receipt counterpart to the bind-first producer (the opener SERVES the op; the producer CALLS it per accept): ForwardedHandler (the injected assembly handler — the parsed params + the channel BiStream; the assembly owns the pump, since the local halves are assembly state)

    • make_forwarded_op_handler (the built-in wrapper: deny-unknown params parse (a malformed open fails LOUD → the wrapper returns early → teardown-at-birth reaps the channel — no phantom, the correct shape for a malformed ingress), accept_bi FIRST (R-02 accepted-flag telemetry), then wrapper→assembly bridge, the injected handler's JoinHandle awaited inline); + register_tunnel_forwarded_openable (register_openable — no establisher, the data plane starts immediately; scope gate tunnel:forwarded, ingress — a separate grant from tunnel:open/tunnel:direct, never implied by either). Plus listen_reply_fields (the lenient, infallible session-surface projection of the bind-first reply — unknown extra reply fields tolerated, a malformed shape logs loud and surfaces both-None; shared with TunnelListener). Correlation-miss teardown semantics documented + test-pinned: the assembly holds the map, its error path drops the stream, the channel reaps (the producer's accept loop drops the accepted connection on the open's failure — the shapes mirror).
  • The bind-first establisher + accept dispatcher (shape 3, ADR-008 as amended) — the -R/ingress establisher whose establishment phase BINDS (the address-first-vs-accept-first deadlock dissolves; the accept wait lives in the teardown-governed pump phase): BindFn (the substrate bind function — the platform boundary, the protocol never binds) + BoundListener (the opaque listener + the real OS-chosen bound address); bindfirst_establisher (registry namespace gate → token mint → bind → bound + listen reply fields via Establishment::with_reply_field → the BindFirstPlan plan payload; the token mint is the pinned discipline — non-zero- seeded AtomicU32, per-open fresh, per-producer unique, token 0 never minted); BindFirstAcceptLoop (the type-erased accept closure — receives the opaque listener, yields (peer, halves) per accept); ForwardedOpener (the per-accept forwarded-open caller — the call plane: call_with_payload on channel 0 + manager.adopt_channel, the reverse POC's shape); make_bindfirst_accept_handler (the dispatcher: accept_bi first per R-02, the idle listen-stream EOF cancels the loop and tears the listener down, per-accept pump_bidi tasks tracked by the handler's JoinHandle — a forwarded pump never outlives its listener; forwarded-open failures are per-accept isolated — log + drop, the loop continues, v1 no retry); register_tunnel_bindfirst_openable (same spec + tunnel:open scope gate as the dial/listen variants — the per-registration establisher posture; the injected seams ride BindFirstInjections {bind, accept_loop, open_forwarded} + the optional CF-006 identity witness). The opener-side forwarded-op serving shape (register_openable, no establisher) landed with tunnels/forwarded-receipt.

  • The forwarded op + the bind-first params surface (ADR-008 as amended) — the wire-stable types pin first (the ordering tunnels/params used for the base op): OP_TUNNEL_FORWARDED / TUNNEL_FORWARDED_SCOPE constants; TunnelForwardedParams {listen, substrate, peer} (deny-unknown; listen is the producer-minted correlation token, positive by wire contract — schema minimum: 1 + a serde-level pin) with a per-substrate peer sub-schema (malformed peers fail the registry's schema gate before the opener-side handler); tunnel_forwarded_spec() (Sub-typed, External, alk/tunnel ALPN marker, no establisher — the forwarded data plane starts immediately); TunnelListenReply + parse_listen_reply (the typed projection of the bind-first listen open's additive bound/listen reply fields — unknown reply fields tolerated, malformed ones fail loud via the new TunnelError::MalformedReply); BindFirstPlan (the opaque assembly-typed listener + the token — the plan payload the bind-first establisher/accept-dispatcher task consumes).

  • The direct op (channels/tunnel/direct, ADR-007) — dynamic- target egress (the ssh direct-tcpip / SOCKS5 CONNECT shape), riding the same alk/tunnel ALPN: SubstrateAddr (the untagged substrate-shaped address object, also the forwarded op's peer shape) + TunnelDirectParams {substrate, target} with a per-substrate input_schema (malformed targets fail the registry's schema gate — INVALID_INPUT — before the establisher), tunnel_direct_spec(), direct_establisher (+ witness variant; no registry lookup — unknown_resource can never fire; targets render to the same backing-string form the registry backings use and ride the SAME injected DialFn), register_tunnel_direct_ openable (same pump handler + plan-flow as the base op), and TunnelSession::open_direct (separate constructor — tunnel:direct is a separate grant from tunnel:open, never implied by it; the session type, data planes, and teardown API are identical). An old producer rejects the unknown op loudly (NOT_FOUND).

Changed

  • alkcall 0.7.1 → 0.8.0 (crates.io). The two upstream asks the graduation ADRs depend on landed (alkcall review 008): the establisher → reply projection (Establishment::with_reply_field, ADR-049 amendment 3 — the bind-first bound carrier) and flavor-form open-op ids in discovery (ADR-047 amendment 3 — channels/tunnel/direct-shaped ops reconstruct their channel_open marker through discovery, so hub relay wraps them as channels, not plain forwarding stubs). Plus the bonus: ChannelRelay + HubLegTemplate (ADR-051) — the hub re-produce machinery the graduation's re-produce story assumed would be assembly-layer is now in-tree, with bound flowing end-to-end per-hop-truthful (ADR-051 §6, ADR-008 §6's sentence realized). No alktunnels API or wire change in this bump; 71/91 tests pass, wasm-clean verified.

  • BindFirstPlan gains substrate: Substrate (review 002 M-1 remediation) — the accept dispatcher's silent unwrap_or(Substrate::Tcp) fallback (the second TunnelParams re-parse of wire input) is removed: the establisher stamps the parsed substrate into the plan (one parse, one source of truth) and the dispatcher reads it from the plan it already downcast. A drift can no longer silently stamp Tcp into a UDP listen tunnel's forwarded opens (a raw-vs-framed data-plane mismatch on the opener side). Plan-payload change is pre-consumer (no external BindFirstPlan consumer yet). Pinned by forwarded_opens_carry_plan_stamped_substrate (a UDP bind-first flow whose forwarded opens carry substrate: udp from the plan).

0.1.0 - 2026-09-10

The initial release: the v1 tunnel protocol implementation over alkcall 0.7.x channels, spec-complete (Phase 1) and implementation- complete (Phase 2, review 001 findings remediated).

Added

  • Wire surface (one-way-door stable from this release): the channels/tunnel/sub open op on the alk/tunnel ALPN; params {resource, substrate} (deny_unknown_fields; ADR-001); the tunnel:open scope gate (ADR-006).
  • Data-plane codec (ADR-003): raw pass-through for stream substrates (tcp, unix); mandatory [len: u16 BE] boundary- preserving framing for udp (len = 0 a legal empty datagram, never EOF; oversize rejected at frame time; truncation fails loud — OQ-TN-13). The BAST contract: docs/architecture/bast.md.
  • Producer half (ADR-004): ResourceRegistry, tunnel_establisher (dial shape) and listen_establisher (the -R far-side accept shape) over alkcall's establishment surface (ADR-049), make_tunnel_pump_handler (the pump_bidi two-pump contract, alkcall ADR-050), and the register_tunnel_openable / register_tunnel_listen_openable registration helpers.
  • Consumer half (ADR-005): TunnelSession — open (forward) / adopt (reverse), substrate-shaped data planes (stream_halves, take_halves, send_datagram/recv_datagram), pump ownership (pump_against), teardown ownership (close/join/Drop); open_reverse_channel for the hub-call path.
  • local feature (non-wasm): dial_tcp, bind_tcp + TcpListenerHalves accept loop, connect_udp wrapped in the FRAMED UdpHalf adapter (the codec composes at the substrate boundary — review 001 U-1), dial_unix, and the all-substrate local_dial closure. Stdio intentionally out of scope (alktty's pipe mode owns process stdio — OQ-TN-14).
  • Identity seam (alkcall 0.7.0 CF-005/CF-006): payload-token > ServingConfig.identity > transport identity precedence proven end-to-end; the per-call opener identity reaches the establisher.
  • Test suite: 71 tests default / 91 with local, including the review 001 gates (real-socket UDP framing probes, the AcceptQueue lost-wakeup regression, the identity-precedence matrix). Wasm-clean default crate verified (wasm32-unknown-unknown check + clippy).