Files
alktunnels/tasks/tunnels/wire-codec.md
T
glm-5.3-flash 60c625899a feat: wire-codec — UDP datagram framing (frame_datagram/DatagramReader)
- [len: u16 BE] per-datagram framing per ADR-003; len=0 is a legal
  empty datagram (F-2 invariant), never EOF
- DatagramCodecError: Oversize (frame-time, never a wire overflow) +
  InvalidLength (truncated stream — the OQ-TN-13 fail-loud codec-side
  anchor)
- DatagramReader: incremental decoder — datagrams split across chunks,
  batched in one chunk, partial headers; feed -> Vec<Bytes> (zero-copy
  freeze handoff)
- is_mid_datagram teardown diagnostic
- 8 test families pinning the ADR-003/BAST invariants incl. 7-byte
  chunk splits and truncated-stream-never-yields-partial

Verified: cargo test (14 passed), clippy --all-targets -D warnings
(native + wasm32), fmt --check, wasm32 check — all clean
2026-09-08 07:16:18 +00:00

5.0 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
tunnels/wire-codec Data-plane codec (frame_datagram / DatagramReader) + sentinel layering tests completed
tunnels/crate-init
tunnels/params
narrow medium component implementation
wire
codec
udp

Description

Implement src/wire.rs per ADR-003 + wire.md §The Data Plane + bast.md: the mandatory UDP length-framing codec. This is a direct port of the forward POC's /workspace/alktunnels-udp-poc/src/wire.rs (17 tests rode it; the shape is settled) with the POC-to-crate generalizations the ADRs pin.

API

pub const MAX_DATAGRAM_LEN: usize = u16::MAX as usize;  // 65535

pub struct DatagramCodecError { .. }   // thiserror: Oversize(usize), InvalidLength(...)

pub fn frame_datagram(payload: &[u8]) -> Result<Bytes, DatagramCodecError>
// [len: u16 BE][payload]; len=0 is a legal empty datagram; >65535 = Oversize at frame time

pub struct DatagramReader { /* incremental decoder state */ }
impl DatagramReader {
    pub fn new() -> Self
    pub fn feed(&mut self, chunk: &[u8]) -> Result<Vec<Bytes>, DatagramCodecError>
    // zero or more complete datagrams per chunk; buffers partial frames
    // across chunk boundaries
}

The invariants the tests MUST pin (all POC-validated; they are the

spec's executable form)

  1. Single datagram round-trip (frame → feed → exact bytes out).
  2. Empty datagram (len=0): survives as a real datagram — NEVER confused with EOF (the F-2 invariant; the codec layer never emits a zero-length read).
  3. Split across chunks (feed a frame in awkward 7-byte chunks; exact reassembly).
  4. Two datagrams batched in one chunk (feed once, two out, in order).
  5. Partial header at a chunk boundary (1-byte len prefix split).
  6. Mid-datagram state observable (incremental decode correctness).
  7. Oversize rejected at frame time (never a wire overflow).
  8. Truncation fail-loud (OQ-TN-13): the adapter-level receive that would truncate surfaces as an error — test at the codec boundary by asserting frame_datagram + reader round-trip with a buffer smaller than a full datagram is not silently accepted (the concrete API shape lands with tunnels/local-socket-halves; here, pin the codec-side invariant: a truncated stream yields an error, not a partial datagram).

No stream_type byte, no 5-byte header — a tunnel has one data stream per direction (ADR-003). No EOF sentinel in the codec — EOF is the channels-level length=0 chunk, a different layer (the two coexist; the POC proved no collision).

Acceptance Criteria

  • All 8 test families above pass (the POC's 7 + the truncation pin)
  • frame_datagram returns Bytes (zero-copy handoff to the mux)
  • DatagramReader state is incremental across arbitrary chunk boundaries
  • cargo clippy --all-targets -- -D warnings, cargo fmt --check clean
  • wasm32 check passes (the codec is pure byte work — it must be wasm-clean)

References

  • docs/architecture/decisions/003-codec-and-udp-framing.md
  • docs/architecture/bast.md (the binary contract)
  • docs/architecture/wire.md §The Data Plane (normative byte diagrams)
  • POC reference: /workspace/alktunnels-udp-poc/src/wire.rs (port with the crate's doc-comment style)

Notes

  • Direct port of the POC's wire.rs (17 tests rode it) with the crate conventions: thiserror doc comments, module doc per ADR-003 + wire.md + bast.md layering (sentinel layering called out — the F-2 invariant is load-bearing, so the module doc states why raw pass-through is structurally broken for UDP).
  • Error naming per the task's API: Oversize(usize, usize) (was POC's TooLarge) + InvalidLength {declared, have} (the truncated-stream shape — the OQ-TN-13 fail-loud posture's codec-side anchor).
  • feed returns Vec<Bytes> per the task API (the POC's Vec<Datagram> wrapper dropped — Bytes is the payload; the mux handoff is zero-copy via BytesMut::freeze).
  • MAX_DATAGRAM_LEN/DATAGRAM_LEN_FIELD constants; DATAGRAM_LEN_FIELD is private (implementation detail), MAX_DATAGRAM_LEN public (the frame-time bound consumers need).
  • 8 test families: single round-trip, empty-datagram-not-EOF, 7-byte-chunk split reassembly, two-datagram batch, partial header, mid-datagram observability, oversize at frame time, truncated stream (feeds an empty chunk + a one-byte-short tail — never yields a partial datagram; the codec-side half of OQ-TN-13; the adapter-level receive shape lands with local-socket-halves).
  • POC's Datagram struct dropped: Bytes suffices (the task's feed -> Vec<Bytes> signature); is_empty() moves to callers (trivial on Bytes).

Summary

src/wire.rs complete: frame_datagram (Oversize at frame time, len=0 legal), DatagramReader (incremental decoder, is_mid_datagram teardown diagnostic), DatagramCodecError {Oversize, InvalidLength} — the ADR-003/BAST contract in executable form. 8 codec tests + 6 params tests = 14 passing. Verified: cargo test, clippy --all-targets -D warnings (native + wasm32), fmt --check, wasm32 check — all clean.