Files
featherChat/warzone/docs/INTEGRATION.md
Siavash Sameni 2dbbc61dfe Comprehensive documentation: architecture, usage, integration, progress, security
docs/ARCHITECTURE.md (531 lines):
  System design, ASCII diagrams, crypto stack, dual-curve identity,
  wire protocol (7 WireMessage variants), server/client architecture,
  data flow diagrams, storage model, extensibility points

docs/USAGE.md (550 lines):
  Complete user guide: installation, all CLI commands (10),
  all TUI commands (20+), all web commands, file transfer,
  identity management, aliases, groups, multi-device, backup,
  keyboard shortcuts

docs/INTEGRATION.md (542 lines):
  WarzonePhone concept, Ethereum/Web3, OIDC, DNS federation,
  transport abstraction, multi-server mode, custom clients,
  ntfy, how-to guides for extending message types/commands/storage

docs/PROGRESS.md (234 lines):
  Timeline, Phase 1 (16 features), Phase 2 (16 features),
  v0.0.20, 28 tests, bugs fixed, known limitations, Phase 3-7 roadmap

docs/SECURITY.md (438 lines):
  Threat model, 8 crypto primitives, key derivation paths,
  forward secrecy, Sender Keys trade-offs, seed security,
  server trust, WASM security, known weaknesses,
  comparison with Signal/Matrix/SimpleX

Total: 3,751 lines across 8 doc files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 05:25:46 +04:00

543 lines
14 KiB
Markdown

# 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<AppState>,
bearer: TypedHeader<Authorization<Bearer>>,
Json(req): Json<RegisterRequest>,
) -> AppResult<Json<Value>> {
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.<SHA256(fingerprint)[:16]>.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<Vec<u8>>;
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_<fingerprint_prefix>
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<Option<RatchetState>>;
}
trait MessageStore {
fn queue_message(&self, to: &str, message: &[u8]) -> Result<()>;
fn poll_messages(&self, fingerprint: &str) -> Result<Vec<Vec<u8>>>;
}
// 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.