Files
alksocks/tasks/implementation/core-wire-codec.md
T
glm-5.3-flash a2483e4a84 tasks: decompose the protocol-core group — thirteen dependency-ordered tasks
Batch 1 of the Phase 1 decomposition: the default, wasm-clean crate
(producer + consumer + wire + seams + params/errors), per the
architecture spec set (ADRs 001-013) and the three POCs' validated
shapes. The assembly layer (local/composed providers, front doors) is
batch 2, decomposed after review-protocol-core opens.

- foundation: crate-deps (alkcall 0.8.0 first consumption), wire-codec
  (the ASSOCIATE frame + the joint bound), seam-types (the three
  ADR-004 seams), params-errors (the one-way-door surface)
- harness: duplex transport + identities + in-process seams (the
  alkcall 0.8.0 establishment surface proven before the producer)
- verticals: producer/consumer splits per command (CONNECT,
  ASSOCIATE, BIND) — each POC's scope, producer tasks test via the
  raw RFC client, consumer tasks e2e through the harness
- closing: integration suite (three-command regression, teardown
  matrix, identity matrix, docs sync) + review-protocol-core (the
  batch gate)

taskgraph: 8 generations, no cycles, risk path 2.55 (no high-risk
tasks). Also records the wire.md/bast.md len=0 residue (payload-level
F-2 is the operative invariant) as a doc-sync deliverable of the
wire-codec task.

Verification: cargo test (76 passed), fmt clean; taskgraph
validate/cycles/parallel/critical clean.
2026-09-23 07:38:27 +00:00

4.9 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
implementation/core-wire-codec Core — the ASSOCIATE datagram frame codec (src/wire.rs) pending
implementation/core-crate-deps
narrow low component implementation

Description

Implement src/wire.rs per wire.md §The Data Plane and bast.md: the first-party [len: u16 BE][addr + data] frame codec for the UDP ASSOCIATE datagram stage (ADR-003), re-implemented in first-party code shaped on alktunnels' length codec (/workspace/@alkdev/ alktunnels/src/wire.rs — frame_datagram/DatagramReader — same F-2 behavior, but the payload here is the RFC 1928 address encoding plus data, not a bare payload).

The codec is the crate's one framed stage (CONNECT/BIND are raw pass-through — no framing). Framing is applied exactly once, by this layer: Egress::next_reply returns inner bytes (addr + data); the producer's frame writer adds the single length prefix (the double-framing bug class POC #2 caught is an API-design invariant this module's docs must state).

Deliverables:

  1. frame(addr: &TargetAddr, payload: &[u8]) -> Result<Bytes, WireError> — one frame per datagram: [len: u16 BE][addr + data] where addr + data is TargetAddr::to_be_bytes() (ATYP + addr + port) followed by the payload.
  2. The joint bound (the load-bearing constraint): reject Oversize at frame time whenever len(addr) + len(payload) > 65535. The UDP payload bound alone (65507) does NOT fit u16 unconditionally — a 65507-byte datagram to a 255-character domain is 65766 frame bytes. Max data: 65276 for domain-form destinations, 65516 for IPv6, 65528 for IPv4. No unchecked u16 arithmetic on the payload bound (wire.md's explicit requirement). Expose addr_encoding_len(addr) (7 v4 / 19 v6 / 259 max domain) so callers can compute the joint bound.
  3. DatagramReader — the incremental decoder: feed it whatever read() yields; it reassembles frames across chunk boundaries (split frames, batched frames, partial headers). Returns (TargetAddr, Bytes) per completed frame — it decodes the addr sub-layout (ATYP determines the address length) and splits addr + data. Infallible decode direction (the oversize bound is enforced at frame time); is_mid_frame() diagnostic for teardown.
  4. The F-2 invariant, correctly stated: a zero-length payload is a legal empty datagram — never EOF. (len == 0 is unreachable on this wire since addr is always ≥ 7 bytes; the invariant that matters is payload-level. See the doc-sync deliverable.)
  5. Doc-sync (wire.md + bast.md residue): both docs say "len = 0 is a legal empty datagram" while also pinning that len covers addr + data (joint bound) — len = 0 is unreachable since addr ≥ 7 bytes. Correct both docs to state the operative invariant at payload level ("a zero-length payload is a legal empty datagram, never EOF — the F-2 invariant") in the same change; do not silently diverge.
  6. EOF layering note in the module docs: EOF is the channels-level sentinel on the BiStream, outside this codec; the codec never emits zero-length reads, so the layers never collide (the alktunnels wire.rs module-doc posture).

Tests: round-trip (v4/v6/domain), empty payload legal + not EOF, split across awkward chunks, batched frames in one chunk, partial header waits, joint-bound rejection (the 65507 + 255-char-domain case), oversize at frame time, mid-frame state observable, garbage ATYP decode error (the udp_undecodable_frame_ends_association shape — the decode error type must distinguish a bad ATYP so the producer stage can fail the association loud).

Acceptance Criteria

  • frame/DatagramReader/addr_encoding_len implemented per the joint bound; no unchecked u16 arithmetic
  • Zero-length payload round-trips as a legal empty datagram
  • Split/batch/partial-header reassembly tests pass
  • Joint-bound rejection test (65507-byte payload + 255-char domain → Oversize) passes
  • Garbage-ATYP decode surfaces a typed error (not a panic)
  • wire.md + bast.md residue corrected (payload-level F-2 statement) in the same change
  • cargo test green; clippy --all-targets -- -D warnings clean; cargo fmt --check clean
  • cargo check --target wasm32-unknown-unknown + cargo clippy --target wasm32-unknown-unknown -- -D warnings clean

References

  • docs/architecture/wire.md §The Data Plane (the frame + the joint bound arithmetic)
  • docs/architecture/bast.md (the BAST contract for the frame)
  • docs/architecture/decisions/003-udp-handle-swap.md (framing-once, F-2)
  • Template: /workspace/@alkdev/alktunnels/src/wire.rs (the length codec + its test matrix — shape on it, do not depend on it)
  • POC sketch: /workspace/alksocks-udp-poc/src/datagram.rs (frame/inner/unframe/addr_encoding_len)

Notes

To be filled by implementation agent

Summary

To be filled on completion