diff --git a/warzone/CLAUDE.md b/warzone/CLAUDE.md new file mode 100644 index 0000000..5b982e8 --- /dev/null +++ b/warzone/CLAUDE.md @@ -0,0 +1,78 @@ +# featherChat — Design Principles & Conventions + +## Architecture Principles + +1. **Single seed, multiple identities** — Ed25519 (messaging), X25519 (encryption), secp256k1 (ETH address) all derived from one BIP39 seed via HKDF with domain-separated info strings. + +2. **E2E by default** — All user messages are Double Ratchet encrypted. The server NEVER sees plaintext. Friend lists are client-side encrypted. Only bot messages are plaintext (v1). + +3. **Server is semi-trusted** — Server sees metadata (who talks to whom, timing, groups) but cannot read message content. Design all features with this trust boundary in mind. + +4. **Federation is transparent** — Users don't need to know which server their peer is on. Key lookup, alias resolution, and message delivery automatically proxy through federation. + +5. **Telegram Bot API compatibility** — Bot API follows Telegram conventions (getUpdates, sendMessage, token-in-URL). Bot aliases must end with Bot/bot/_bot. + +6. **Auth on writes, open reads** — All POST/write endpoints require bearer tokens. GET/read endpoints are public (needed for key exchange before auth is possible). + +## Coding Conventions + +### Rust +- Workspace crates: protocol (no I/O), server (axum), client (ratatui), wasm (wasm-bindgen), mule (future) +- Error handling: `AppResult` in server, `anyhow::Result` in client, `ProtocolError` in protocol +- State: `AppState` with `Arc>` for shared state, `Arc` for sled +- Auth: `AuthFingerprint` extractor as first handler param for protected routes +- Fingerprints: always normalize with `normfp()` (strip non-hex, lowercase) +- New routes: create `routes/.rs`, add `pub fn routes() -> Router`, merge in `routes/mod.rs` + +### TUI +- 7 modules in `tui/`: types, draw, commands, input, file_transfer, network, mod +- All ChatLine must include `timestamp: Local::now()` +- Add new commands to both the handler chain AND `/help` text +- Self-messaging prevention: check `normfp(&peer) != normfp(&self.our_fp)` + +### Web (WASM) +- JS embedded in `routes/web.rs` as Rust raw string — careful with escaping +- Service worker cache version must be bumped on WASM changes (`wz-vN`) +- `WasmSession::initiate()` stores X3DH result — `encrypt_key_exchange` must NOT re-initiate + +### Federation +- Persistent WS between servers, NOT HTTP polling +- Presence re-pushed every 10s + on connect +- Key lookup: proxy to peer for non-local fingerprints (never cache remote bundles) +- Alias resolution: fall back to peer if not found locally +- Registration: check peer to enforce global uniqueness + +### Bot API +- Token stored as `bot:` in tokens tree +- Reverse lookup: `bot_fp:` → token +- Alias auto-registered on bot creation with `_bot` suffix +- Reserved aliases: `*Bot`, `*bot`, `*_bot` blocked for non-bots + +## Task Naming + +`FC-P{phase}-T{task}[-S{subtask}]` + +See `docs/TASK_PLAN.md` for the full breakdown. + +## Testing + +- Protocol: unit tests in each module's `#[cfg(test)]` +- TUI: unit tests for types, input, draw (using ratatui TestBackend) +- WASM: can't test natively (js-sys dependency) — test equivalent logic in protocol crate +- Server: no integration tests yet (planned) + +## Key Files + +| What | Where | +|------|-------| +| Wire format | `warzone-protocol/src/message.rs` | +| Crypto primitives | `warzone-protocol/src/crypto.rs` | +| Server state | `warzone-server/src/state.rs` | +| All routes | `warzone-server/src/routes/mod.rs` | +| Federation | `warzone-server/src/federation.rs` | +| TUI commands | `warzone-client/src/tui/commands.rs` | +| Web client | `warzone-server/src/routes/web.rs` | +| WASM bridge | `warzone-wasm/src/lib.rs` | +| Task plan | `docs/TASK_PLAN.md` | +| Bot API docs | `docs/BOT_API.md` | +| LLM help ref | `docs/LLM_HELP.md` | diff --git a/warzone/docs/ARCHITECTURE.md b/warzone/docs/ARCHITECTURE.md index 8a67596..a95a4f1 100644 --- a/warzone/docs/ARCHITECTURE.md +++ b/warzone/docs/ARCHITECTURE.md @@ -12,11 +12,11 @@ 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] - SRVB -->|Call Signaling| WZP ``` --- @@ -78,6 +78,7 @@ warzone/ | `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 @@ -86,7 +87,7 @@ warzone/ |----------------------|---------------------------------------------------| | `main` | CLI args, startup, federation init | | `state` | AppState, Connections, CallState, DedupTracker | -| `db` | 7 sled trees: keys, messages, groups, aliases, tokens, calls, missed_calls | +| `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 | @@ -100,6 +101,9 @@ warzone/ | `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) @@ -238,6 +242,13 @@ Public (no 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): @@ -366,6 +377,16 @@ sequenceDiagram | 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) @@ -438,6 +459,51 @@ flowchart LR --- +## Bot API (Telegram-Compatible) + +```mermaid +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 +``` + +- Bots register with a fingerprint and get a token +- Bot aliases must end with `Bot`, `bot`, or `_bot` (enforced) +- Non-bot users cannot register reserved aliases +- `getUpdates` returns Telegram-compatible Update objects +- `sendMessage` delivers plaintext (no E2E in v1) +- Messages from users arrive as encrypted blobs (base64) or plaintext bot messages + +### 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 @@ -491,7 +557,7 @@ graph TB ## Storage Model -### Server sled Trees (7) +### Server sled Trees (9) | Tree | Key Format | Value | |----------------|---------------------------|--------------------------| @@ -502,6 +568,8 @@ graph TB | `tokens` | `` | JSON {fp, expires_at} | | `calls` | `` | JSON CallState | | `missed_calls` | `missed::` | JSON {caller, timestamp} | +| `friends` | `` | Encrypted blob (ChaCha20) | +| `eth_addresses` | `0x...` or `rev:` | ETH↔fingerprint mapping | ### Client sled Trees (5) @@ -519,11 +587,11 @@ graph TB | Crate | Tests | Coverage | |-------|------:|---------| -| warzone-protocol | 28 | X3DH, Double Ratchet, Sender Keys, AEAD, HKDF, identity, ethereum, prekeys, mnemonic | +| warzone-protocol | 34 | X3DH, Double Ratchet, Sender Keys, AEAD, HKDF, identity, ethereum, prekeys, mnemonic, friend list, x3dh web client | | warzone-client (types) | 10 | App init, scroll, connected, timestamps, normfp | | warzone-client (input) | 25 | Text editing, cursor movement, scroll keys, quit | | warzone-client (draw) | 9 | Rendering, timestamps, connection dot, scroll, unread badge | -| **Total** | **72** | All passing | +| **Total** | **122** | All passing | WZP side: 15 cross-project identity tests + 17 integration tests (separate repo). diff --git a/warzone/docs/SECURITY.md b/warzone/docs/SECURITY.md index 1b44c9b..b357421 100644 --- a/warzone/docs/SECURITY.md +++ b/warzone/docs/SECURITY.md @@ -1,7 +1,7 @@ # Warzone Messenger (featherChat) — Security Model & Threat Analysis -**Version:** 0.0.20 -**Last Updated:** 2026-03-28 +**Version:** 0.0.21 +**Last Updated:** 2026-03-29 --- @@ -20,6 +20,10 @@ | Session state | Encrypted backup (HKDF + ChaCha20-Poly1305) | | Pre-key authenticity | Ed25519 signature on signed pre-keys | | Key exchange integrity | X3DH with 3-4 DH operations | +| Friend list | E2E encrypted blob (ChaCha20 + HKDF-derived key) | +| API write operations | Bearer token middleware on all POST routes | +| Device sessions | Kick/revoke-all, max 5 WS per fingerprint | +| Bot aliases | Reserved suffixes (Bot/bot/_bot) enforced | ### What Is NOT Protected (Current) @@ -32,6 +36,7 @@ | Message sizes | Server sees encrypted message sizes | | Online/offline status | Server knows when clients connect via WebSocket| | IP addresses | Server sees client IP addresses | +| Bot messages | Plaintext (not E2E) in v1 — bots don't hold ratchet sessions | ### Trust Boundaries @@ -63,6 +68,34 @@ └─────────────────────────────────────────────────────┘ ``` +### Authentication & Authorization + +- Challenge-response: Ed25519 signature over random challenge +- Bearer tokens: 7-day TTL, required on all write endpoints +- Auth middleware: `AuthFingerprint` extractor returns 401 on invalid/missing token +- Bot tokens: separate namespace (`bot:`), validated per-request +- Federation: shared secret compared on WS auth frame + +Protected endpoints (require bearer token): +- messages/send, groups/*, aliases/*, calls/*, devices/*, friends, presence/batch + +Public endpoints (no auth): +- keys/:fp, messages/poll, groups GET, alias/resolve, resolve/:address, bot/* + +### Rate Limiting & Abuse Prevention + +- Global: 200 concurrent requests (tower ConcurrencyLimitLayer) +- Per-fingerprint: max 5 WebSocket connections +- Stale connections auto-cleaned on new registrations +- Federation: auto-reconnect with 3s backoff (no amplification) + +### Session Recovery + +On ratchet decryption failure: +1. Corrupted session deleted from local DB +2. Warning shown: "[session reset]" +3. Next KeyExchange re-establishes the session automatically + --- ## Cryptographic Primitives