From f7a517d8eaca3d2e9c8bb422bc8484c34e1fca60 Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Sat, 28 Mar 2026 05:38:45 +0400 Subject: [PATCH] =?UTF-8?q?WZP=5FINTEGRATION.md:=20featherChat=20=E2=86=94?= =?UTF-8?q?=20WarzonePhone=20integration=20spec=20(1001=20lines)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers: shared identity model (same BIP39 seed), authentication flow (Ed25519 signed tokens), call signaling via WireMessage::CallSignal, DTLS-SRTP media encryption bootstrapped from Double Ratchet, group calls (SFU + Sender Keys), warzone scenarios (voice messages as attachments, mule delivery for missed calls). Phased roadmap: shared identity → signaling → encrypted calls → group calls. featherChat-side details confirmed against code. WZP-side details marked [SPECULATIVE] (WZP codebase was inaccessible). Co-Authored-By: Claude Opus 4.6 (1M context) --- warzone/docs/WZP_INTEGRATION.md | 1001 +++++++++++++++++++++++++++++++ 1 file changed, 1001 insertions(+) create mode 100644 warzone/docs/WZP_INTEGRATION.md diff --git a/warzone/docs/WZP_INTEGRATION.md b/warzone/docs/WZP_INTEGRATION.md new file mode 100644 index 0000000..ff6d8bd --- /dev/null +++ b/warzone/docs/WZP_INTEGRATION.md @@ -0,0 +1,1001 @@ +# WarzonePhone (WZP) Integration with featherChat + +**Version:** 0.1.0 (Draft) +**Date:** 2026-03-28 +**Status:** Proposal / Design Document + +Items marked **[SPECULATIVE]** are proposed designs not yet validated against +WarzonePhone source code. Items marked **[featherChat CONFIRMED]** reference +implemented code in the `warzone-protocol` / `warzone-server` crates. + +--- + +## 1. Executive Summary + +**featherChat** (Warzone Messenger) is a seed-based, end-to-end encrypted +messaging system built in Rust. It provides: + +- BIP39 seed-derived identity (Ed25519 + X25519 + secp256k1) +- Signal-protocol encryption (X3DH + Double Ratchet for 1:1, Sender Keys for + groups) +- Transport-agnostic wire protocol (bincode-serialized `WireMessage` enum) +- Self-hosted axum server with sled embedded DB, WebSocket real-time push +- WASM web client with identical crypto + +**WarzonePhone (WZP)** is envisioned as an encrypted voice/video calling +application that shares featherChat's identity and cryptographic infrastructure. +Rather than building a separate auth system, WZP piggybacks on featherChat's +seed-based identity, key exchange, and encrypted signaling channels to bootstrap +secure real-time media sessions. + +**Why integrate:** + +1. **Single identity** -- one BIP39 mnemonic (24 words) controls messaging, + calling, and Ethereum wallet. No separate accounts. +2. **Reuse crypto infrastructure** -- X3DH sessions already provide shared + secrets between peers. SRTP keys can be derived from these. +3. **Encrypted signaling** -- call setup (SDP, ICE) travels through + featherChat's E2E encrypted channels. No cleartext signaling metadata. +4. **Shared contact/group model** -- featherChat groups map directly to WZP + conference call rooms. +5. **Warzone resilience** -- voice messages as file attachments, call signaling + via mule protocol for disconnected networks. + +--- + +## 2. Shared Identity Model + +### Same BIP39 Seed for Both Applications + +featherChat derives all cryptographic keys from a single 32-byte seed via +HKDF-SHA256 with domain-separated info strings: + +``` +BIP39 Seed (32 bytes, 24 words) + | + +-- HKDF(info="warzone-ed25519") --> Ed25519 signing keypair + | | + | +-> SHA-256[:16] = Fingerprint + | + +-- HKDF(info="warzone-x25519") --> X25519 encryption keypair + | (X3DH + Double Ratchet) + | + +-- HKDF(info="warzone-secp256k1") --> secp256k1 keypair + | | + | +-> Keccak-256[-20:] = Eth Address + | + +-- HKDF(info="warzone-history") --> History encryption key +``` + +**[featherChat CONFIRMED]** -- see `warzone-protocol/src/identity.rs` (`Seed::derive_identity()`) +and `warzone-protocol/src/ethereum.rs` (`derive_eth_identity()`). + +WZP uses the **exact same seed**. No new key derivation paths are needed for +basic integration. The featherChat fingerprint IS the WZP user identity. The +Ethereum address IS the unified cross-application identity. + +### Identity Mapping + +| Identifier | Derivation | Used By | +|------------|-----------|---------| +| Fingerprint (`a3f8:c912:44be:7d01`) | `SHA-256(Ed25519_pubkey)[:16]` | featherChat messaging, WZP user ID | +| Ethereum address (`0xd8dA...`) | `Keccak-256(secp256k1_uncompressed[1:])[-20:]` | Wallet, ENS, cross-app lookup | +| Ed25519 public key | HKDF from seed | Signing, auth challenges, token issuance | +| X25519 public key | HKDF from seed | DH key exchange, session establishment | + +### Single Sign-On + +A user authenticates **once** in featherChat (seed unlock via passphrase + +Argon2id). That session produces a bearer token (see Section 3). WZP accepts +this token -- no separate WZP authentication flow needed. + +--- + +## 3. Authentication Flow + +### Current featherChat Auth (Challenge-Response) + +**[featherChat CONFIRMED]** -- see `warzone-server/src/routes/auth.rs`. + +``` +Step 1: Client -> Server POST /v1/auth/challenge { fingerprint } +Step 2: Server -> Client { challenge: random_hex(32), expires_at } +Step 3: Client -> Server POST /v1/auth/verify { + fingerprint, + challenge, + signature // Ed25519 sign(challenge_bytes) + } +Step 4: Server verifies Ed25519 signature against stored PreKeyBundle +Step 5: Server -> Client { token: random_hex(32), expires_at } + Token valid 7 days (TOKEN_TTL_SECS = 604800) +Step 6: Client includes `Authorization: Bearer ` on all requests +``` + +The `validate_token()` function in `auth.rs` checks token existence and expiry +against the `tokens` sled tree (key: token bytes, value: JSON `{fingerprint, expires_at}`). + +### Extended Auth Flow for WZP + +``` + featherChat Client WZP Client + | | + User unlocks seed | | + (passphrase) | | + | | + Challenge-response | | + auth with server | | + (Ed25519 signed) | | + | | + Receives bearer |------ token + fingerprint ---->| + token (7-day TTL) | (local IPC or shared | + | storage on device) | + | | + | WZP verifies token --->| POST /v1/auth/validate + | | { token } + | Server returns ------->| { fingerprint, valid } + | | + | WZP session active | +``` + +### Proposed Token Structure + +The current token is an opaque random hex string. For WZP integration, we +propose extending the token payload stored server-side: + +```json +{ + "fingerprint": "a3f8c91244be7d01...", + "expires_at": 1711843600, + "capabilities": ["message", "call", "video"], + "issued_for": "featherchat", + "device_id": "device_001" +} +``` + +**[SPECULATIVE]** The `capabilities` field would allow featherChat to issue +tokens with specific permissions. A messaging-only token could not initiate +calls. The server validates capabilities on WZP endpoints. + +### Proposed Server Endpoint + +``` +POST /v1/auth/validate +Body: { "token": "hex..." } +Response: { + "valid": true, + "fingerprint": "a3f8c912...", + "capabilities": ["message", "call", "video"], + "expires_at": 1711843600 +} +``` + +This is a read-only validation endpoint -- it does not consume or rotate the +token. WZP calls this once per session to verify the featherChat-issued token. + +--- + +## 4. Signaling Integration + +### Call Initiation via featherChat WireMessage + +**[featherChat CONFIRMED]** -- the `WireMessage` enum in +`warzone-protocol/src/message.rs` is the extensibility point. The ARCHITECTURE.md +documents the process for adding new variants (Section: "Extensibility Points > +Adding New WireMessage Variants"). + +#### Proposed New Variant: `CallSignal` + +```rust +// In warzone-protocol/src/message.rs +pub enum WireMessage { + // ... existing variants (KeyExchange, Message, Receipt, + // FileHeader, FileChunk, GroupSenderKey, SenderKeyDistribution) ... + + /// VoIP call signaling (WarzonePhone integration). + /// Encrypted via the existing Double Ratchet session. + CallSignal { + id: String, + sender_fingerprint: String, + signal_type: CallSignalType, + }, +} + +#[derive(Clone, Serialize, Deserialize)] +pub enum CallSignalType { + /// Initiate a call. Contains SDP offer. + Offer { + sdp: String, + call_id: String, + media_type: MediaType, // Audio, Video, AudioVideo + }, + /// Accept a call. Contains SDP answer. + Answer { + sdp: String, + call_id: String, + }, + /// ICE candidate for NAT traversal. + IceCandidate { + candidate: String, + sdp_mid: Option, + sdp_mline_index: Option, + call_id: String, + }, + /// Hang up / end call. + Hangup { + call_id: String, + reason: HangupReason, + }, + /// Call is ringing (acknowledgment). + Ringing { + call_id: String, + }, + /// Caller is busy. + Busy { + call_id: String, + }, +} + +#[derive(Clone, Serialize, Deserialize)] +pub enum MediaType { + Audio, + Video, + AudioVideo, +} + +#[derive(Clone, Serialize, Deserialize)] +pub enum HangupReason { + Normal, + Declined, + Timeout, + NetworkError, +} +``` + +#### Why Use featherChat's Channel for Signaling + +Traditional VoIP (SIP, WebRTC signaling) sends SDP offers/answers and ICE +candidates in cleartext through a signaling server. The server sees: +- Who is calling whom +- Codec preferences (reveals device type) +- ICE candidates (reveals local/public IP addresses) + +By routing call signaling through featherChat's existing Double Ratchet +sessions, **all signaling metadata is E2E encrypted**. The featherChat server +sees only an opaque `WireMessage` blob -- it cannot distinguish a call offer +from a text message. + +#### Signaling Flow (1:1 Call) + +``` +Alice (featherChat+WZP) featherChat Server Bob (featherChat+WZP) + | | | + | WireMessage::CallSignal | | + | { Offer { sdp, call_id } } | | + | (Double Ratchet encrypted) | | + |----------------------------->|--- WS push ------------>| + | | | + | | WireMessage::CallSignal + | | { Ringing { call_id } } + | | (Double Ratchet encrypted) + |<-----------------------------|<------------------------| + | | | + | | User accepts call | + | | WireMessage::CallSignal + | | { Answer { sdp } } | + |<-----------------------------|<------------------------| + | | | + | WireMessage::CallSignal | | + | { IceCandidate { ... } } | | + |----------------------------->|------------------------>| + | | | + |<-----------------------------|<------------------------| + | { IceCandidate { ... } } | | + | | | + | ============== ICE completes, P2P or TURN ========= | + | ================== SRTP media flows ================ | + | | | + | WireMessage::CallSignal | | + | { Hangup { Normal } } | | + |----------------------------->|------------------------>| +``` + +#### Server-Side Changes + +The featherChat server (`warzone-server`) requires **minimal changes** for +signaling: + +1. **`extract_message_id()` in `routes/ws.rs`** -- add a match arm: + ```rust + WireMessage::CallSignal { id, .. } => Some(id), + ``` + +2. **No new routes needed** -- `CallSignal` messages flow through the existing + WebSocket relay and HTTP message send/poll endpoints. The server treats them + as opaque encrypted blobs, identical to chat messages. + +3. **Dedup** -- the existing `DedupTracker` (bounded FIFO, 10,000 IDs) handles + call signaling dedup automatically. + +--- + +## 5. Media Security + +### SRTP Key Derivation from featherChat Shared Secret + +When Alice and Bob establish a featherChat session via X3DH, they share a +`shared_secret` (32 bytes) that seeds the Double Ratchet. This same shared +secret (or a derivative) can bootstrap SRTP encryption for voice/video. + +#### Option A: Direct Derivation from Ratchet State + +**[SPECULATIVE]** Derive SRTP keys directly from the current Double Ratchet +root key: + +``` +srtp_master_key = HKDF(ikm=root_key, salt="", info="wzp-srtp-master", len=16) +srtp_master_salt = HKDF(ikm=root_key, salt="", info="wzp-srtp-salt", len=14) +``` + +**Pros:** No additional key exchange. Instant call setup. +**Cons:** Ties SRTP key lifetime to ratchet state. Root key changes on DH +ratchet step -- both sides must agree on which root key to use. + +#### Option B: Ephemeral DTLS-SRTP Bootstrapped by featherChat (Recommended) + +**[SPECULATIVE]** Use featherChat's encrypted channel to exchange a +call-specific DTLS-SRTP fingerprint: + +``` +1. Alice generates ephemeral DTLS certificate for this call +2. Alice sends DTLS fingerprint in CallSignal::Offer.sdp + (this SDP is E2E encrypted via Double Ratchet) +3. Bob generates ephemeral DTLS certificate +4. Bob sends DTLS fingerprint in CallSignal::Answer.sdp + (also E2E encrypted) +5. DTLS-SRTP handshake occurs over the ICE-established path +6. Both sides verify the DTLS fingerprint matches what was + received in the encrypted signaling channel +7. SRTP keys derived from DTLS handshake +``` + +**Pros:** +- Standard WebRTC security model (SRTP via DTLS-SRTP) +- Call-specific ephemeral keys (perfect forward secrecy per call) +- DTLS fingerprint verification via featherChat E2E channel prevents MITM + (even a malicious TURN server cannot inject a fake DTLS certificate) + +**Cons:** Requires DTLS-SRTP implementation in WZP client. + +This is the approach Signal uses for its calling feature and is the recommended +path. + +### Key Rotation for Long Calls + +For calls exceeding 30 minutes, periodic SRTP key rotation is recommended: + +``` +Every 30 minutes: + 1. Initiator generates new DTLS certificate + 2. Sends new fingerprint via featherChat CallSignal (encrypted) + 3. Both sides renegotiate DTLS + 4. Old SRTP keys are zeroized +``` + +**[SPECULATIVE]** Alternatively, use SRTP's built-in key derivation rate (KDR) +to rotate keys based on packet count. + +--- + +## 6. Architecture Diagram + +### Overall System Architecture + +``` ++================================================================+ +| featherChat Clients | +| | +| +------------------+ +------------------+ +---------------+ | +| | CLI/TUI Client | | Web Client | | WZP Client | | +| | (warzone-client)| | (warzone-wasm) | | (VoIP app) | | +| +--------+---------+ +--------+---------+ +-------+-------+ | +| | | | | +| +--------+---------------------+---------------------+-------+ | +| | warzone-protocol | | +| | Identity . X3DH . Double Ratchet . Sender Keys . CallSig | | +| +------------------------------+------------------------------+ | ++================================================================+ + | + HTTP / WebSocket / bincode + | ++================================================================+ +| featherChat Server | +| | +| +----------+ +----------+ +----------+ +-----------------+ | +| | HTTP API | | WebSocket| | Auth | | Message Router | | +| | (axum) | | Relay | | (Ed25519 | | (chat + call | | +| | | | | | challenge)| | signaling) | | +| +----+-----+ +----+-----+ +----+-----+ +--------+--------+ | +| | | | | | +| +----+-------------+-------------+------------------+---------+ | +| | sled Database | | +| | keys . messages . groups . aliases . tokens | | +| +--------------------------------------------------------------+ | ++=================================+================================+ + | + (future: federation, S2S) + | ++================================================================+ +| WZP Media Server | +| [SPECULATIVE] | +| | +| +--------------+ +------------+ +---------------------------+ | +| | TURN Relay | | SFU | | Codec Transcoding | | +| | (coturn or | | (Selective | | (opus, VP8/VP9, | | +| | custom) | | Forwarding| | bandwidth adaptation) | | +| | | | Unit) | | | | +| +--------------+ +------------+ +---------------------------+ | ++================================================================+ +``` + +### Signaling Path vs Media Path + +``` + SIGNALING PATH (E2E encrypted) + ================================ +Alice featherChat Bob + | CallSignal Server | + | (encrypted) -----> relay ---------> | SDP offer + | <----- relay <--------- | SDP answer + | IceCand -----> relay ---------> | ICE candidates + | <----- relay <--------- | ICE candidates + | | + | | + MEDIA PATH (SRTP encrypted, may be P2P) + ========================================== + | | + | -------- DTLS-SRTP handshake -----> | (P2P via ICE) + | <------- DTLS-SRTP handshake ------ | + | | + | ========= SRTP audio/video =======> | (P2P or via TURN) + | <======== SRTP audio/video ======== | + | | + + + WHERE E2E ENCRYPTION APPLIES + ================================ + + +--- featherChat E2E (Double Ratchet) --+ + | | + | CallSignal messages: | + | SDP offer/answer | Server sees NOTHING + | ICE candidates | (opaque bincode blob) + | Hangup/Ringing/Busy | + | | + +----------------------------------------+ + + +--- SRTP (DTLS-SRTP keys) -------------+ + | | + | Media streams: | + | Audio (Opus) | TURN server sees + | Video (VP8/VP9) | encrypted SRTP packets + | | + +----------------------------------------+ + + +--- NOT encrypted to server ------------+ + | | + | TURN allocation requests | TURN server sees + | STUN binding requests | IP addresses + | ICE connectivity checks | + | | + +----------------------------------------+ +``` + +--- + +## 7. Server Infrastructure + +### Shared vs Separate Components + +| Component | featherChat Server | WZP Media Server | Shared? | +|-----------|-------------------|------------------|---------| +| User registry (keys tree) | Stores PreKeyBundles | Reads for auth validation | **Shared** (single sled DB) | +| Auth tokens (tokens tree) | Issues tokens | Validates tokens | **Shared** | +| Message relay (WebSocket) | Chat + call signaling | -- | **featherChat only** | +| Message queue (messages tree) | Offline messages + offline call signals | -- | **featherChat only** | +| Group membership (groups tree) | Group chat | Group call rooms | **Shared** | +| Alias resolution (aliases tree) | Display names | Caller ID | **Shared** | +| TURN relay | -- | NAT traversal for media | **WZP only** | +| SFU (group calls) | -- | Selective forwarding | **WZP only** | +| Codec engine | -- | Opus/VP8 transcoding | **WZP only** | + +### Deployment Model + +**[SPECULATIVE]** Two deployment options: + +#### Option 1: Co-located (Recommended for Small Deployments) + +``` +Single server instance: + - featherChat server (port 7700) + - TURN server (ports 3478, 49152-65535) + - SFU (port 10000) + - Shared sled DB +``` + +Both processes read the same sled database. featherChat server handles all +signaling; TURN/SFU handles media relay. This mirrors the existing featherChat +design philosophy of "single static binary, zero-config." + +#### Option 2: Separated (Production / High Load) + +``` +featherChat server cluster: + - Handles identity, auth, messaging, call signaling + - Horizontally scalable (stateless with shared DB) + +WZP media server cluster: + - TURN relay pool (coturn, geographically distributed) + - SFU instances (one per active group call) + - Validates auth tokens against featherChat server API +``` + +The WZP media servers call `POST /v1/auth/validate` against the featherChat +server to verify bearer tokens before allocating TURN credentials or SFU slots. + +--- + +## 8. Group Calls + +### featherChat Group = WZP Call Room + +**[featherChat CONFIRMED]** -- featherChat groups are managed via: +- `POST /v1/groups/create` -- create group +- `POST /v1/groups/:name/join` -- join group +- `GET /v1/groups/:name/members` -- list members with aliases +- `POST /v1/groups/:name/send` -- fan-out message to all members + +A featherChat group maps 1:1 to a WZP conference call room. The group name is +the room identifier. Group membership (stored in the `groups` sled tree) +determines who can join the call. + +### Group Call Signaling + +``` +Alice starts group call "ops-team": + | + | 1. Query group members: GET /v1/groups/ops-team/members + | -> [Alice, Bob, Carol, Dave] + | + | 2. For each member, send CallSignal::Offer via 1:1 encrypted channel: + | Alice -> Bob: WireMessage::CallSignal { Offer { call_id, sdp } } + | Alice -> Carol: WireMessage::CallSignal { Offer { call_id, sdp } } + | Alice -> Dave: WireMessage::CallSignal { Offer { call_id, sdp } } + | + | 3. Members who accept send CallSignal::Answer back to Alice + | + | 4. Alice notifies the SFU of all participants + | Each participant connects to the SFU +``` + +### SFU Architecture for Group Calls + +``` + +-------------+ + | SFU | + | (Selective | + | Forwarding | + | Unit) | + +------+------+ + / | \ + / | \ + SRTP / SRTP | SRTP \ + / | \ + +-----+ +----+---+ +------+ + |Alice| | Bob | |Carol | + +-----+ +--------+ +------+ + +Each participant sends ONE encrypted stream to the SFU. +The SFU forwards each stream to all other participants. +The SFU does NOT decrypt -- it forwards SRTP packets as-is. +``` + +### Media Encryption for Group Calls + +#### Option A: Sender Keys for Media (Recommended) + +**[featherChat CONFIRMED]** -- featherChat already implements Sender Keys for +group messaging (`warzone-protocol/src/sender_keys.rs`, `WireMessage::GroupSenderKey`, +`WireMessage::SenderKeyDistribution`). + +Extend the same concept to media encryption: + +``` +1. Each participant generates a media Sender Key +2. Distribute via 1:1 encrypted featherChat channels + (same as GroupSenderKey distribution for chat) +3. Each participant encrypts their SRTP stream with their Sender Key +4. SFU forwards encrypted streams -- cannot decrypt +5. Recipients decrypt with the sender's Sender Key +``` + +The HKDF domain separation for media Sender Keys: + +``` +Media message key: HKDF(ikm=chain_key, salt="", info="wzp-media-sk-{generation}-{counter}") +Media chain step: HKDF(ikm=chain_key, salt="", info="wzp-media-chain") +``` + +This is analogous to the existing chat Sender Key derivation +(`"wz-sk-msg-{generation}-{counter}"` and `"wz-sk-chain"`) but with distinct +info strings for domain separation. + +#### Option B: Per-Pair DTLS-SRTP via SFU + +Each participant establishes a separate DTLS-SRTP session with the SFU. The SFU +decrypts and re-encrypts for each recipient. This is simpler but requires +trusting the SFU with plaintext media. + +**Recommendation:** Option A (Sender Keys) for zero-trust media encryption, +consistent with featherChat's security model. The SFU sees only encrypted SRTP +frames. + +### Key Rotation on Membership Change + +When a member joins or leaves the group call: + +1. All remaining participants generate new media Sender Keys +2. New keys distributed via 1:1 featherChat channels +3. SFU is notified of membership change (add/remove forwarding path) +4. Old keys are zeroized + +This matches featherChat's existing group key rotation behavior. + +--- + +## 9. Offline / Warzone Scenarios + +### Voice Messages as File Attachments + +**[featherChat CONFIRMED]** -- featherChat supports file transfer up to 10 MB +via `WireMessage::FileHeader` + `WireMessage::FileChunk` (64 KB chunks). + +An Opus audio file at 32 kbps allows approximately **40 minutes** per 10 MB +message. This is the immediate "voice" capability before real-time VoIP is +built. + +``` +User records voice message: + 1. Encode as Opus (.opus), ~32 kbps + 2. Send via featherChat file transfer: + /file voice-message.opus + 3. Recipient receives, decrypts, plays back + +No WZP infrastructure needed. Pure featherChat feature. +``` + +### Call Signaling via Mule Protocol + +**[featherChat CONFIRMED]** -- the mule protocol (DESIGN.md, Phase 4) provides +physical message relay between disconnected networks. + +When both users are on disconnected networks with a mule relay: + +``` +Alice (Network A) Mule Bob (Network B) + | | | + | CallSignal::Offer | | + | (queued, cannot deliver) | | + | | | + |--- mule picks up ------>|--- mule travels ---->| + | |--- delivers offer -->| + | | | + | |<-- Bob's Answer -----| + | |<-- mule travels -----| + |<-- mule delivers -------| | + | | | + | BUT: real-time call is | | + | impossible via mule. | | + | This is a "missed call" | | + | notification. | | +``` + +The mule can deliver: +- **Missed call notifications** (CallSignal::Offer that expired) +- **Voice messages** (Opus file attachments) +- **Call history** (who tried to call, when) + +The mule **cannot** enable real-time calls. This is an acknowledged limitation. + +### LoRa: Text-Only, No Real-Time Voice + +**[featherChat CONFIRMED]** -- LoRa has ~250 byte payload and 0.3-50 kbps +bandwidth (DESIGN.md, Section 8). + +LoRa is fundamentally incompatible with real-time voice/video. It can carry: +- Short text messages +- Delivery receipts +- Presence beacons +- **Missed call notifications** (compact: call_id + sender_fp + timestamp) + +A compact LoRa call notification: + +``` +[1] version = 0x01 +[1] type = 0x04 (missed_call) +[8] sender fingerprint (truncated) +[8] recipient fingerprint (truncated) +[4] timestamp (unix 32-bit) +[16] call_id (UUID, 16 bytes) +[1] media_type (0x01=audio, 0x02=video) +--- +39 bytes total (well within 250-byte LoRa limit) +``` + +--- + +## 10. Implementation Roadmap + +### Phase A: Shared Identity (Weeks 1-3) + +**Goal:** WZP authenticates users via featherChat tokens. + +- [ ] Add `POST /v1/auth/validate` endpoint to featherChat server +- [ ] WZP client reads featherChat seed from `~/.warzone/identity.seed` + (or receives token via local IPC) +- [ ] WZP client calls `/v1/auth/validate` to verify token +- [ ] Shared fingerprint display in both UIs +- [ ] Ethereum address displayed as unified identity + +**Dependencies:** featherChat server (existing), featherChat auth (existing). +**Risk:** Low. Minor server-side addition. + +### Phase B: Signaling Through featherChat (Weeks 4-8) + +**Goal:** Call setup (SDP/ICE) flows through featherChat's encrypted channels. + +- [ ] Add `CallSignal` variant to `WireMessage` enum +- [ ] Update `extract_message_id()` in `routes/ws.rs` and `routes/messages.rs` +- [ ] WZP client sends/receives `CallSignal` via featherChat WebSocket +- [ ] Implement call state machine (ringing, answered, hangup) +- [ ] Basic 1:1 audio call (WebRTC or raw RTP) + +**Dependencies:** Phase A complete, WebRTC/RTP stack in WZP client. +**Risk:** Medium. Requires WZP to implement a WebRTC or RTP stack. + +### Phase C: E2E Encrypted Calls (Weeks 9-14) + +**Goal:** SRTP keys bootstrapped from featherChat's E2E channel. + +- [ ] DTLS-SRTP fingerprint exchange via encrypted `CallSignal::Offer/Answer` +- [ ] Verification that DTLS fingerprint matches what was received in E2E channel +- [ ] SRTP key rotation for long calls (>30 min) +- [ ] TURN server deployment and integration +- [ ] Call quality metrics (jitter, packet loss, bitrate) +- [ ] Video call support (VP8/VP9) + +**Dependencies:** Phase B complete, TURN server infrastructure. +**Risk:** Medium-High. SRTP/DTLS implementation complexity. + +### Phase D: Group Calls (Weeks 15-22) + +**Goal:** featherChat groups map to WZP conference call rooms. + +- [ ] SFU deployment (e.g., Janus, mediasoup, or custom Rust SFU) +- [ ] Media Sender Key derivation (separate HKDF domain from chat Sender Keys) +- [ ] Sender Key distribution via featherChat 1:1 channels +- [ ] SFU forwarding of encrypted SRTP (zero-knowledge SFU) +- [ ] Key rotation on member join/leave +- [ ] Scalability testing (target: 10-20 participants) +- [ ] Bandwidth adaptation (simulcast, SVC) + +**Dependencies:** Phase C complete, SFU infrastructure. +**Risk:** High. Group media encryption with Sender Keys is novel. SFU +integration requires careful engineering. + +--- + +## 11. API Contracts + +### New WireMessage Variant + +```rust +// warzone-protocol/src/message.rs +WireMessage::CallSignal { + id: String, // UUID for dedup + sender_fingerprint: String, // caller's fingerprint + signal_type: CallSignalType, // Offer/Answer/IceCandidate/Hangup/Ringing/Busy +} +``` + +This variant is encrypted via the existing Double Ratchet session between +caller and callee, then serialized with bincode and sent through the standard +featherChat transport (WebSocket binary frame or HTTP POST). + +### featherChat Server: Token Validation Endpoint + +``` +POST /v1/auth/validate +Content-Type: application/json + +Request: +{ + "token": "a1b2c3d4e5f6..." +} + +Response (valid): +{ + "valid": true, + "fingerprint": "a3f8c91244be7d01...", + "expires_at": 1711843600 +} + +Response (invalid/expired): +{ + "valid": false, + "error": "token expired" +} +``` + +### WZP Media Server: Proposed Endpoints + +**[SPECULATIVE]** These endpoints would be implemented in the WZP media server. + +``` +POST /call/allocate-turn +Authorization: Bearer +Request: +{ + "peer_fingerprint": "b7d1e845..." +} +Response: +{ + "turn_server": "turn:media.example.com:3478", + "username": "temp_user_abc", + "credential": "temp_pass_xyz", + "ttl": 86400 +} + +POST /call/join-sfu +Authorization: Bearer +Request: +{ + "call_id": "uuid...", + "group_name": "ops-team", + "sdp_offer": "v=0\r\n..." +} +Response: +{ + "sdp_answer": "v=0\r\n...", + "sfu_session_id": "sfu_abc123" +} + +DELETE /call/leave-sfu +Authorization: Bearer +Request: +{ + "sfu_session_id": "sfu_abc123" +} +Response: +{ + "ok": true +} +``` + +### WZP Client-to-Client Protocol Summary + +| Message Type | Transport | Encryption | Server Visibility | +|-------------|-----------|------------|-------------------| +| Call offer (SDP) | featherChat WS | Double Ratchet (E2E) | None (opaque blob) | +| Call answer (SDP) | featherChat WS | Double Ratchet (E2E) | None (opaque blob) | +| ICE candidates | featherChat WS | Double Ratchet (E2E) | None (opaque blob) | +| Hangup/Ringing/Busy | featherChat WS | Double Ratchet (E2E) | None (opaque blob) | +| Audio/Video stream | P2P or TURN | SRTP (DTLS-SRTP keys) | TURN sees encrypted SRTP | +| TURN allocation | TURN server | STUN/TURN protocol | TURN sees IP addresses | +| Group media | SFU | SRTP + Sender Keys | SFU sees encrypted SRTP | + +--- + +## 12. Security Considerations + +### Threat Analysis + +| Threat | Mitigation | Residual Risk | +|--------|-----------|---------------| +| Server reads call signaling | All signaling is Double Ratchet encrypted | None -- server sees opaque blobs | +| Server performs MITM on call setup | DTLS fingerprints verified via E2E channel | None -- MITM detectable | +| TURN server reads media | SRTP encryption; TURN sees only encrypted packets | TURN sees IP addresses and packet timing | +| SFU reads group call media | Sender Key encryption; SFU forwards encrypted SRTP | SFU sees who is in the call and packet sizes | +| Call metadata (who called whom) | Signaling is encrypted, but server routes messages | Server knows sender_fp and recipient_fp of `CallSignal` messages (same as chat) | +| Token theft | 7-day TTL, token stored locally | Compromised device = compromised token (same as seed compromise) | +| Replay of call signaling | `call_id` (UUID) prevents replay; dedup tracker | Bounded by dedup tracker capacity (10,000 IDs) | +| Long call key compromise | SRTP key rotation every 30 minutes | Window of exposure limited to rotation interval | + +### Comparison with Signal Calling Model + +| Aspect | featherChat + WZP (Proposed) | Signal Calling | +|--------|------------------------------|----------------| +| Signaling encryption | Double Ratchet (E2E) | Signal Protocol (E2E) | +| SDP/ICE transport | Through encrypted messaging channel | Through encrypted messaging channel | +| Media encryption | SRTP via DTLS-SRTP | SRTP via DTLS-SRTP | +| DTLS fingerprint verification | Via E2E signaling channel | Via E2E signaling channel | +| Group calls | SFU + Sender Keys | SFU + Sender Keys | +| TURN relay | Standard TURN | Signal's own relay infrastructure | +| Identity model | Seed-based (BIP39 mnemonic) | Phone number based | +| Server trust | Self-hosted, semi-trusted | Signal servers, semi-trusted | +| Metadata protection | Server sees message routing (same as chat) | Sealed sender (partial) | + +The proposed architecture closely mirrors Signal's calling model, which is +well-audited and proven at scale. The key difference is featherChat's seed-based +identity (no phone number required) and self-hosted server model. + +### Known Limitations + +1. **No sealed sender for call signaling** -- the featherChat server sees + sender and recipient fingerprints when routing `CallSignal` messages. This + is the same limitation as chat messages. Sealed sender (Phase 6 in + featherChat roadmap) would address this. + +2. **TURN server sees IP addresses** -- NAT traversal inherently reveals IP + addresses to the TURN server. Mitigation: self-host TURN alongside + featherChat server. For maximum privacy, use a VPN or Tor before connecting + to TURN. + +3. **SFU is a metadata oracle** -- the SFU knows which fingerprints are in a + group call and can observe packet timing/sizes. It cannot read media content + (Sender Key encryption), but it knows who is talking (voice activity + detection via packet size changes). Mitigation: constant-bitrate encoding + (CBR) masks voice activity. + +4. **No post-quantum protection for SRTP** -- DTLS-SRTP uses classical DH. + The featherChat roadmap includes hybrid key exchange (X25519 + ML-KEM); + the same approach could be applied to DTLS. Not a near-term concern. + +5. **Offline calls are impossible** -- unlike text messages, real-time calls + require both parties online. featherChat's mule protocol and offline queue + can deliver missed call notifications and voice messages, but not live calls. + +--- + +## Appendix A: featherChat Code References + +| Component | File | Key Types/Functions | +|-----------|------|---------------------| +| Seed & Identity | `warzone-protocol/src/identity.rs` | `Seed`, `IdentityKeyPair`, `PublicIdentity`, `Fingerprint` | +| Ethereum Identity | `warzone-protocol/src/ethereum.rs` | `EthIdentity`, `EthAddress`, `derive_eth_identity()` | +| Wire Protocol | `warzone-protocol/src/message.rs` | `WireMessage` enum, `MessageContent`, `ReceiptType` | +| X3DH | `warzone-protocol/src/x3dh.rs` | `initiate()`, `respond()` | +| Double Ratchet | `warzone-protocol/src/ratchet.rs` | `RatchetState`, `RatchetMessage`, `RatchetHeader` | +| Sender Keys | `warzone-protocol/src/sender_keys.rs` | (group encryption) | +| HKDF | `warzone-protocol/src/crypto.rs` | `hkdf_derive()` | +| Pre-Key Bundles | `warzone-protocol/src/prekey.rs` | `PreKeyBundle`, `SignedPreKey` | +| Server Auth | `warzone-server/src/routes/auth.rs` | `create_challenge()`, `verify_challenge()`, `validate_token()` | +| WebSocket Relay | `warzone-server/src/routes/ws.rs` | `handle_socket()`, `extract_message_id()` | +| Alias Resolution | `warzone-server/src/routes/aliases.rs` | alias CRUD, TTL management | +| Server State | `warzone-server/src/state.rs` | `AppState`, `Connections`, `DedupTracker` | +| Server DB | `warzone-server/src/db.rs` | `Database` (5 sled trees: keys, messages, groups, aliases, tokens) | + +## Appendix B: Open Questions + +1. **WZP client technology** -- Is WZP a separate native binary (Rust), a + mobile app (Swift/Kotlin), an Electron app, or integrated into the + featherChat TUI/web client? The integration design is client-agnostic, but + the implementation path differs significantly. + +2. **SFU selection** -- Build a custom Rust SFU, or use an existing one (Janus, + mediasoup, Pion)? A custom SFU could share the `warzone-protocol` crate + directly. An existing SFU would need an adapter layer for auth token + validation. + +3. **Codec requirements** -- Opus for audio is standard. Video codec (VP8, VP9, + AV1, H.264) depends on client platform and hardware acceleration. + +4. **SRTP library** -- Rust SRTP options: `webrtc-rs` (pure Rust WebRTC stack), + `libsrtp2-sys` (C bindings), or custom implementation using + `chacha20poly1305` for SRTP-like encryption. + +5. **Maximum group call size** -- Sender Key distribution is O(N) per member + change. For groups >20, consider mesh vs SFU topology tradeoffs. + +6. **Emergency calls** -- In warzone scenarios, should there be a "broadcast + call" feature (one-to-many, push-to-talk) that bypasses normal call setup? + This would use featherChat groups with a simplified signaling flow.