Files
featherChat/warzone/docs/ARCHITECTURE.md
Siavash Sameni c2be68ca20 docs: comprehensive update all docs to v0.0.46
11 files updated to reflect current state (v0.0.22 → v0.0.46):

ARCHITECTURE.md:
- Ring tones, group calls, read receipts, markdown rendering sections
- Bot API expanded (BotFather, numeric IDs, Telegram compat)
- Admin commands, known issues, 155 tests

TASK_PLAN.md:
- All P1-P4 marked DONE with version numbers
- Additional completed work section (bots, ETH, ring tones, group calls)
- New FC-P7 (Voice & Transport): cpal, Sender Keys, WebTransport
- FC-P6-T9/T10 added

PROGRESS.md:
- Full version history table v0.0.22 through v0.0.46
- Known issues section

README.md:
- Voice calls, ring tones, group calls, read receipts, markdown, 155 tests

SECURITY.md:
- Bot API security, voice call security, admin commands sections
- Updated protection tables

USAGE.md:
- Group calls, read receipts, markdown formatting, admin commands

CLIENT.md:
- Call commands, read receipts, markdown rendering

LLM_HELP.md + LLM_BOT_DEV.md:
- Call/group call/admin commands, ring tones, per-bot numeric IDs

TESTING_E2E.md:
- Tests 16-18: ring tones, group calls, admin commands

CLAUDE.md:
- Ring tone notes, group signal endpoint, MLS roadmap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 09:47:13 +04:00

28 KiB

Warzone Messenger (featherChat) — Architecture

Version: 0.0.46 Status: Phase 1 + Phase 2 + Phase 3 + WZP Integration + Federation + Bots + Admin

Features: E2E encrypted messaging (Double Ratchet), group messaging (Sender Keys), voice calls (DM E2E + group transport-encrypted), ring tones (Web Audio API), browser call notifications, group calls (/gcall, /gjoin, /gleave-call), read receipts (sent/delivered/read indicators), markdown rendering (TUI + Web), Telegram-compatible Bot API, admin commands, federation, device management, aliases, ETH address display, file transfer, friend lists, encrypted history backup


High-Level Architecture

graph TB
    CLI[CLI Client] --> PROTO[warzone-protocol]
    TUI[TUI Client] --> PROTO
    WEB[Web Client WASM] --> PROTO
    BOT[Bots TG API] -->|HTTP| SRVA
    PROTO -->|HTTP / WS| SRVA[Server Alpha]
    PROTO -->|HTTP / WS| SRVB[Server Bravo]
    SRVA <-->|Federation WS| SRVB
    SRVA -->|Call Signaling| WZP[WarzonePhone Relay]

Crate Structure

graph LR
    subgraph Workspace
        PROTO["warzone-protocol<br/>(library, no I/O)"]
        SERVER["warzone-server<br/>(axum binary)"]
        CLIENT["warzone-client<br/>(CLI/TUI binary)"]
        WASM["warzone-wasm<br/>(wasm-bindgen)"]
        MULE["warzone-mule<br/>(future)"]
    end

    SERVER --> PROTO
    CLIENT --> PROTO
    WASM --> PROTO
    MULE --> PROTO

    subgraph External["WarzonePhone (submodule)"]
        WZP_PROTO["wzp-proto"]
        WZP_CRYPTO["wzp-crypto"]
        WZP_RELAY["wzp-relay"]
        WZP_WEB["wzp-web"]
    end
warzone/
├── Cargo.toml                  # Workspace root (v0.0.46)
├── federation.example.json     # Federation config template
├── crates/
│   ├── warzone-protocol/       # Core crypto & message types
│   ├── warzone-server/         # Server binary (axum + sled)
│   ├── warzone-client/         # CLI/TUI client binary
│   ├── warzone-wasm/           # WASM bridge for web client
│   └── warzone-mule/           # Mule binary (future)
├── warzone-phone/              # WZP submodule (voice/video)
└── docs/

Protocol Modules

warzone-protocol

Module Purpose
identity Seed, IdentityKeyPair, PublicIdentity, Fingerprint
mnemonic BIP39 mnemonic encode/decode (24 words)
crypto HKDF-SHA256, ChaCha20-Poly1305 AEAD
prekey SignedPreKey, OneTimePreKey, PreKeyBundle
x3dh X3DH key agreement (initiate + respond)
ratchet Double Ratchet state machine (MAX_SKIP=1000)
message WireMessage enum (8 variants), CallSignalType
sender_keys Sender Key protocol for group encryption
history Encrypted backup/restore
ethereum secp256k1, Keccak-256, Ethereum address derivation
friends E2E encrypted friend list (encrypt/decrypt with HKDF key)
types Fingerprint, DeviceId, SessionId, MessageId

warzone-server

Module Purpose
main CLI args, startup, federation init
state AppState, Connections, CallState, DedupTracker
db 9 sled trees: keys, messages, groups, aliases, tokens, calls, missed_calls, friends, eth_addresses
federation Peer config, presence sync, message forwarding
auth_middleware Bearer token extractor (401 on protected routes)
routes/auth Challenge-response authentication
routes/ws WebSocket relay + call signaling awareness
routes/messages Send, poll (fetch-and-delete), ack
routes/groups Create, join, leave, kick, members, send
routes/calls Call CRUD, group call initiation
routes/devices Device listing, kick, revoke-all
routes/presence Online status (single + batch)
routes/federation Peer presence sync + message forwarding
routes/wzp WZP relay config + service token
routes/aliases Alias CRUD with TTL + recovery keys
routes/keys Pre-key bundle registration & retrieval
routes/friends Encrypted friend list blob storage (GET/POST)
routes/bot Telegram Bot API compatibility layer
routes/resolve Address resolution (ETH/alias/fingerprint → fp)

warzone-client (TUI)

Module Purpose
tui/mod Event loop, run_tui() entry point
tui/types App, ChatLine, scroll/connection state
tui/draw Rendering: timestamps, scroll, status dot, badge
tui/input Keyboard: text editing, scroll keys
tui/commands /help, /call, /devices, /kick, 20+ commands
tui/file_transfer Chunked file send (DM + group)
tui/network WS/HTTP polling, group decrypt, session recovery
storage LocalDb: sessions, pre_keys, contacts, history, sender_keys

warzone-wasm

Export Purpose
WasmIdentity Seed generation, fingerprint, bundle
WasmSession Encrypt/decrypt with Double Ratchet
decrypt_wire_message Full message pipeline (all 8 variants)
create_receipt Build receipt WireMessages
decrypt_group_message Sender Key group decryption
create_sender_key_from_distribution Build SenderKey from distribution
self_test End-to-end crypto verification in WASM

Cryptographic Stack

graph TB
    PLAIN["Plaintext Message"] --> DR["Double Ratchet<br/>(per-message keys)"]
    DR --> X3DH_INIT["X3DH Session Init<br/>(3-4 DH operations)"]
    X3DH_INIT --> AEAD["ChaCha20-Poly1305<br/>(AEAD encryption)"]
    AEAD --> SIGN["Ed25519 Signature<br/>(pre-key signing)"]
    SIGN --> WIRE["WireMessage<br/>(bincode serialization)"]
    WIRE --> TRANSPORT["HTTP POST / WS Binary"]

    style DR fill:#2d5016,color:#fff
    style AEAD fill:#1a3a5c,color:#fff
    style X3DH_INIT fill:#4a1a5c,color:#fff

Primitives

Primitive Crate Purpose
Ed25519 ed25519-dalek Signing, identity verification
X25519 x25519-dalek Diffie-Hellman key exchange
ChaCha20-Poly1305 chacha20poly1305 Authenticated encryption (AEAD)
HKDF-SHA256 hkdf + sha2 Key derivation with domain separation
SHA-256 sha2 Fingerprints, file integrity, room hashing
Argon2id argon2 Passphrase-based seed encryption at rest
secp256k1 ECDSA k256 Ethereum-compatible signing
Keccak-256 tiny-keccak Ethereum address derivation

Identity Derivation

graph LR
    SEED["BIP39 Seed<br/>(32 bytes, 24 words)"]
    SEED -->|"HKDF(info='warzone-ed25519')"| ED["Ed25519 Signing Key"]
    SEED -->|"HKDF(info='warzone-x25519')"| X25519["X25519 Encryption Key"]
    SEED -->|"HKDF(info='warzone-secp256k1')"| SECP["secp256k1 Key"]
    SEED -->|"HKDF(info='warzone-history')"| HIST["History Encryption Key"]

    ED -->|"SHA-256[:16]"| FP["Fingerprint<br/>xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx"]
    SECP -->|"Keccak-256[-20:]"| ETH["Ethereum Address<br/>0x..."]

A single mnemonic controls: messaging identity (Ed25519 + X25519), Ethereum wallet (secp256k1), and backup encryption. WarzonePhone uses the same seed with identical HKDF parameters for shared identity (verified by 15 cross-project tests).


Wire Protocol

WireMessage Variants

graph TB
    WM["WireMessage (bincode)"]
    WM --> KE["KeyExchange<br/>X3DH + first ratchet msg"]
    WM --> MSG["Message<br/>Double Ratchet encrypted"]
    WM --> REC["Receipt<br/>Sent/Delivered/Read"]
    WM --> FH["FileHeader<br/>filename, size, SHA-256"]
    WM --> FC["FileChunk<br/>64KB encrypted chunks"]
    WM --> GSK["GroupSenderKey<br/>Sender Key encrypted"]
    WM --> SKD["SenderKeyDistribution<br/>Share key via 1:1 channel"]
    WM --> CS["CallSignal<br/>Offer/Answer/Hangup/..."]

CallSignalType

Offer | Answer | IceCandidate | Hangup | Reject | Ringing | Busy

Transport Encoding

Client Path Format
CLI/TUI WS binary 64 hex chars (recipient fp) + raw bincode
CLI/TUI HTTP POST JSON envelope with bincode as byte array
Web WS JSON {"to": "fingerprint", "message": [bytes]}
Server↔Server WS JSON JSON frames over persistent federation WS

Server Architecture

Route Map

Auth-Protected (bearer token required):
  POST /v1/messages/send           Send encrypted message
  POST /v1/groups/create|join|send|leave|kick
  POST /v1/alias/register|unregister|recover|renew|admin-remove
  POST /v1/keys/register|replenish
  POST /v1/calls/initiate|:id/end
  POST /v1/groups/:name/call       Group call initiation
  POST /v1/groups/:name/signal     Group call signal broadcast
  POST /v1/devices/:id/kick        Kick a device
  POST /v1/devices/revoke-all      Panic button
  POST /v1/presence/batch          Bulk online check

Public (no auth):
  GET  /v1/keys/:fp                Fetch pre-key bundle
  GET  /v1/messages/poll/:fp       Fetch queued messages
  GET  /v1/groups/:name|list|members
  GET  /v1/alias/resolve/:name|list|whois/:fp
  GET  /v1/calls/:id|active|missed
  GET  /v1/presence/:fp            Online status
  GET  /v1/devices                 List own devices (auth)
  GET  /v1/wzp/relay-config        WZP relay address + token
  GET  /v1/federation/status       Federation health
  GET  /v1/ws/:fp                  WebSocket upgrade
  GET  /v1/friends               Encrypted friend list (auth)
  POST /v1/friends               Save friend list (auth)
  GET  /v1/resolve/:address      ETH/alias/fp resolution
  POST /v1/bot/register          Register a bot
  GET  /v1/bot/:token/getMe      Bot identity
  POST /v1/bot/:token/getUpdates Long-poll for messages
  POST /v1/bot/:token/sendMessage Send message as bot
  POST /v1/auth/challenge|verify|validate

Federation (HMAC-authenticated, server-to-server):
  POST /v1/federation/presence     Presence sync
  POST /v1/federation/forward      Message forwarding

Message Routing

flowchart TD
    MSG["Incoming Message<br/>for fingerprint X"] --> DEDUP{"Dedup Check<br/>(10K FIFO)"}
    DEDUP -->|Duplicate| DROP["Drop"]
    DEDUP -->|New| LOCAL{"push_to_client(X)<br/>Local WS?"}
    LOCAL -->|Delivered| DONE["Done"]
    LOCAL -->|Not local| FED{"Federation<br/>enabled?"}
    FED -->|No| QUEUE["Queue in<br/>sled DB"]
    FED -->|Yes| REMOTE{"X in remote<br/>presence?"}
    REMOTE -->|No| QUEUE
    REMOTE -->|Yes| FORWARD["HTTP POST to peer<br/>/v1/federation/forward"]
    FORWARD -->|Success| DONE
    FORWARD -->|Peer down| QUEUE

    style DONE fill:#2d5016,color:#fff
    style DROP fill:#5c1a1a,color:#fff
    style QUEUE fill:#4a3a1a,color:#fff

WebSocket Lifecycle

sequenceDiagram
    participant C as Client
    participant S as Server

    C->>S: GET /v1/ws/:fingerprint
    S->>S: Check connection cap (max 5)
    S->>C: WS Upgrade

    Note over S: Flush queued messages
    S->>C: Binary(queued_msg_1)
    S->>C: Binary(queued_msg_2)

    Note over S: Flush missed calls
    S->>C: Text({"type":"missed_call",...})

    Note over S: Register push channel

    loop Real-time
        C->>S: Binary(64-hex-fp + bincode)
        S->>S: Dedup + Call signal awareness
        S->>S: deliver_or_queue(recipient)
    end

    C->>S: Close
    S->>S: Cleanup stale senders

Federation

graph LR
    subgraph Alpha[Server Alpha]
        CA[Client A + B]
    end
    subgraph Bravo[Server Bravo]
        CC[Client C + D]
    end
    Alpha <-->|Persistent WS\nPresence + Forward| Bravo

Configuration

Each server has a federation.json:

{
  "server_id": "alpha",
  "shared_secret": "long-random-string-shared-between-both",
  "peer": {
    "id": "bravo",
    "url": "http://10.0.0.2:7700"
  }
}

Start with: warzone-server --federation federation.json

Presence Sync

On startup each server opens a persistent WebSocket to its peer and authenticates with the shared secret. Presence updates and message forwards flow over this single connection:

WS /v1/federation/ws
Auth: {"type":"auth","secret":"HMAC(shared_secret)"}
Presence: {"type":"presence","fingerprints":["aabb...","ccdd..."]}
Forward:  {"type":"forward","to":"<fp>","message":"<base64>"}

The receiving server replaces its remote presence set on each presence frame. If the WebSocket drops, the server auto-reconnects every 3 seconds and re-sends its full presence list.

Message Forwarding

sequenceDiagram
    participant SA as Server Alpha
    participant SB as Server Bravo

    Note over SA,SB: Persistent WS connection
    SA->>SB: {"type":"auth","secret":"..."}
    SA->>SB: {"type":"presence","fingerprints":["A","B"]}
    SB->>SA: {"type":"presence","fingerprints":["C","D"]}

    Note over SA: Client A sends message to C
    SA->>SB: {"type":"forward","to":"C","message":"base64..."}
    Note over SB: Deliver to Client C via local WS

Degradation

Scenario Behavior
WS disconnected Auto-reconnect every 3s, messages queue locally
Peer restarts Presence repopulates on WS reconnect
HMAC mismatch Request rejected with 401

Federated Features

Feature How it works
Message forwarding deliver_or_queue() checks remote presence, forwards via WS
Key lookup get_bundle() proxies to peer if fingerprint is not local
Alias resolution resolve_alias() falls back to peer server
ETH resolution resolve endpoint checks peer via HTTP
Presence Bidirectional sync every 10s + on-connect

Call Infrastructure (WZP Integration)

sequenceDiagram
    participant Caller as Caller (TUI)
    participant FC as featherChat Server
    participant WZP as WZP Relay

    Caller->>FC: WireMessage::CallSignal(Offer)
    FC->>FC: Create CallState(Ringing)
    FC->>FC: push_to_client(callee)

    alt Callee online
        FC-->>Callee: CallSignal(Offer) via WS
        Callee->>FC: CallSignal(Answer)
        FC->>FC: Update CallState(Active)
        Note over Caller,WZP: Both connect to WZP Relay with bearer token
        Caller->>WZP: QUIC + AuthToken + Handshake
        Callee->>WZP: QUIC + AuthToken + Handshake
        Note over WZP: Encrypted media flows (ChaCha20-Poly1305)
    else Callee offline
        FC->>FC: Record missed call in sled
        Note over FC: Flushed on callee's next WS connect
    end

    Caller->>FC: CallSignal(Hangup)
    FC->>FC: Update CallState(Ended)

Server Endpoints

Endpoint Purpose
POST /v1/calls/initiate Create call (returns call_id)
GET /v1/calls/:id Get call state
POST /v1/calls/:id/end End a call
GET /v1/calls/active List active calls
POST /v1/calls/missed Get & clear missed calls
POST /v1/groups/:name/call Group call (fan-out to members)
POST /v1/groups/:name/signal Broadcast call signal to group members
GET /v1/presence/:fp Check if peer is online
GET /v1/wzp/relay-config Get relay address + service token

Ring Tones

  • Incoming call: Web Audio API oscillator playing a 440/480 Hz dual-tone pattern (classic North American ring cadence)
  • Outgoing ringback: 2 seconds on / 4 seconds off pattern until callee answers or rejects
  • Browser notifications: If the web client tab is in background, an incoming call triggers a system notification so the user does not miss it

Group Calls

  • /gcall <group> starts a group call room; /gjoin <group> joins an existing room; /gleave-call leaves
  • Group call signals are broadcast via POST /v1/groups/:name/signal (fan-out to all online members)
  • Room naming convention: DM calls use a sorted fingerprint pair as room ID; group calls use gc-<groupname>
  • Encryption: Group calls are transport-encrypted only (QUIC with TLS). They are NOT end-to-end encrypted. MLS (RFC 9420) key agreement for group call media is on the roadmap.

Group Call Room ID

room_id = hex(SHA-256("featherchat-group:" + group_name)[:16])

Deterministic, 32 hex chars. Prevents leaking group name to relay via QUIC SNI.


Device Management

flowchart LR
    USER["User with<br/>3 devices"] --> LIST["GET /v1/devices<br/>(lists all sessions)"]
    USER --> KICK["POST /v1/devices/:id/kick<br/>(force-close one)"]
    USER --> REVOKE["POST /v1/devices/revoke-all<br/>(nuke all except current)"]

    KICK --> CLOSE["WS channel closed<br/>+ token invalidated"]
    REVOKE --> NUKE["All WS closed<br/>+ all tokens cleared"]
  • Max 5 WS connections per fingerprint
  • Stale connections auto-cleaned on new registrations
  • /devices and /kick <id> available as TUI commands

Bot API (Telegram-Compatible)

sequenceDiagram
    participant Dev as Bot Developer
    participant S as featherChat Server
    participant U as User

    Dev->>S: POST /v1/bot/register {name, fp}
    S->>Dev: {token, alias: "@mybot_bot"}

    loop Long-poll
        Dev->>S: POST /bot/:token/getUpdates
        S->>Dev: [updates...]
    end

    U->>S: Message to @mybot_bot
    S->>S: Queue for bot fp
    Dev->>S: getUpdates → receives message
    Dev->>S: POST /bot/:token/sendMessage
    S->>U: Deliver reply via WS
  • BotFather creates bots and issues tokens; each bot gets an auto-registered alias
  • Bot aliases must end with Bot, bot, or _bot (enforced); non-bot users cannot register reserved aliases
  • Per-bot numeric ID mapping: Each user is assigned a unique numeric ID per bot, preventing cross-bot user correlation (privacy)
  • Telegram-compatible endpoints: getUpdates (long-poll), sendMessage, editMessage, sendDocument, inline keyboards
  • sendMessage delivers plaintext (no E2E in v1 — bot messages are not encrypted)
  • Messages from users arrive as encrypted blobs (base64) or plaintext bot messages
  • System bots: Configured via --bots-config <file> on server startup; auto-created on first run

Addressing

Three address formats, all interchangeable:

Format Example Usage
Fingerprint 522d:4d6e:a8ee:588a:... Internal routing, crypto
ETH address 0x742d35Cc6634C0532... User-facing display
Alias @alice, @weatherbot Human-friendly

Resolution: GET /v1/resolve/:address accepts any format, returns fingerprint. ETH↔fingerprint mapping stored on key registration.


Security Model

What's Protected

Layer Protection Mechanism
Message content E2E encrypted ChaCha20-Poly1305 via Double Ratchet
Forward secrecy Per-message keys DH ratchet step on direction change
Session establishment Authenticated X3DH with signed pre-keys
Identity Deterministic from seed HKDF with domain separation
Seed at rest Encrypted Argon2id passphrase KDF
API writes Auth-gated Bearer token middleware (401)
Inter-server Authenticated SHA-256(secret || body) token
WS connections Rate-limited 5 per fingerprint, 200 global
WZP relay Token-gated featherChat bearer token validation
DM calls (voice) E2E encrypted ChaCha20-Poly1305 over QUIC via WZP relay
Group calls (voice) Transport-encrypted only QUIC/TLS — NOT E2E (MLS on roadmap)

What's NOT Protected (Phase 1 scope)

Data Exposure
Sender/recipient metadata Server sees routing info
Message timing Server sees timestamps
Online/offline status Server tracks WS connections
Group membership Server stores plaintext member list
IP addresses Server logs (standard for HTTP)

Planned mitigations: sealed sender (Phase 6), onion routing, metadata encryption.

Trust Boundaries

graph TB
    subgraph TRUSTED["Trusted: Your Device"]
        SEED["Seed in memory"]
        LDB["Local sled DB"]
    end

    subgraph SEMI["Semi-Trusted: Server"]
        SRVR["Sees metadata<br/>Can't read messages"]
    end

    subgraph UNTRUSTED["Untrusted: Network"]
        NET["TLS protects transport"]
    end

    TRUSTED -->|"E2E encrypted + TLS"| SEMI
    SEMI -->|"TLS"| UNTRUSTED

Storage Model

Server sled Trees (9)

Tree Key Format Value
keys <fingerprint> bincode PreKeyBundle
messages queue:<fp>:<uuid> raw bincode WireMessage
groups <group_name> JSON GroupInfo
aliases a:<alias>, fp:<fp>, rec:<alias> Various
tokens <token_hex> JSON {fp, expires_at}
calls <call_id> JSON CallState
missed_calls missed:<fp>:<call_id> JSON {caller, timestamp}
friends <fingerprint> Encrypted blob (ChaCha20)
eth_addresses 0x... or rev:<fp> ETH↔fingerprint mapping

Client sled Trees (5)

Tree Key Format Value
sessions <peer_fp_hex> bincode RatchetState
pre_keys spk:<id>, otpk:<id> 32-byte StaticSecret
contacts <fingerprint> JSON contact record
history hist:<fp>:<ts>:<uuid> JSON message record
sender_keys sk:<fp>:<group> bincode SenderKey

Test Coverage

Crate Tests Coverage
warzone-protocol 39 X3DH, Double Ratchet, Sender Keys, AEAD, HKDF, identity, ethereum, prekeys, mnemonic, friend list, x3dh web client, receipts
warzone-client (types) 10 App init, scroll, connected, timestamps, normfp
warzone-client (input) 25 Text editing, cursor movement, scroll keys, quit
warzone-client (draw) 13 Rendering, timestamps, connection dot, scroll, unread badge, markdown
warzone-server (integration) 10 Route handlers, auth middleware, group ops, call state
warzone-server (bin) 10 CLI args, startup, federation init, bot config
Other (e2e, misc) 48 Client-side E2E flows, file transfer, admin commands
Total 155 All passing

WZP side: 15 cross-project identity tests + 17 integration tests (separate repo).


Data Flow Diagrams

1:1 Direct Message (First Contact)

sequenceDiagram
    participant A as Alice
    participant S as Server
    participant B as Bob

    A->>S: GET /v1/keys/:bob_fp
    S->>A: PreKeyBundle (bincode)

    Note over A: X3DH initiate(bundle)<br/>Double Ratchet init_alice()<br/>ratchet.encrypt("hello")

    A->>S: WireMessage::KeyExchange
    S->>B: Push via WS (or queue)

    Note over B: X3DH respond(spk_secret)<br/>init_bob()<br/>ratchet.decrypt() = "hello"

    B->>S: WireMessage::Receipt(Delivered)
    S->>A: Push receipt

Group Message (Sender Keys)

sequenceDiagram
    participant A as Alice
    participant S as Server
    participant B as Bob
    participant C as Carol

    Note over A: SenderKey::generate("ops")

    A->>S: SenderKeyDistribution (via 1:1 to Bob)
    S->>B: Push distribution
    A->>S: SenderKeyDistribution (via 1:1 to Carol)
    S->>C: Push distribution

    Note over A: sender_key.encrypt("attack")

    A->>S: POST /groups/ops/send (GroupSenderKey)
    S->>B: Fan-out
    S->>C: Fan-out

    Note over B,C: sender_key.decrypt() = "attack"

Federated Message

sequenceDiagram
    participant A as Client A (Alpha)
    participant SA as Server Alpha
    participant SB as Server Bravo
    participant C as Client C (Bravo)

    Note over SA,SB: Persistent WS between servers
    SA->>SB: presence ["A","B"]
    SB->>SA: presence ["C","D"]

    A->>SA: Message for C
    SA->>SA: Not local, C in remote presence
    SA->>SB: forward to C via federation WS
    SB->>C: Push via local WS

Admin Commands

Command Purpose
/admin-calls List all currently active calls on the server
/admin-unalias <alias> <pw> Force-remove an alias (requires admin password)
/admin-help Show available admin commands

Admin commands are available in the TUI client and are authenticated server-side.


Read Receipts

  • TUI: Tracks which messages are visible in the viewport and sends Receipt::Read back to the sender when a message scrolls into view
  • Web: Sender sees delivery indicators: single check mark (sent) then double check mark (delivered) then blue double check mark (read)
  • Deduplication: Each message is receipted only once; the client tracks which message IDs have already been acknowledged to avoid redundant receipt traffic

Markdown Rendering

  • TUI: Custom md_to_spans parser converts markdown to ratatui Span objects supporting bold, italic, inline code, headers, blockquotes, and lists
  • Web: renderMd() function in the embedded JS handles code blocks, inline code, bold, italic, headers, links, blockquotes, and ordered/unordered lists
  • Both renderers are deliberately simple (no AST) to avoid pulling in heavy markdown dependencies

Known Issues and Limitations

Issue Details
Group call signal delivery Depends on members being online; there is no offline queue for call signals
TUI voice calls Require the web client; no native audio (cpal) integration yet
Bot messages are plaintext v1 limitation; bots cannot participate in E2E encryption
/gmembers ETH resolution Async resolution may briefly show the raw fingerprint before the ETH address loads
Service worker cache staleness Cache version in web.rs must be bumped on every change or browsers will serve stale WASM/JS content

Extensibility

Adding New WireMessage Variants

  1. Add variant to WireMessage in warzone-protocol/src/message.rs
  2. Update extract_message_id() in routes/messages.rs and routes/ws.rs
  3. Handle in tui/network.rs (process_wire_message)
  4. Handle in warzone-wasm/src/lib.rs (decrypt_wire_message)
  5. bincode serialization is automatic

Adding New Server Routes

  1. Create module in routes/
  2. Implement pub fn routes() -> Router<AppState>
  3. Merge in routes/mod.rs
  4. Add _auth: AuthFingerprint for write endpoints

Adding Federation Peers (Future)

Current: 1 peer via JSON config. Future: N peers via config array or DNS discovery. The deliver_or_queue() method would iterate over peers checking remote presence.