Files
wz-phone/docs/PRD/reports/T3.5-report.md
2026-05-12 06:45:56 +04:00

5.4 KiB
Raw Blame History

T3.5 — Tier E (per-fingerprint token bucket)

Status: Pending Review Agent: Kimi Code CLI Started: 2026-05-11T16:29Z Completed: 2026-05-11T16:29Z Commit: (see git log) PRD: ../PRD-relay-conformance.md

What I changed

  • crates/wzp-relay/src/conformance.rs:1 — Updated module doc: Tier A/B/C/DTier A/B/C/D/E.
  • crates/wzp-relay/src/conformance.rs:26-27 — Added Violation::RateCapExceeded variant for Tier E.
  • crates/wzp-relay/src/conformance.rs:30-76 — Added TokenBucket struct with:
    • capacity: u64, tokens: f64, refill_per_sec: u64, last_refill: Instant
    • new(capacity, refill_per_sec) constructor
    • for_audio_session() factory: 256 kbps cap, 30 s @ 2× burst = 1_920_000 byte capacity
    • try_consume(bytes, now) — refills based on elapsed time, then deducts cost
  • crates/wzp-relay/src/conformance.rs:84-85 — Added token_bucket: Option<TokenBucket> to ConformanceMeter.
  • crates/wzp-relay/src/conformance.rs:97-102 — Added ConformanceMeter::with_token_bucket(bucket) constructor.
  • crates/wzp-relay/src/conformance.rs:130-137 — Wired Tier E check into observe(): after Tier D, if a token bucket is present, attempt to consume the full wire size; return Err(Violation::RateCapExceeded) on exhaustion.
  • crates/wzp-relay/src/metrics.rs:409 — Added Violation::RateCapExceeded => "E" tier label.
  • crates/wzp-relay/src/room.rs:762-785 — Updated run_participant() signature to accept is_authenticated: bool and forward it to both plain and trunked loops.
  • crates/wzp-relay/src/room.rs:807-814 — Plain loop: creates ConformanceMeter::with_token_bucket(TokenBucket::for_audio_session()) for all participants (authed and anon share the same per-session audio cap).
  • crates/wzp-relay/src/room.rs:1042-1044 — Trunked loop: same token-bucket meter setup.
  • crates/wzp-relay/src/main.rs:2028 — Call site passes authenticated_fp.is_some() into run_participant().
  • crates/wzp-relay/src/conformance.rs:470-528 — Added 5 Tier E tests:
    • token_bucket_small_burst_ok — 50 KB inside 100 KB cap succeeds
    • token_bucket_large_burst_fails — 1 MB exceeds 100 KB cap
    • token_bucket_refills_over_time — drain, wait 1 s, consume refilled amount
    • token_bucket_sustained_rate_balanced — 32 KB/s for 5 s stays balanced
    • conformance_tier_e_integration — meter with 1_000-byte bucket, two 500-byte packets OK, third packet triggers RateCapExceeded

Why these choices

  • Used f64 for internal token tracking so fractional refills across sub-second intervals are accurate. The public API still speaks in whole bytes.
  • Both authenticated and anonymous participants get the same per-session audio cap (256 kbps / 1.92 MB burst). The spec's authed/anon split applies to the monthly quota (50 GB vs 1 GB), which is a separate accounting concern not covered by the per-session token bucket. Passing is_authenticated through the call chain makes it easy to add monthly-quota wiring later.
  • Tier E runs after Tiers AD so the cheaper checks still fire first on obvious abuse, while the token bucket catches the "low packet count, high burst size" tunneling vector.

Deviations from the task spec

  • The spec's TokenBucket sketch used AtomicU64 for tokens and last_refill. Since each ConformanceMeter (and its bucket) is owned by a single tokio task (the per-participant forwarding loop), atomics are unnecessary. I used plain f64 / Instant fields instead.

Verification output

$ cargo test -p wzp-relay token_bucket
running 4 tests
test conformance::tests::token_bucket_large_burst_fails ... ok
test conformance::tests::token_bucket_refills_over_time ... ok
test conformance::tests::token_bucket_small_burst_ok ... ok
test conformance::tests::token_bucket_sustained_rate_balanced ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 89 filtered out; finished in 0.00s
$ cargo test -p wzp-relay --lib
running 93 tests
...
test result: ok. 93 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
$ cargo test --workspace --exclude wzp-android --no-fail-fast
... (all crates pass)
Total: 617 passed; 0 failed

Test summary

  • Tests added: 5
    • token_bucket_small_burst_ok
    • token_bucket_large_burst_fails
    • token_bucket_refills_over_time
    • token_bucket_sustained_rate_balanced
    • conformance_tier_e_integration
  • Tests modified: 0
  • Workspace test count before: 612 / after: 617
  • cargo clippy -p wzp-relay --all-targets -- -D warnings: clean in wzp-relay; failures are pre-existing debt in wzp-codec (9 errors) and warzone-protocol (3 errors)
  • cargo fmt --all -- --check: pass

Risks / follow-ups

  • Monthly byte quota (50 GB authed / 1 GB anon) is not yet implemented. The is_authenticated flag is now threaded through the forwarding loop so a future task can add a per-fingerprint monthly counter alongside the per-session token bucket.
  • Video sessions will need TokenBucket::for_video_session() (5 Mbps cap) once video forwarding loops land in Wave 4.
  • Tier E is observe-only, consistent with Tiers AD. Hard enforcement (packet drop or session close) can be wired later if the reviewer wants.

Reviewer checklist (filled in by reviewer)

  • Code matches PRD intent
  • Verification output is real (re-run if suspicious)
  • No backward-incompat surprises
  • Tests cover the new behavior
  • Approved