Commit Graph

441 Commits

Author SHA1 Message Date
Siavash Sameni
bf56d84ef0 test: 17 new tests for S-4/5/6/7/9 integration tasks
S-4 Room hashing + ACL (8 tests in featherchat_compat.rs):
- hash_room_name: deterministic, 32 hex chars, different inputs differ
- hash_room_name_matches_fc_convention: manual SHA-256 verification
- room_acl: open mode, enforced mode, allow-listed, deny-unlisted

S-5 Handshake integration (4 tests in handshake_integration.rs):
- handshake_succeeds: real QUIC, encrypt/decrypt cross-verified
- handshake_verifies_identity: different seeds, session still works
- auth_then_handshake: AuthToken + CallOffer/Answer in sequence
- handshake_rejects_bad_signature: tampered sig → error

S-6/7/9 Web+Proto+TLS (5 tests in featherchat_compat.rs):
- auth_response_with_eth_address: FC's extra field handled
- wzp_proto_has_auth_token_variant: serialize/deserialize roundtrip
- all_fc_call_signal_types_representable: all 7 types verified
- hash_room_name_used_as_sni_is_valid: unicode/special chars → valid hex
- wzp_proto_cargo_toml_is_standalone: no workspace inheritance

196 total tests passing across all crates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 10:09:34 +04:00
Siavash Sameni
59069bfba2 feat: complete all WZP-S integration tasks (S-4/5/6/7/9)
WZP-S-4: Room access control
- hash_room_name() in wzp-crypto: SHA-256("featherchat-group:"+name)[:16]
- CLI --room flag hashes before SNI, web bridge does the same
- RoomManager gains ACL: with_acl(), allow(), is_authorized()
- join() returns Result, rejects unauthorized fingerprints

WZP-S-5: Crypto handshake wired into all live paths
- CLI: perform_handshake() after connect, before any mode
- Relay: accept_handshake() after auth, before room join
- Web bridge: perform_handshake() after auth, before audio
- Relay generates ephemeral identity at startup

WZP-S-6: Web bridge featherChat auth
- --auth-url flag: browsers send {"type":"auth","token":"..."} as first WS msg
- Validates against featherChat, passes token to relay
- --cert/--key flags for production TLS (replaces self-signed)

WZP-S-7: wzp-proto standalone
- Cargo.toml uses explicit versions (no workspace inheritance)
- FC can use as git dependency

WZP-S-9: All 6 hardcoded assumptions resolved
- Auth, hashed rooms, mandatory handshake, real TLS certs,
  profile negotiation, token validation

CLI also gains --room and --token flags.
179 tests passing across all crates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:59:05 +04:00
Siavash Sameni
26dc848081 test: 15 cross-project integration tests — WZP ↔ featherChat verified
Identity (6 tests):
- Same seed → same Ed25519/X25519 keys, same fingerprint, same display
- Random seed, raw HKDF output verified

BIP39 Mnemonic (3 tests):
- Roundtrip both directions, identical strings

CallSignal Interop (4 tests):
- Offer/Answer/Hangup roundtrip through FC bincode serialization
- Signal type mapping verified

Auth Contract (2 tests):
- Request/response shapes match between WZP and FC

Uses warzone-protocol v0.0.21 as real dependency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:39:04 +04:00
Siavash Sameni
ad16ddb903 feat: WZP-S-2 relay auth + WZP-S-3 featherChat signaling bridge
WZP-S-2: Relay token authentication
- New --auth-url flag: relay calls POST {url} with bearer token
- Clients must send SignalMessage::AuthToken as first signal
- Relay validates against featherChat's /v1/auth/validate endpoint
- Rejects unauthenticated clients before they join rooms
- New auth.rs module with validate_token() + tests

WZP-S-3: featherChat signaling bridge
- New featherchat.rs module for CallSignal interop
- WzpCallPayload: wraps SignalMessage + relay_addr + room name
- encode_call_payload/decode_call_payload for JSON serialization
- CallSignalType enum mirrors featherChat's variant
- signal_to_call_type maps WZP signals to FC types

Protocol: Added SignalMessage::AuthToken { token } variant

129 tests passing across all crates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:23:46 +04:00
Siavash Sameni
d870c9e08a docs: mark WZP-FC-1 and WZP-FC-4 as DONE (featherChat v0.0.21)
featherChat commit 064a730 implements:
- CallSignal WireMessage variant with Offer/Answer/ICE/Hangup/Reject/Ringing/Busy
- POST /v1/auth/validate endpoint returning fingerprint + alias

WZP can now:
- Send SignalMessage as JSON in CallSignal.payload through FC's E2E channel
- Verify FC bearer tokens on the relay via the validate endpoint

Next: WZP-S-2 (relay auth) and WZP-S-3 (signaling bridge in client)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:16:52 +04:00
Siavash Sameni
616505e8a9 docs: shared crate strategy for WZP ↔ featherChat interop
Defines 5 tasks (FC-CRATE-1/2/3, WZP-CRATE-1/2) to make both
projects' crates importable by each other:

featherChat side:
- FC-CRATE-1: Make warzone-protocol standalone (replace workspace deps)
- FC-CRATE-2: Add CallSignal variant using wzp-proto types
- FC-CRATE-3: Extract warzone-identity micro-crate (optional)

WZP side (after FC-CRATE-1):
- WZP-CRATE-1: Replace identity mirror with real warzone-protocol dep
- WZP-CRATE-2: Verify wzp-proto works as git dep from featherChat

Priority: FC-CRATE-1 first (30 min, unblocks everything).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:14:25 +04:00
Siavash Sameni
12cdfe6c8a feat: featherChat-compatible identity — seed, mnemonic, fingerprint
New identity module (wzp-crypto/src/identity.rs) mirrors featherChat's
warzone-protocol identity.rs exactly:
- Seed: 32 bytes, from hex or BIP39 mnemonic (24 words)
- HKDF derivation: same salt (None), same info strings
- Fingerprint: SHA-256(Ed25519 pub)[:16], same xxxx:xxxx format
- Cross-verified: test proves identity module matches KeyExchange trait

CLI flags:
- --seed <64 hex chars>: use a specific identity
- --mnemonic <24 words>: use BIP39 mnemonic from featherChat
- Without either: generates ephemeral identity

Also adds featherChat as git submodule at deps/featherchat for reference.

32 crypto tests passing (27 original + 5 identity tests).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:09:38 +04:00
Siavash Sameni
97402f6e60 docs: integration task tracker from featherChat commit 65f6390
Maps all WZP-S-* (our side) and WZP-FC-* (featherChat side) tasks
with status tracking and priority order.

Key findings:
- WZP-S-1 (HKDF alignment): DONE — both use None salt, info strings match
- WZP-S-9: 6 hardcoded assumptions documented for fixing
- Priority: identity test → CLI seed → CallSignal variant → auth → handshake

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 08:56:26 +04:00
Siavash Sameni
237adbbf21 chore: update Cargo.lock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 08:25:22 +04:00
Siavash Sameni
ac3b997758 fix: align HKDF info strings with featherChat identity derivation
Changed HKDF expand info strings to match featherChat's identity.rs:
- "warzone-ed25519-identity" → "warzone-ed25519"
- "warzone-x25519-identity" → "warzone-x25519"

Same BIP39 seed now produces identical Ed25519/X25519 keypairs in both
featherChat and WZP. This is the prerequisite for shared identity.

Also added FEATHERCHAT_INTEGRATION.md (1209 lines) from featherChat repo
documenting the full integration plan with confirmed code references.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 08:16:57 +04:00
Siavash Sameni
5425c59e7d docs: comprehensive project documentation
- ARCHITECTURE.md: protocol design, wire format, FEC, crypto, relay modes
- USAGE.md: build instructions, all CLI flags, deployment examples
- DESIGN.md: rationale for codec/FEC/transport/crypto choices
- EXTENSIBILITY.md: trait extension points, Warzone integration, future features
- PROGRESS.md: phase 1-4 timeline, test coverage, known issues
- API.md: complete crate API reference for all 8 crates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 05:30:11 +04:00
Siavash Sameni
d8330525ef feat: multi-party rooms (SFU) + push-to-talk radio mode
Room-based SFU relay:
- Clients join named rooms (room name from QUIC SNI)
- Each participant's packets forwarded to all others (no mixing)
- Multiple rooms run concurrently on one relay
- Web bridge passes room name from URL path to relay

Push-to-talk (radio mode):
- Toggle "Radio mode" checkbox after connecting
- Hold PTT button or spacebar to transmit
- Release to mute mic (receive-only)
- Works on desktop (spacebar) and mobile (touch)

URL routing:
- /myroom → joins room "myroom"
- Room name input field as fallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:36:19 +04:00
Siavash Sameni
b65f76e4db ci: Gitea Actions build workflow for multi-arch binaries
Triggers:
- On tag push (v*): builds amd64, creates release with artifacts
- Manual dispatch: select targets (amd64, arm64, armv7)

Targets:
- linux/amd64: full build with headless + audio client
- linux/arm64: Raspberry Pi 4/5, cross-compiled (headless)
- linux/armv7: Raspberry Pi 3/Zero 2W, cross-compiled (headless)

Each target produces a tarball with:
  wzp-relay, wzp-client, wzp-web, wzp-bench, static/

Uses rust:1-bookworm container to match Debian 12 glibc.
Cargo cache keyed on Cargo.lock for faster rebuilds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:22:42 +04:00
Siavash Sameni
12b6f30f9b feat: room-based calls + AudioWorklet for capture and playback
Rooms:
- URL-based: open /myroom to join a room
- Two clients in same room get bridged through relay
- Input field for room name, also supports URL path and hash
- Each room creates independent relay connections

AudioWorklet (replaces deprecated ScriptProcessorNode):
- capture-processor.js: accumulates mic samples, sends 960-sample frames
- playback-processor.js: pull-based output with 200ms buffer cap
- Falls back to ScriptProcessor if AudioWorklet unavailable
- Eliminates drift: worklet runs on audio thread, not main thread

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:16:06 +04:00
Siavash Sameni
722bca0c87 fix: remove unused warn import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:01:03 +04:00
Siavash Sameni
d38c655e79 fix: install rustls crypto provider in relay (same as wzp-web fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:55:56 +04:00
Siavash Sameni
ce6aacb25f fix: bridge pairing + auto-reconnect + test stability
Bridge mode rewrite:
- First client echoes while waiting, checks every 100ms if paired
- Second client triggers bridge immediately, first exits echo loop
- After bridge ends, slot is cleared for the next pair
- No more two tasks competing for the same transport recv

Web client auto-reconnect:
- On WebSocket close/error, automatically reconnects after 1s
- Keeps retrying as long as the user hasn't clicked Disconnect

Test fix:
- Install rustls crypto provider in transport config tests
  (fixes race condition when running full workspace tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:49:27 +04:00
Siavash Sameni
38ae62b542 fix: raise drift cap to 1s — stops constant resetting on jittery links
150ms cap was too tight for Iran relay (high jitter), causing constant
audio drops. Raised to 1s — packet bursts are absorbed smoothly,
drift reset only fires on real accumulation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:41:45 +04:00
Siavash Sameni
709ad1ba7d fix: revert to scheduled playback with 200ms drift cap
Pull-based ScriptProcessor approach broke audio completely.
Back to createBufferSource scheduling which worked, but with
tighter 200ms max drift (was 300ms). Snaps back when exceeded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:31:26 +04:00
Siavash Sameni
1c91c4a1b5 fix: sample-accurate playback buffer eliminates robotic audio
Previous version output 960 samples into 1024-sample callback frames,
causing 64 samples of silence per frame (choppy/robotic sound).

Now accumulates float samples in a continuous buffer, output callback
pulls exactly 1024 at a time regardless of input frame size.
Buffer capped at 200ms to prevent drift.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:29:52 +04:00
Siavash Sameni
4de72e2d98 fix: pull-based audio playback eliminates drift + rustls crypto provider
Web playback rewritten from push-scheduling to pull-based ring buffer:
- ScriptProcessorNode pulls frames from buffer every ~21ms
- Buffer capped at 10 frames (~200ms) — drops oldest on overflow
- Latency permanently bounded, no drift over time

Also: install ring crypto provider for rustls TLS on Linux,
       build on debian-12 to match mequ glibc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:26:59 +04:00
Siavash Sameni
61d6fb173d feat: HTTPS support for web bridge (--tls flag)
Generates a self-signed certificate at startup for HTTPS.
Required for mic access on Android/remote browsers (getUserMedia
needs a secure context).

Usage: wzp-web --port 9090 --relay 127.0.0.1:4433 --tls
Browser: accept the self-signed cert warning, then mic works.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:56:00 +04:00
Siavash Sameni
66f720f1ee fix: cap web playback latency at 300ms — prevents drift accumulation
When playback buffer drifts beyond 300ms ahead of real-time, reset
to 40ms. This prevents the unbounded latency growth over long sessions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:44:33 +04:00
Siavash Sameni
7fce83be82 build: include wzp-web + static files in Linux build script
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:35:23 +04:00
Siavash Sameni
9ad21182a8 fix: web audio playback quality — gapless scheduling + sample rate debug
- Schedule each playback buffer to start exactly where the last ended
  (was causing gaps/overlaps with fixed 60ms offset)
- Log AudioContext sample rate to console for debugging
- Reset playback timeline when falling behind

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:29:00 +04:00
Siavash Sameni
a7afe4ff21 fix: web audio capture buffer size + relay warning
- Use power-of-2 buffer (1024) for ScriptProcessorNode
- Accumulate samples and send exact 960-sample frames
- Remove unused watch import from relay

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:27:08 +04:00
Siavash Sameni
3f128936c4 feat: web bridge — browser-based voice calls via WebSocket
New wzp-web crate serves a web page with:
- Browser mic capture via Web Audio API (48kHz mono)
- WebSocket transport for raw PCM audio
- Server-side Opus encode/decode + FEC through wzp relay
- Real-time audio playback in browser
- Level meter and connection stats

Usage:
  wzp-relay --listen 0.0.0.0:4433    # start relay
  wzp-web --port 8080 --relay 127.0.0.1:4433  # start web bridge
  Open http://localhost:8080 in browser

Two browsers connected to the same relay get bridged for a call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:23:39 +04:00
Siavash Sameni
bddcfb1440 fix: remove unused variable warning in cli.rs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:15:09 +04:00
Siavash Sameni
a04b8271cc fix: record mode decode-per-packet (same fix as live mode)
The --record recv loop was using while-drain which exhausted the jitter
buffer and stopped decoding after the first burst. Now decodes once per
source packet, matching the live mode fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:05:10 +04:00
Siavash Sameni
d5390db7af feat: --send-file for real audio testing + fix warnings
- --send-file <file.raw> sends a raw PCM file (48kHz mono s16le) through relay
- Combine with --record: --send-file talk.raw --record echo.raw <relay>
- Fixed all unused import warnings in echo_test.rs

Convert any audio to test format:
  ffmpeg -i input.mp3 -ar 48000 -ac 1 -f s16le input.raw

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:51:55 +04:00
Siavash Sameni
28d5a3a9ad feat: automated echo quality test with time-window analysis
New --echo-test <secs> flag sends a 440Hz tone through relay echo,
records the return, and analyzes quality in 5-second windows:
- Per-window: frames sent/received, loss %, SNR (dB), correlation
- Detects quality degradation over time (compares first vs second half)
- Reports jitter buffer stats (depth, lost, late packets)
- Diagnoses jitter buffer drift and packet loss accumulation

Also exposes jitter_stats() on CallDecoder for diagnostics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:44:08 +04:00
Siavash Sameni
26ed015cca feat: relay bridge mode — pairs two clients for real calls
When no --remote is configured, the relay now operates in bridge mode:
- First client connects → echoes while waiting for a peer
- Second client connects → both clients are bridged bidirectionally
- A's packets go to B, B's packets go to A
- Stats logged every 5 seconds (a_to_b / b_to_a packet counts)
- Falls back to echo if only one client connects

This enables the core use case: two clients on different networks
calling each other through a single relay.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:21:12 +04:00
Siavash Sameni
0723f52d76 fix: live audio playback working — jitter buffer and decode loop fixes
- Reduced jitter buffer min_depth from 25 (500ms) to 3 (60ms) for fast start
- Fixed live recv loop: decode once per source packet instead of draining
  the jitter buffer dry (which advanced seq past future packets)
- Fixed Ok(None) handling: connection closed, not "no packet yet"

Live echo test confirmed working with continuous audio.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:09:33 +04:00
Siavash Sameni
b147de5ae9 fix: graceful Ctrl+C recording + relay echo mode
- --record now handles Ctrl+C: saves PCM file before exiting
- Relay without --remote runs in echo mode (loops packets back to sender)
  instead of sink mode, enabling single-relay audio testing
- recv task returns collected PCM via channel for clean file write

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:32:12 +04:00
Siavash Sameni
df80ad5343 fix: make cpal/ALSA optional — headless Linux builds work without libasound
- cpal is now behind an 'audio' feature flag (off by default)
- --live mode requires --features audio at build time
- --send-tone and --record work on headless servers without audio libs
- Linux build script no longer installs libasound2-dev

Build for headless: cargo build --release
Build with mic/speakers: cargo build --release --features audio

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:24:44 +04:00
Siavash Sameni
708fb268bc feat: file-based audio testing + Hetzner build scripts
CLI modes:
- --send-tone <secs>: send 440Hz test tone (no mic needed)
- --record <file.raw>: save received audio to raw PCM file
- --help: usage info
- Combine: --send-tone 10 --record out.raw

Raw PCM format: 48kHz mono s16le
Play with: ffplay -f s16le -ar 48000 -ac 1 out.raw

Build scripts:
- scripts/build-linux.sh: Hetzner VPS build with auto-cleanup
- scripts/cleanup-builder.sh: kill stale builders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:11:59 +04:00
Siavash Sameni
85f472d824 fix: scale FEC ratio with loss rate in benchmarks
The bench tool now auto-calculates the FEC ratio needed to survive
the requested loss percentage, matching how the adaptive quality
controller would behave in production.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:21:21 +04:00
Siavash Sameni
3c99503eb1 fix: IPv6 support, client address family matching, gitignore cleanup
- Client auto-detects IPv4/IPv6 from relay address and binds accordingly
- Relay defaults to 0.0.0.0:4433, use --listen [::]:4433 for IPv6
- .gitignore excludes .claude/, swap files
- Fix pipeline drain infinite loop in benchmarks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:17:49 +04:00
Siavash Sameni
79f9ff1596 feat: Phase 3 — crypto handshake, codec2, benchmarks, audio I/O, relay forwarding
E2E crypto handshake:
- Client/relay handshake via SignalMessage (CallOffer/CallAnswer)
- X25519 ephemeral key exchange with Ed25519 identity signatures
- Integration tests proving bidirectional encrypt/decrypt

Codec2 integration:
- Pure Rust codec2 crate (v0.3) — no C bindings needed
- MODE_3200 (160 samples/20ms, 8 bytes) and MODE_1200 (320 samples/40ms, 6 bytes)
- 11 new tests including encode/decode roundtrip and adaptive switching

Relay forwarding:
- Bidirectional client → remote forwarding with pipeline processing
- CLI args: --listen, --remote
- Periodic stats logging, clean shutdown via tokio::select!

Benchmark tool (wzp-bench):
- Codec roundtrip, FEC recovery, crypto throughput, full pipeline benchmarks
- Sine wave PCM generator for realistic testing

Audio I/O (cpal):
- AudioCapture (microphone) and AudioPlayback (speakers) at 48kHz mono
- CLI --live mode: mic → encode → send / recv → decode → speakers

120 tests passing, 0 failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:43:22 +04:00
Siavash Sameni
43d7f70fe9 feat: Phase 2 — relay daemon and client library with integration pipelines
wzp-relay:
- RelayPipeline: ingest → FEC decode → jitter buffer → FEC encode → send
- SessionManager: tracks active calls, idle expiry
- RelayConfig: TOML-based configuration
- Binary: accepts QUIC connections, receives media packets

wzp-client:
- CallEncoder: mic PCM → Opus encode → FEC → MediaPackets
- CallDecoder: MediaPackets → FEC decode → jitter → Opus decode → PCM
- CLI binary: connects to relay, sends test silence frames

99 tests passing across all 7 crates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:08:33 +04:00
Siavash Sameni
51e893590c feat: WarzonePhone lossy VoIP protocol — Phase 1 complete
Rust workspace with 7 crates implementing a custom VoIP protocol
designed for extremely lossy connections (5-70% loss, 100-500kbps,
300-800ms RTT). 89 tests passing across all crates.

Crates:
- wzp-proto: Wire format, traits, adaptive quality controller, jitter buffer, session FSM
- wzp-codec: Opus encoder/decoder (audiopus), Codec2 stubs, adaptive switching, resampling
- wzp-fec: RaptorQ fountain codes, interleaving, block management (proven 30-70% loss recovery)
- wzp-crypto: X25519+ChaCha20-Poly1305, Warzone identity compatible, anti-replay, rekeying
- wzp-transport: QUIC via quinn with DATAGRAM frames, path monitoring, signaling streams
- wzp-relay: Integration stub (Phase 2)
- wzp-client: Integration stub (Phase 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 12:45:07 +04:00