Files
wz-phone/vault/PRDs/PRD-wire-format-v2.md
Siavash Sameni ed8a7ae5aa docs: protocol audit 2026-05-25, update architecture + Obsidian vault
Audit:
- docs/AUDIT-2026-05-25.md: full protocol audit covering 8 findings
  (4 critical, 2 high, 5 medium, 4 low) with code references and fix
  effort estimates
- vault/Audit/Tasks.md: Obsidian Tasks plugin file tracking all audit
  items with priorities, due dates, and per-step checklists

Architecture docs updated for Wire format v2 and Wave 5/6 features:
- ARCHITECTURE.md: adds wzp-video to dependency graph and project
  structure; wire format updated to v2 (16B header, 5B MiniHeader);
  relay concurrency section corrected (DashMap+RwLock is current, not
  a future optimization); test count 571→702; Android note
- PROGRESS.md: Wave 5 and Wave 6 sections appended; test count 372→702;
  current status and open blockers as of 2026-05-25
- ROAD-TO-VIDEO.md: implementation status table inserted (/🟡/🔴/🔲
  per phase); 6-step critical path to first video call
- WZP-SPEC.md: MediaHeader updated to v2 (16B byte-aligned); MiniHeader
  updated to 5B with seq_delta; codec IDs 9-12 added (H.264/H.265/AV1);
  version negotiation section added

Obsidian vault (vault/):
- 114 files across Architecture/, PRDs/, Reports/, Android/,
  Reference/, Audit/ with YAML frontmatter
- 00 - Home.md index note with wiki links
- .obsidian/app.json config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:00:17 +04:00

4.9 KiB
Raw Blame History

tags, type
tags type
prd
wzp
prd

PRD: Wire Format v2

Status: proposed Resolves: Audit W1, W4, W9, W10. Keystone prerequisite for video and per-MediaType conformance enforcement. References: docs/WZP-SPEC.md, docs/ROAD-TO-VIDEO.md Phase V1, docs/PROTOCOL-AUDIT.md.

Problem

v1 wire format has four structural problems that compound the moment video lands:

  • 16-bit sequence wraps in ~21 min at 50 pps (W1)
  • MiniHeader has no sequence delta, so a missed full header desyncs (W4)
  • CodecID is 4 bits → 16 codec slots, 9 used; video will exhaust it (W9)
  • No MediaType field → SFU cannot distinguish audio/video/data without a codec lookup (W10)

Fixing these post-deployment is a multi-client coordinated break. Fix once, before video.

Goals

  • One wire-format change resolves W1, W4, W9, W10 and reserves headroom for the next decade.
  • v1 and v2 can co-exist briefly during rollout via explicit version handshake (typed rejection, not silent corruption).
  • All 571 audio tests pass under v2.

Non-goals

  • Backward wire compatibility (we will not encode v2 atop v1 — it is a clean break).
  • Video framing rules themselves (covered by PRD #5).
  • New codec IDs beyond reservation (covered by PRDs #5, #6).

Design

MediaHeader v2 (16 bytes, byte-aligned)

Byte 0:    version          (u8)   0x02
Byte 1:    flags            (u8)   bit 7: T (FEC repair)
                                   bit 6: Q (QualityReport trailer present, inside AEAD)
                                   bit 5: KeyFrame (video I-frame packet)
                                   bit 4: FrameEnd (last packet of access unit)
                                   bits 3-0: reserved (must be 0)
Byte 2:    media_type       (u8)   0=audio, 1=video, 2=data, 3=control
Byte 3:    codec_id         (u8)
Byte 4:    stream_id        (u8)   0=base; simulcast layers 1..N
Byte 5:    fec_ratio        (u8)   0..200 → 0.0..2.0
Bytes 6-9:   sequence       (u32 BE)
Bytes 10-13: timestamp_ms   (u32 BE)
Bytes 14-15: fec_block_id   (u16 BE)
                                   audio: low 8 bits = block_id, high 8 = symbol_idx
                                   video: full u16 block_id (large FEC blocks for I-frames)

Justification for byte alignment (16 B over 12 B packed) is in ROAD-TO-VIDEO.md Phase V1; benchmarks showed ≤ 0.32 % stream overhead delta across all scenarios.

MiniHeader v2 (5 bytes)

[FRAME_TYPE_MINI = 0x01]
Byte 0:    seq_delta            (u8)        ← new; resolves W4
Bytes 1-2: timestamp_delta_ms   (u16 BE)
Bytes 3-4: payload_len          (u16 BE)

Audio only. Video pays the full 16 B header per packet (no clean periodic structure to compress).

Version negotiation

CallOffer and CallAnswer already carry supported profiles. Add:

struct CallOffer {
    ...
    protocol_version: u8,         // 2 in v2 clients
    supported_versions: Vec<u8>,  // e.g. [2]
}

Relay/peer side:

  • If protocol_version is supported → proceed.
  • If unsupported → close with Hangup::ProtocolVersionMismatch { server_supported: Vec<u8> }.

No silent fallback. No mixed-version session.

Sequencing semantics

  • sequence is per-stream, monotonic, u32, wraps at 2^32. At 1000 pps that is ~50 days — effectively no wrap.
  • timestamp_ms is per-stream, milliseconds since session start, u32, ~49.7 days range. Rebase behavior at rekey: does not reset — kept monotonic across rekeys (documented as a separate hardening item in PRD #4, W3).
  • fec_block_id is per-stream, u16, wraps at 2^16. With ≥ 5-frame blocks that is ~22 minutes at 50 pps — adequate but PRD #4 (W2) covers epoch counter if needed.

Implementation outline

  1. New types in wzp-proto/src/packet.rs behind a proto-v2 feature flag.
  2. Round-trip tests for MediaHeader v2 and MiniHeader v2 (encode → decode → assert equal).
  3. Migrate wzp-codec encode path to emit v2 headers.
  4. Migrate wzp-client and wzp-relay parse paths.
  5. CallOffer/CallAnswer carry protocol_version and supported_versions.
  6. Typed Hangup::ProtocolVersionMismatch reason.
  7. Remove v1 emission path once all 571 tests pass under v2 (drop the feature flag default).
  8. Add migration note to WZP-SPEC.md.

Acceptance criteria

  • All 571 audio tests pass with v2 headers.
  • A v1 client connecting to a v2 relay receives Hangup::ProtocolVersionMismatch within 1 RTT.
  • Wire-level capture confirms 16 B MediaHeader and 5 B MiniHeader on real audio calls.
  • media_type byte readable by relay without parsing codec_id (enables PRD #2 Tier A separation).

Risks

  • Stranding old clients. Force-update prompt in UI; release notes; staged rollout (relays accept v1 for 2 weeks before flipping to reject).
  • MiniHeader 5 B vs 4 B regression check. Trunking math reconfirmed (cap of 10 binds before MTU — no change).

Effort

~2.5 engineer-days (Wave 1 tasks T1.1T1.3 in the index).