Files
featherChat/warzone/docs/WZP_INTEGRATION.md
Siavash Sameni f7a517d8ea WZP_INTEGRATION.md: featherChat ↔ WarzonePhone integration spec (1001 lines)
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) <noreply@anthropic.com>
2026-03-28 05:38:45 +04:00

38 KiB

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 <token>` 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:

{
  "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

// 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<String>,
        sdp_mline_index: Option<u32>,
        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:

    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.

[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:

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

[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

// 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 <featherchat_token>
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 <featherchat_token>
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 <featherchat_token>
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.