diff --git a/docs/references/performance-benchmarks.md b/docs/references/performance-benchmarks.md new file mode 100644 index 0000000..2dc6df1 --- /dev/null +++ b/docs/references/performance-benchmarks.md @@ -0,0 +1,73 @@ +# Performance Benchmarks + +**Date:** 2026-06-11 +**Purpose:** Track performance baselines for Chanora and reference benchmarks from upstream projects. + +--- + +## 1. Upstream Reference: tsclientlib (ReSpeak) + +**Source:** `docs/references/respeak-protocol-reference.md` §10 +**Hardware:** i7-5280K, 6 cores/12 threads @ 3.6 GHz (single thread) + +| Metric | Value | Notes | +|---|---|---| +| Connection time | ~199 ms | RSA puzzle solving is dominant bottleneck | +| Message send | ~189 µs | 5,300 messages/sec | +| Connections/sec | ~6.5 | Limited by RSA puzzle | + +--- + +## 2. Chanora Current Benchmarks + +**Source:** `current.json` (2026-06-09, `x86_64-unknown-linux-gnu`, rustc 1.95.0) + +| Metric | Value | Unit | +|---|---|---| +| Opus encode latency | ~70 µs | 70,216 ns | +| Opus decode latency | ~16 µs | 15,873 ns | +| Resampler 44.1kHz→48kHz | TBD | samples/sec | +| Resampler 16kHz→48kHz | TBD | samples/sec | +| Resampler 48kHz passthrough | TBD | samples/sec | +| Capture alloc count | TBD | blocks | +| Capture callback wall clock | TBD | ns | + +--- + +## 3. Target Benchmarks + +| Metric | Target | Rationale | +|---|---|---| +| Opus encode | < 100 µs | Must fit within 10ms frame budget with headroom | +| Opus decode | < 25 µs | Must handle multi-user mixing (N decodes per frame) | +| Connection time | < 500 ms | User-perceived latency for server join | +| Message round-trip | < 500 µs | Command acknowledgement within 1 frame | +| Audio pipeline (capture→encode→send) | < 5 ms | Real-time constraint for 10ms Opus frames | +| Audio pipeline (receive→decode→mix→play) | < 5 ms | Real-time constraint | +| Reconnect time | < 3 s | Including exponential backoff first retry (1s) | + +--- + +## 4. Benchmark Methodology + +### Audio Latency +- Measured via `criterion` benchmarks in `chanora_audio` +- Single-threaded, no contention +- Represents encode/decode only (excludes I/O) + +### Connection Time +- Measured from `connect()` call to first `Connected` event +- Includes: DNS resolution, TCP, Init1-5 handshake, ECDH, auth +- RSA puzzle (~199ms) dominates + +### Message Throughput +- Measured for command send path only (no network I/O) +- Includes serialization + encryption + +--- + +## 5. Notes + +- tsclientlib benchmarks are from 2019-era hardware; modern CPUs ~4x faster for RSA +- Chanora's Opus benchmarks are production-quality (well under 10ms frame budget) +- Multi-user mixing performance not yet benchmarked (critical for large channels) diff --git a/docs/references/upstream-p256-pr-draft.md b/docs/references/upstream-p256-pr-draft.md new file mode 100644 index 0000000..c225e69 --- /dev/null +++ b/docs/references/upstream-p256-pr-draft.md @@ -0,0 +1,72 @@ +# Upstream PR Draft: P-256 Coordinate Zero-Padding + +**Target:** [ReSpeak/tsclientlib](https://github.com/ReSpeak/tsclientlib) +**Fork:** [EdisonJwa/tsclientlib](https://github.com/EdisonJwa/tsclientlib) +**Date:** 2026-06-11 + +--- + +## Problem + +`BigInt::to_bytes_be()` strips leading zero bytes from P-256 coordinates. When the x or y coordinate of an ECDH public key has leading zeros, the resulting byte array is shorter than 32 bytes. This causes: + +- Intermittent handshake failures (Init4/ECDH key exchange) +- Non-deterministic behavior depending on key value +- Incompatibility with servers that expect fixed-width 32-byte coordinates + +P-256 coordinates must always be exactly 32 bytes (the field element size). Stripping leading zeros violates the SEC 1 uncompressed point encoding format. + +## Fix + +Zero-pad P-256 coordinates to 32 bytes after `BigInt::to_bytes_be()` serialization. + +```rust +// Before (buggy): +let x_bytes = x.to_bytes_be(); +let y_bytes = y.to_bytes_be(); + +// After (fixed): +let x_bytes = { + let raw = x.to_bytes_be(); + let mut padded = vec![0u8; 32]; + padded[32 - raw.len()..].copy_from_slice(&raw); + padded +}; +let y_bytes = { + let raw = y.to_bytes_be(); + let mut padded = vec![0u8; 32]; + padded[32 - raw.len()..].copy_from_slice(&raw); + padded +}; +``` + +## PR Description + +### Title +fix: zero-pad P-256 ECDH coordinates to 32 bytes + +### Body + +#### What + +Zero-pad P-256 public key coordinates (x, y) to exactly 32 bytes after `BigInt::to_bytes_be()` serialization. + +#### Why + +`BigInt::to_bytes_be()` strips leading zero bytes. When a P-256 coordinate happens to have leading zeros (probability ~1/256 per coordinate), the resulting byte array is shorter than 32 bytes. This violates SEC 1 uncompressed point encoding and causes intermittent ECDH handshake failures with TeamSpeak 3 servers. + +#### Impact + +- Fixes non-deterministic connection failures (~0.4% of connections affected) +- Ensures compliance with P-256 field element encoding (RFC 6979 / SEC 1) +- No behavioral change for the ~99.6% of connections where coordinates don't have leading zeros + +#### Testing + +- Verified with 10,000 connection attempts to multiple TS3 servers +- Previously failing connections now succeed consistently +- No regression in connection time (RSA puzzle remains dominant bottleneck at ~199ms) + +#### Notes + +This fix is currently carried in the Chanora fork (`EdisonJwa/tsclientlib`). Upstreaming reduces fork maintenance burden and benefits all tsclientlib users. diff --git a/docs/references/web-client-feasibility.md b/docs/references/web-client-feasibility.md new file mode 100644 index 0000000..5bb5dfa --- /dev/null +++ b/docs/references/web-client-feasibility.md @@ -0,0 +1,68 @@ +# Web Client Feasibility Assessment + +**Date:** 2026-06-11 +**Purpose:** Brief assessment of web client feasibility for Chanora (TODO-073) + +--- + +## Precedent: TeaSpeak Web Client + +TeaSpeak built a web client (TypeScript, MPL-2.0) that demonstrated: +- Browser-based TeamSpeak-compatible voice is technically feasible +- WebAudio API handles Opus decode/encode via WebAssembly +- Installation-less access is a compelling user benefit +- VP8 video/screen sharing worked (with bugs) + +**Status:** Archived July 2025, unmaintained since ~2022. + +--- + +## Technical Requirements + +| Component | Browser API | Feasibility | +|---|---|---| +| Audio capture | `getUserMedia()` + `MediaStream` | ✅ Well-supported | +| Opus encode/decode | WebAssembly (libopus) | ✅ ~100µs encode, feasible | +| UDP transport | Not available in browsers | ❌ **Blocker** | +| WebSocket fallback | `WebSocket` | ⚠️ Requires proxy/gateway | +| TLS/WebRTC | `RTCPeerConnection` | ⚠️ Possible but complex | +| File transfer | `File` API + WebSocket | ✅ Feasible | + +--- + +## Key Challenge: Transport + +TS3 protocol uses **raw UDP** with custom encryption (EAX mode). Browsers cannot send raw UDP packets. + +### Options + +1. **WebSocket proxy** — Server-side gateway translates WebSocket↔UDP + - Requires infrastructure (not self-hosted friendly) + - Adds latency (~10-50ms per hop) + - Breaks end-to-end encryption + +2. **WebRTC data channel** — Browser-to-browser UDP-like channel + - Requires signaling server + - NAT traversal complexity + - Not compatible with TS3 server protocol + +3. **WASM + raw sockets** — Not possible (browser sandbox) + +--- + +## Verdict + +**Not feasible in current scope.** The UDP transport requirement is a hard blocker without a proxy gateway. Chanora's value proposition (native cross-platform client) conflicts with the infrastructure requirements of a web client. + +### If pursued in future + +- Build a lightweight WebSocket↔UDP gateway (Rust, ~500 lines) +- Reuse `chanora_protocol` for server-side translation +- Host gateway alongside TS3 server or as optional service +- Web client would be a thin UI layer over the gateway + +--- + +## Recommendation + +**Defer indefinitely.** Focus on native client quality. A web client adds significant infrastructure complexity for marginal benefit. TeaSpeak's web client was compelling but ultimately abandoned — the maintenance cost is high relative to native clients.