# Warzone Messenger (featherChat) — Integration & Extensibility Guide **Version:** 0.0.20 Items marked with **(future)** are designed but not yet implemented. --- ## WarzonePhone Integration (future) WarzonePhone is envisioned as a separate project for encrypted voice/video calls, sharing infrastructure with the messenger. ### Shared Components - **Identity:** Same BIP39 seed and fingerprint. One identity for messaging + calls. - **Server infrastructure:** Same server hosts both message relay and SRTP/VoIP signaling. - **Pre-key bundles:** Reuse X3DH bundles for call setup (SRTP key exchange). - **Contact list:** Shared aliases and contact metadata. ### Voice Messages Before VoIP is built, voice messages can be sent as file attachments: ``` /file voice-message.opus ``` The `/file` command already supports arbitrary file transfer up to 10 MB. An Opus audio file at 32 kbps allows ~40 minutes per message. ### Integration Pattern ``` warzone-protocol (shared) │ ┌─────┴──────┐ │ │ warzone-client warzone-phone (messaging) (VoIP, future) ``` Both binaries link against `warzone-protocol` for identity, key exchange, and encryption. --- ## Ethereum / Web3 Integration ### Current Implementation (v0.0.20) The `ethereum` module in `warzone-protocol` provides: - **secp256k1 keypair** derived from the BIP39 seed via `HKDF(seed, info="warzone-secp256k1")` - **Ethereum address** computation: `Keccak-256(uncompressed_pubkey[1:])[-20:]` - **EIP-55 checksummed addresses** - **ECDSA signing and verification** (secp256k1) - CLI command: `warzone eth` - TUI command: `/eth` ### MetaMask / Wallet Connect (future) Planned integration flow: ``` 1. User clicks "Connect Wallet" in web client 2. Web client requests eth_sign(challenge) from MetaMask 3. Server verifies secp256k1 signature 4. Server maps Ethereum address → Warzone fingerprint 5. Session established Challenge: MetaMask signs with secp256k1, but Warzone messaging uses Ed25519/X25519. The wallet connect only proves ownership of the Ethereum address — a separate X3DH session is still needed for E2E encryption. ``` ### ENS Resolution (future) Planned: resolve ENS names to Warzone fingerprints. ``` @vitalik.eth → resolve ENS → 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 → server lookup → Warzone fingerprint → /peer @vitalik.eth ``` Implementation would use `alloy` or `ethers-rs` for ENS resolution. ### Hardware Wallet Support (future) Ledger and Trezor natively support secp256k1. Integration plan: - Seed lives on the hardware wallet, never exported - Ed25519 signing delegated to device (BIP44 path `m/44'/1234'/0'`) - X25519 derived from Ed25519 or separate derivation path - Session key delegation: sign once per 30 days, client uses delegated key for daily operations ### Session Delegation (future) For hardware wallets that cannot be used for every message: ``` Hardware wallet signs: "I delegate signing to ephemeral key X for 30 days" Client stores ephemeral key in memory All messages signed with ephemeral key Contacts verify delegation chain: HW_pubkey → delegation_cert → ephemeral_sig ``` --- ## OIDC Integration (future) For organizational deployments, an OIDC provider can gate registration and associate corporate identities. ### Concept ``` 1. User authenticates with corporate IdP (Okta, Azure AD, etc.) 2. IdP issues OIDC token containing email/groups 3. User presents OIDC token to Warzone server during registration 4. Server verifies token, associates fingerprint with corporate identity 5. Optional: server restricts messaging to verified users only Benefits: - Gated registration (only org members can register) - Corporate directory integration (resolve by email) - Audit trail (fingerprint ↔ corporate identity mapping) - Seed recovery via corporate identity (re-register) ``` ### Implementation Pattern ```rust // Future: auth middleware async fn register_with_oidc( State(state): State, bearer: TypedHeader>, Json(req): Json, ) -> AppResult> { let claims = verify_oidc_token(&bearer.token())?; // Associate claims.email with req.fingerprint // Only allow registration if claims are valid } ``` --- ## DNS Federation (future) ### Server Discovery Each Warzone server publishes a DNS TXT record: ``` _warzone._tcp.example.com TXT "v=wz1; endpoint=https://wz.example.com; pubkey=base64..." ``` Other servers discover peers by querying DNS: ``` 1. User sends message to user@example.com 2. Local server: DNS TXT lookup → _warzone._tcp.example.com 3. Parse endpoint URL and server pubkey 4. TLS connection, mutual authentication 5. Deliver encrypted message blob ``` ### Key Transparency Users publish their public keys in DNS to prevent server MITM: ``` _wz._id..example.com TXT "v=wz1; fp=a3f8...; pubkey=base64...; sig=base64..." ``` The `sig` field is a self-signature — even the DNS admin cannot forge it without the user's private key. ### Alias Resolution via DNS (future) ``` _wz._alias.alice.example.com TXT "fp=a3f8c912..." ``` --- ## Transport Abstraction The protocol is transport-agnostic. The `WireMessage` format is identical regardless of how it travels. ### Current Transports (v0.0.20) | Transport | Client→Server | Server→Client | Status | |-----------|---------------|---------------|--------| | HTTPS | POST JSON | GET poll | Implemented | | WebSocket | Binary/JSON | Binary push | Implemented | ### Planned Transports (future) | Transport | Range | Bandwidth | Use Case | |-------------|------------|------------|-----------------------------| | Bluetooth | 10-100m | ~2 Mbps | Mule sync, nearby devices | | LoRa | 2-15 km | 0.3-50 kbps| Emergency text, receipts | | Wi-Fi Direct| ~200m | ~250 Mbps | Local group mesh | | USB/File | Physical | Unlimited | Sneakernet, mule export | ### LoRa Compact Format (future) For LoRa's ~250 byte payload limit: ``` [1] version [1] type (text=0x01, receipt=0x02, beacon=0x03) [8] sender fingerprint (truncated) [8] recipient fingerprint (truncated) [4] timestamp (unix 32-bit) [12] nonce [~216] ciphertext (~200 chars of text) ``` ### USB / Sneakernet (future) ```bash warzone export --since 24h --to /mnt/usb/messages.wz # Carry USB drive to destination warzone import /mnt/usb/messages.wz ``` ### Implementing a New Transport Define a type that implements the transport interface (conceptual — trait not yet formalized): ```rust // Future trait trait Transport: Send + Sync { async fn send(&self, endpoint: &str, blob: &[u8]) -> Result<()>; async fn recv(&self) -> Result>; fn name(&self) -> &str; } ``` The message blob is always a bincode-serialized `WireMessage`. The transport only needs to deliver bytes. --- ## Multi-Server Mode (future) ### Federation Servers communicate using mutual TLS and server-to-server protocol: ``` Server A Server B │ │ │ DNS lookup: _warzone._tcp.B │ │ TLS connect + mutual auth │ │ ─── deliver encrypted blob ────────→│ │ ←── delivery receipt ───────────────│ ``` ### Server-to-Server Relay When direct connectivity is not available: ``` Server A → Server C (relay) → Server B Server C is configured as a relay for B. C queues messages for B until B reconnects. ``` ### Gossip Discovery (future) Servers share their known peer lists: ```json { "peers": [ {"domain": "wz.example.com", "pubkey": "base64...", "last_seen": 1711443600}, {"domain": "chat.org", "pubkey": "base64...", "last_seen": 1711440000} ] } ``` ### Mule Protocol (future) Physical message relay between disconnected networks: 1. Mule authenticates with source server 2. Mule picks up queued outbound messages (encrypted blobs) 3. Mule physically travels to destination 4. Mule delivers blobs to destination server 5. Mule carries back delivery receipts 6. Receipt enforcement: no receipts = no new pickup --- ## Custom Client Development ### Using warzone-protocol as a Library Add to your `Cargo.toml`: ```toml [dependencies] warzone-protocol = { path = "../warzone/crates/warzone-protocol" } ``` Core operations: ```rust use warzone_protocol::identity::Seed; use warzone_protocol::prekey::{generate_signed_pre_key, generate_one_time_pre_keys}; use warzone_protocol::x3dh; use warzone_protocol::ratchet::RatchetState; use warzone_protocol::message::WireMessage; // Generate identity let seed = Seed::generate(); let identity = seed.derive_identity(); let pub_id = identity.public_identity(); println!("Fingerprint: {}", pub_id.fingerprint); // Generate pre-key bundle let (spk_secret, spk) = generate_signed_pre_key(&identity, 1); let otpks = generate_one_time_pre_keys(1, 10); // Initiate session (Alice side) let x3dh_result = x3dh::initiate(&identity, &their_bundle)?; let mut ratchet = RatchetState::init_alice( x3dh_result.shared_secret, x25519_dalek::PublicKey::from(their_bundle.signed_pre_key.public_key), ); // Encrypt let encrypted = ratchet.encrypt(b"hello")?; // Build wire message let wire = WireMessage::Message { id: uuid::Uuid::new_v4().to_string(), sender_fingerprint: pub_id.fingerprint.to_string(), ratchet_message: encrypted, }; let bytes = bincode::serialize(&wire)?; ``` ### WASM for Browsers The `warzone-wasm` crate exposes the protocol to JavaScript: ```javascript import init, { WasmIdentity, WasmSession, decrypt_wire_message } from './warzone_wasm.js'; await init(); // Create identity const identity = new WasmIdentity(); console.log("Fingerprint:", identity.fingerprint()); console.log("Seed:", identity.seed_hex()); // Register bundle with server const bundleBytes = identity.bundle_bytes(); await fetch('/v1/keys/register', { method: 'POST', body: JSON.stringify({ fingerprint: identity.fingerprint_hex(), bundle: Array.from(bundleBytes), }), }); // Create session and encrypt const session = WasmSession.initiate(identity, theirBundleBytes); const encrypted = session.encrypt_key_exchange(identity, theirBundleBytes, "hello"); // Decrypt incoming const result = decrypt_wire_message( identity.seed_hex(), identity.spk_secret_hex(), messageBytes, existingSessionBase64, // null for first message ); const parsed = JSON.parse(result); // parsed.sender, parsed.text, parsed.session_data, parsed.message_id ``` ### Native Mobile (future) The `warzone-protocol` crate compiles to any Rust target: - **iOS:** via `cargo-lipo` or Swift package with C FFI - **Android:** via `cargo-ndk` with JNI bindings - Same crypto, same wire format, full interop --- ## Notification Integration (future) ### ntfy Concept [ntfy](https://ntfy.sh) provides push notifications without Google Play Services: ``` User registers topic: wz_ Server pushes on new message: POST https://ntfy.example.com/wz_a3f8c912 Body: "New message" (NO content — E2E encrypted) User receives push → opens Warzone to read ``` Self-hostable alongside the Warzone server. ntfy handles Android/iOS/desktop notifications. ### Metadata Consideration ntfy sees that *someone* messaged a topic (user). Mitigation: self-host ntfy on the same infrastructure as the Warzone server. --- ## How to Add New Message Types ### Step 1: Extend WireMessage In `warzone-protocol/src/message.rs`: ```rust pub enum WireMessage { // ... existing variants ... /// Your new message type MyNewType { id: String, sender_fingerprint: String, // your fields here }, } ``` bincode serialization is automatic — the variant gets a new enum tag. ### Step 2: Update Server Dedup In `warzone-server/src/routes/messages.rs` and `routes/ws.rs`, update `extract_message_id()`: ```rust WireMessage::MyNewType { id, .. } => Some(id), ``` ### Step 3: Handle in Clients **TUI client** (`warzone-client/src/tui/app.rs`): Handle the new variant in the message receive/poll loop. **Web client** (`warzone-wasm/src/lib.rs`): Add a match arm in `decrypt_wire_message()`: ```rust WireMessage::MyNewType { id, sender_fingerprint, .. } => { Ok(serde_json::json!({ "type": "my_new_type", "id": id, "sender": sender_fingerprint, }).to_string()) } ``` ### Step 4: Add Tests In the protocol crate, add serialization and round-trip tests. --- ## How to Add New Commands ### TUI Commands In `warzone-client/src/tui/app.rs`, inside `handle_send()`: ```rust if text.starts_with("/mycommand ") { let arg = text[11..].trim(); self.add_message(ChatLine { sender: "system".into(), text: format!("My command: {}", arg), is_system: true, is_self: false, message_id: None, }); return; } ``` Pattern: parse the command text, perform the action, add a system message for feedback. ### Web Commands In the web client JavaScript, add to the command dispatcher: ```javascript if (text.startsWith('/mycommand ')) { const arg = text.slice(11).trim(); addSystemMessage(`My command: ${arg}`); return; } ``` --- ## How to Add New Storage Backends ### Current Pattern Both server (`db.rs`) and client (`storage.rs`) use sled directly with method wrappers: ```rust pub struct LocalDb { sessions: sled::Tree, // ... } impl LocalDb { pub fn save_session(&self, peer: &Fingerprint, state: &RatchetState) -> Result<()> { let data = bincode::serialize(state)?; self.sessions.insert(key, data)?; Ok(()) } } ``` ### Abstracting to Traits (future) ```rust trait SessionStore { fn save_session(&self, peer: &Fingerprint, state: &RatchetState) -> Result<()>; fn load_session(&self, peer: &Fingerprint) -> Result>; } trait MessageStore { fn queue_message(&self, to: &str, message: &[u8]) -> Result<()>; fn poll_messages(&self, fingerprint: &str) -> Result>>; } // Implementations: struct SledStore { /* ... */ } struct SqliteStore { /* ... */ } struct IndexedDbStore { /* ... */ } // for WASM ``` The key insight: all storage is key-value with prefix scanning. Any ordered KV store (sled, RocksDB, SQLite, IndexedDB, LevelDB) can serve as a backend.