Commit Graph

541 Commits

Author SHA1 Message Date
Siavash Sameni
ca987d547c fix(android): return -6 on Oboe start timeout; fix error toast; add bug report
- oboe_bridge.cpp: return -6 (instead of silent 0) when streams do not
  reach Started within the 2s poll deadline; also clean up streams on
  that path so a retry can succeed
- main.ts: shared connectWithTimeout() so room-join and direct-call
  auto-connect both get the 15s JS timeout; shared errorMessage() so
  Tauri error objects don't show as [object Object] in toasts
- docs/bugs/001-android-join-voice-hang.md: comprehensive bug report
  with root cause chain, evidence, return code table, and next steps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 07:31:55 +04:00
Siavash Sameni
5a13f12334 fix(android): spawn_blocking for audio_start + 15s JS connect timeout
wzp_oboe_start is a sync FFI call that can block the OS thread
indefinitely waiting on the Android audio HAL. Calling it directly
from an async context freezes all tokio tasks including Rust-side
timeouts. Fix: run it via spawn_blocking so tokio stays responsive.

Also add a 15s Promise.race timeout in JS so a frozen audio_start
surfaces as "connect timed out — check audio permissions" instead of
the join button staying stuck in "Connecting…" forever.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 07:13:26 +04:00
Siavash Sameni
b0a3b1f18e fix: 10s timeout on handshake CallAnswer; button stays visible during connect
- handshake.rs: add 10s timeout on recv_signal() waiting for CallAnswer —
  previously hung forever if relay didn't respond, making join button
  disappear with no feedback
- main.ts: keep join button visible + show "Connecting…" state instead of
  hiding it before the await; button restores correctly on error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:59:57 +04:00
Siavash Sameni
32c07d1b61 fix(ui): show error toast + guard double-tap on join; ntfy relay deploy
- main.ts: add showToast() — surfaces Rust connect errors that were
  previously swallowed silently (key for diagnosing "never joins calls")
- main.ts: connectPending flag prevents double-tap race on Join Voice
  and CallSetup auto-connect; hides button while connect is in-flight
- build-linux-docker.sh: send ntfy notification per-server after each
  relay deploy (shows host + version deployed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:49:05 +04:00
Siavash Sameni
5d05b021aa fix(wzp-video): gate shiguredo AV1 crates to macOS only; fix Linux relay build
- Cargo.toml: merge duplicate [target.macos.deps] sections; move
  shiguredo_dav1d/svt_av1/video_toolbox into single block
- lib.rs: dav1d + svt_av1 modules and re-exports guarded by
  cfg(target_os = "macos") instead of cfg(not(android))
- factory.rs: AV1 encoder/decoder paths split into macos (svt-av1/dav1d)
  and linux fallback (NotInitialized); update doc comments and tests
- build-linux-docker.sh: build only wzp-relay + wzp-web (drops
  wzp-client which pulled in shiguredo crates); fix Docker copy step;
  add --deploy flag + deploy_relay(); fix branch auto-detection
- build-tauri-android.sh: default to release build, arm64 only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:33:35 +04:00
Siavash Sameni
4ac62d99e0 fix(audit): M1 — add version: u8 to all SignalMessage variants
Convert Hold/Unhold/Mute/Unmute/TransferAck from unit variants to struct
variants with `version: u8` (serde default = 2). Every SignalMessage
variant now carries a version field, enabling future semantic versioning
and clean rejection of deprecated variants during federation routing.

305 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:27:23 +04:00
Siavash Sameni
4ebb2dac2d feat(scripts): add --deploy flag to build-linux-docker.sh
Deploys wzp-relay to both relay servers after building:
- manwe@manwehs:/home/manwe/wzp (tmux session 5)
- manwe@pangolin.manko.yoga:/home/manwe/wzp-linux (tmux session 0)

Captures current relay args from /proc, stops via tmux C-c, restarts
with same args. Also fixes hardcoded branch default to use current git branch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:25:32 +04:00
Siavash Sameni
52a6f5e048 fix(audit): address C2, C3, M4, M5 from 2026-05-25 audit
C2: Add EncryptingTransport wrapper — all media I/O now goes through
ChaChaSession encrypt/decrypt before hitting the QUIC datagram path.
cli.rs run_live/run_silence/run_file_mode accept Arc<dyn MediaTransport>
and receive a wrapped transport after the handshake.

C3: Wire VideoScorer::observe() into both plain and trunked forwarding
loops in room.rs. Packets from participants with Abusive verdict are
dropped before forwarding. last_bwe_kbps tracked from quality reports.

M4: Widen FEC repair symbol index from u8 to u16 throughout
(FecEncoder::generate_repair, FecDecoder::add_symbol, all call sites in
call.rs, bench.rs, pipeline.rs, wzp-android). Eliminates theoretical
wrapping when num_source + repair_count > 255.

M5: Track last_encrypt_timestamp in ChaChaSession. debug_assert in
encrypt() that timestamp is non-decreasing across calls (including post-
rekey). complete_rekey() explicitly preserves last_encrypt_timestamp to
prevent accidental timestamp reset regressions.

583 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:20:05 +04:00
Siavash Sameni
15af58a95d fix(wzp-video): fix ndk 0.9 MediaCodec API + missing constants for Android build
- Replace buffer.index() with buffer.buffer_mut()/buffer.buffer() (ndk 0.9 RAII API)
- Replace queue_input_buffer_by_index/release_output_buffer_by_index with
  queue_input_buffer/release_output_buffer taking buffer objects
- Fix MaybeUninit<u8> copy using .write() instead of copy_from_slice
- Add BITRATE_MODE_CBR and AMEDIACODEC_BUFFER_FLAG_KEY_FRAME local constants
  (removes ndk_sys dependency for these values)
- Add unsafe impl Send for all six MediaCodec wrapper structs
- Pin @tauri-apps/api to ^2.11 to match Cargo.lock tauri 2.11.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:05:58 +04:00
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
Siavash Sameni
12b0d9738f fix(wzp-crypto): derive AEAD nonces from MediaHeader.seq, not recv_seq
The previous scheme built ChaCha20-Poly1305 nonces from an internal
recv_seq counter that incremented once per decrypt() call. Under
in-order delivery recv_seq stayed in sync with the sender's send_seq,
but any out-of-order or lost packet caused them to diverge permanently —
every subsequent packet then used the wrong nonce and AEAD decryption
failed for the rest of the session.

Fix: parse the MediaHeader at the top of both encrypt() and decrypt()
and use header.seq as the nonce input. Both sides now derive the nonce
from the same wire field, surviving reordering by construction.

send_seq / recv_seq are kept as pure packet counters for the rekey
interval trigger; they no longer affect nonce derivation.

All tests updated to pass valid v2 MediaHeader bytes instead of raw
byte literals (the new code requires a parseable header for nonce
derivation). New test decrypt_survives_out_of_order_delivery encrypts
5 packets and delivers them out of order (indices 0,2,1,4,3); this
test would have failed under the old counter-based scheme.

Fixes audit finding C1 from AUDIT-2026-05-25.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 06:00:01 +04:00
Siavash Sameni
f78794f4b6 chore: pin @tauri-apps/api to ^2.11 to match Cargo.lock 2026-05-25 05:55:20 +04:00
Siavash Sameni
f3e3ee5ed0 fix(wzp-video): cfg-gate dav1d + svt-av1 off Android target
shiguredo_dav1d and shiguredo_svt_av1 build scripts panic with
'unsupported target: os=android, arch=aarch64'. The AV1 SW fallback
is only needed on macOS / Linux desktop — Android uses MediaCodec
for AV1 anyway.

- Cargo.toml: AV1 SW deps moved under cfg(not(target_os = "android"))
- lib.rs: cfg-gate the dav1d and svt_av1 modules and re-exports
- factory.rs: on Android, Av1Main paths return NotInitialized when
  HW MediaCodec is also unavailable (only path on Android)
- factory tests: assert NotInitialized on Android, Ok elsewhere

Unblocks T4.3.1.1 (Android target-compile of wzp-video / mediacodec).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:58:37 +04:00
Siavash Sameni
f28f39d814 ci: gitleaks allowlist for historical findings
Two pre-existing PASTE_AUTH tokens in scripts/build.sh and
scripts/build-linux-notify.sh are real and should be rotated if the
paste.tbs.amn.gg / paste.dk.manko.yoga endpoints still authenticate
— this allowlist only silences the pre-push hook, it does not
remove the exposure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:51:51 +04:00
Siavash Sameni
1e729e4b1d T6.3: Design exploration for federated reputation gossip
Add docs/PRD/PRD-relay-federation-gossip.md comparing 3 approaches:
1. Push gossip — relay broadcasts RepeatAbusive verdicts to peers
2. Pull oracle — peers query a reputation oracle periodically
3. Ban-list distribution — admin signs and pushes authoritative list

For each: wire format, Sybil resistance, convergence, storage,
partition tolerance, failure modes. Open questions block implementation
(trust model, privacy leakage, key infrastructure). Move T6.3 to Blocked
pending reviewer design call.
2026-05-12 19:13:31 +04:00
Siavash Sameni
086d0a4845 T6.1.2: Wire AV1 into call engine (factory + step tables)
- New: factory.rs — create_video_encoder/decoder dispatch by CodecId with
  platform-aware HW→SW fallback. AV1 encoder: SvtAv1Encoder (universal SW).
  AV1 decoder: VideoToolboxAv1Decoder (macOS M3+) → MediaCodecAv1Decoder
  (Android) → Dav1dDecoder (all platforms fallback).
- controller.rs: codec-specific step tables (H.264/H.265/AV1). AV1 ~30%
  lower thresholds than H.264; H.265 ~20% lower. VideoQualityController
  gains codec field with with_codec()/set_codec()/codec() accessors.
- lib.rs: export factory fns and VideoToolboxAv1Decoder
- wzp-client/Cargo.toml: add wzp-video dependency
- 11 new tests (7 factory + 4 controller); 77→88 wzp-video tests; fmt +
  clippy clean; all workspace tests pass
2026-05-12 19:05:45 +04:00
Siavash Sameni
9334aa5ccd T6.1: AV1 encoder/decoder with HW probe + SVT-AV1 SW fallback
- New: av1_obu.rs — OBU framer, depacketizer, keyframe detection, LEB128 helpers
- New: dav1d.rs — SW AV1 decoder wrapper (shiguredo_dav1d)
- New: svt_av1.rs — SW AV1 encoder wrapper (shiguredo_svt_av1)
- Add CodecId::Av1Main = 12 with match-arm fixes in downstream crates
- Add VideoToolboxAv1Decoder for macOS M3+ HW decode
- Add MediaCodecAv1Encoder/Decoder for Android (video/av01)
- Add extract_sequence_header_obu() helper for AV1 decoder CSD
- Add 10-frame encode-decode roundtrip test (svt_av1 + dav1d)
- Fix clippy unused import in dav1d.rs
- 15 tests; all workspace tests pass; cargo fmt clean
2026-05-12 18:44:44 +04:00
Siavash Sameni
553c8a4ce1 T6.1 plan: expand skeleton with files/steps/verify/done-when for AV1 encoder/decoder 2026-05-12 18:08:27 +04:00
Siavash Sameni
8d8dddbd35 docs: add T6.2 report and update status board to Pending Review 2026-05-12 17:45:04 +04:00
Siavash Sameni
f16d650721 T6.2: Tier F video scorer — keyframe periodicity, I/P ratio, BWE responsiveness + 10 tests 2026-05-12 17:42:39 +04:00
Siavash Sameni
31f2fdef1e T6.2 plan: expand skeleton with files/steps/verify/done-when for video scorer 2026-05-12 17:14:25 +04:00
Siavash Sameni
fc9908cd4c docs: fix commit SHA in T5.7.1 report 2026-05-12 16:49:16 +04:00
Siavash Sameni
517d0ebfe0 T5.7.1: Unify Verdict enum into wzp_relay::verdict, drop RepeatAbusive variant 2026-05-12 16:49:04 +04:00
Siavash Sameni
cf4940417e docs: add T5.1.1–T5.8 reports and update status board to Pending Review 2026-05-12 15:41:28 +04:00
Siavash Sameni
ffded2a913 clippy: fix wzp-relay lint issues (empty doc, unused var, TokenExhausted, Default, dead field) 2026-05-12 15:40:55 +04:00
Siavash Sameni
283edd38eb clippy: fix very_complex_type in wzp-video (HevcParameterSets alias) 2026-05-12 15:40:19 +04:00
Siavash Sameni
fdfaed5390 fmt: cargo fmt --all 2026-05-12 15:40:02 +04:00
Siavash Sameni
dbbab0decf T5.8: Tier G response policy — Verdict enum + ResponsePolicy + typed Hangup::PolicyViolation + 9 tests 2026-05-12 15:13:20 +04:00
Siavash Sameni
5fda5ecc52 T5.7: Tier F audio scorer — IAT CoV + silence fraction + bitrate + Q-flag + bimodality + 11 tests 2026-05-12 15:09:28 +04:00
Siavash Sameni
2bbb664df4 T5.6: Per-receiver layer selection at SFU — ReceiverState + hysteresis + forwarding filter 2026-05-12 15:05:32 +04:00
Siavash Sameni
2f1a9f74d5 T5.5: 3-layer simulcast at sender — SimulcastEncoder + tick_simulcast() + 10 tests 2026-05-12 14:56:48 +04:00
Siavash Sameni
b197651557 T5.4: H.265 encoder/decoder wrappers — VideoToolbox + MediaCodec, CodecId::H265Main 2026-05-12 14:50:20 +04:00
Siavash Sameni
9c41d1acdd T5.3 status: Approved (reviewer update) 2026-05-12 14:50:12 +04:00
Siavash Sameni
e34c40dc0f T5.1.1: PriorityMode default = AudioFirst, QualityProfile backward-compat JSON, SetPriorityMode roundtrip 2026-05-12 14:50:06 +04:00
Siavash Sameni
c48cb6fbcb T5.3: EncoderMode::SlideFallback — SD-floor detection + VideoEncoder::set_mode() trait hook 2026-05-12 12:40:53 +04:00
Siavash Sameni
2e0bdc5904 T5.2: VideoQualityController with per-mode allocation gates + 8-step target table 2026-05-12 12:34:32 +04:00
Siavash Sameni
276ecc660e T5.1: PriorityMode enum + SetPriorityMode signal; extend QualityProfile with video fields 2026-05-12 12:21:40 +04:00
Siavash Sameni
001d94f9ae T4.7 rework: make should_forward_pli take now: Instant + 6 unit tests
- Refactor should_forward_pli(room, stream_id) -> should_forward_pli(room, stream_id, now: Instant)
  so the 200 ms dedup window is deterministically testable.
- Update the one caller in run_participant_signals to pass Instant::now().
- Add 6 PLI unit tests covering:
  * first PLI forwards
  * duplicate within 200 ms suppressed
  * after 200 ms forwards again
  * different streams independent
  * different rooms independent
  * no stream owner returns None

Addresses reviewer CR on T4.7 (line drawn at T4.6 — stateful relay features must
have state-transition tests).

wzp-relay tests: 93 -> 99 pass.
2026-05-12 11:39:35 +04:00
Siavash Sameni
36b0421d68 T4.7: PLI suppression at SFU — 200 ms dedup window per (room, stream_id) 2026-05-12 11:25:25 +04:00
Siavash Sameni
828fbea2ea T4.6: SFU keyframe cache — per-(room,sender,stream) I-frame replay on join 2026-05-12 10:54:04 +04:00
Siavash Sameni
cc5aef2534 T4.5: I-frame FEC ratio boost — keyframe-aware repair ratio in RaptorQFecEncoder
- Add add_source_symbol_with_keyframe() default method to FecEncoder trait
- RaptorQFecEncoder tracks has_keyframe per block, uses keyframe_ratio
  when generating repair symbols for keyframe blocks
- AdaptiveFec gains keyframe_repair_ratio (default 0.5) and wires it
  through build_encoder()
- 3 new tests: keyframe boost, non-keyframe nominal ratio, finalize clears flag
- Update status board T4.5 -> Pending Review
2026-05-12 10:36:18 +04:00
Siavash Sameni
397f9d2141 T4.3.1: MediaCodec AMediaCodec wiring via ndk crate (Android); fix wzp-android build on non-Android 2026-05-12 10:03:43 +04:00
Siavash Sameni
410c2a4335 T4.2.1: Real VideoToolbox VTCompressionSession / VTDecompressionSession wiring (macOS) 2026-05-12 09:51:34 +04:00
Siavash Sameni
81042ac190 T4.4: SignalMessage::Nack + PictureLossIndication; NACK sender/receiver state machines 2026-05-12 09:25:29 +04:00
Siavash Sameni
e177e63843 T4.3: MediaCodec H.264 encoder/decoder stub (Android) 2026-05-12 09:15:06 +04:00
Siavash Sameni
1f7d130de9 fix: T4.2 status board → Pending Review 2026-05-12 09:10:50 +04:00
Siavash Sameni
3356ba94c6 T4.2: VideoToolbox H.264 encoder/decoder traits (macOS, MVP) 2026-05-12 09:09:57 +04:00
Siavash Sameni
bb153a331d fix: T4.1 status board → Pending Review 2026-05-12 07:23:15 +04:00
Siavash Sameni
490d2d31c6 T4.1: wzp-video crate scaffold + H.264 NAL framer + depacketizer 2026-05-12 07:22:54 +04:00
Siavash Sameni
db69f7e9d1 fix: T3.5 status board → Pending Review 2026-05-12 06:46:28 +04:00