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>
532 lines
26 KiB
Markdown
532 lines
26 KiB
Markdown
# Warzone Messenger (featherChat) — Architecture
|
|
|
|
**Version:** 0.0.20
|
|
**Status:** Phase 1 complete, Phase 2 complete
|
|
|
|
---
|
|
|
|
## High-Level Architecture
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────────────┐
|
|
│ Clients │
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────────┐ │
|
|
│ │ CLI Client │ │ TUI Client │ │ Web Client (WASM) │ │
|
|
│ │ (warzone) │ │ (ratatui) │ │ (wasm-bindgen) │ │
|
|
│ └──────┬───────┘ └──────┬───────┘ └───────────┬───────────┘ │
|
|
│ │ │ │ │
|
|
│ ┌──────┴─────────────────┴───────────────────────┴──────────┐ │
|
|
│ │ warzone-protocol │ │
|
|
│ │ Identity · X3DH · Double Ratchet · Sender Keys · History │ │
|
|
│ └───────────────────────────┬───────────────────────────────┘ │
|
|
└──────────────────────────────┼──────────────────────────────────┘
|
|
│ HTTP / WebSocket
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────────┐
|
|
│ warzone-server │
|
|
│ │
|
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────────┐ │
|
|
│ │ HTTP API │ │ WebSocket│ │ Auth │ │ Message Router │ │
|
|
│ │ (axum) │ │ Relay │ │Challenge │ │ + Dedup │ │
|
|
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────────┬────────┘ │
|
|
│ │ │ │ │ │
|
|
│ ┌────┴─────────────┴─────────────┴──────────────────┴────────┐ │
|
|
│ │ sled Database │ │
|
|
│ │ keys · messages · groups · aliases · tokens │ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
└──────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Crate Structure
|
|
|
|
The project is a Cargo workspace with five crates:
|
|
|
|
```
|
|
warzone/
|
|
├── Cargo.toml # Workspace root (v0.0.20)
|
|
├── crates/
|
|
│ ├── warzone-protocol/ # Core crypto & message types (library)
|
|
│ ├── warzone-server/ # Server binary (axum + sled)
|
|
│ ├── warzone-client/ # CLI/TUI client binary
|
|
│ ├── warzone-wasm/ # WASM bridge for web client
|
|
│ └── warzone-mule/ # Mule binary (future)
|
|
└── docs/
|
|
```
|
|
|
|
### warzone-protocol
|
|
|
|
The protocol crate is the heart of the system. It is a pure library with zero I/O dependencies, used by all other crates (including WASM).
|
|
|
|
| Module | Purpose |
|
|
|---------------|------------------------------------------------------|
|
|
| `identity` | Seed, IdentityKeyPair, PublicIdentity, Fingerprint |
|
|
| `mnemonic` | BIP39 mnemonic encode/decode |
|
|
| `crypto` | HKDF-SHA256, ChaCha20-Poly1305 AEAD |
|
|
| `prekey` | SignedPreKey, OneTimePreKey, PreKeyBundle |
|
|
| `x3dh` | X3DH key agreement (initiate + respond) |
|
|
| `ratchet` | Double Ratchet state machine |
|
|
| `message` | WireMessage enum, MessageContent, ReceiptType |
|
|
| `session` | Session management types |
|
|
| `store` | Storage trait definitions |
|
|
| `sender_keys` | Sender Key protocol for groups |
|
|
| `history` | Encrypted backup/restore (HKDF → ChaCha20) |
|
|
| `ethereum` | secp256k1 identity, Ethereum address derivation |
|
|
| `types` | Fingerprint, DeviceId, SessionId, MessageId |
|
|
| `errors` | ProtocolError enum |
|
|
|
|
### warzone-server
|
|
|
|
An axum HTTP + WebSocket server with sled embedded database.
|
|
|
|
| Module | Purpose |
|
|
|------------------|--------------------------------------------|
|
|
| `main` | CLI args, server startup (default :7700) |
|
|
| `state` | AppState, Connections map, DedupTracker |
|
|
| `db` | Database struct (5 sled trees) |
|
|
| `routes/keys` | Pre-key bundle registration & retrieval |
|
|
| `routes/messages`| Send, poll (fetch-and-delete), ack |
|
|
| `routes/groups` | Create, join, leave, kick, members, list |
|
|
| `routes/aliases` | Register, resolve, recover, renew, admin |
|
|
| `routes/auth` | Challenge-response authentication |
|
|
| `routes/ws` | WebSocket real-time message delivery |
|
|
| `routes/web` | Static file serving for web client |
|
|
| `routes/health` | Health check endpoint |
|
|
|
|
### warzone-client
|
|
|
|
CLI and TUI client with local sled database for sessions, contacts, and history.
|
|
|
|
| Module | Purpose |
|
|
|-------------|------------------------------------------------|
|
|
| `main` | CLI parser (clap): init, recover, info, etc. |
|
|
| `keystore` | Seed encryption at rest (Argon2id + ChaCha20) |
|
|
| `storage` | LocalDb: sessions, pre_keys, contacts, history |
|
|
| `net` | HTTP + WebSocket client |
|
|
| `tui/app` | TUI application (ratatui + crossterm) |
|
|
| `cli/*` | CLI subcommand handlers |
|
|
|
|
### warzone-wasm
|
|
|
|
WASM bridge exposing protocol functions to JavaScript via `wasm-bindgen`.
|
|
|
|
| Export | Purpose |
|
|
|-------------------------|--------------------------------------------|
|
|
| `WasmIdentity` | Seed generation, fingerprint, bundle |
|
|
| `WasmSession` | Encrypt/decrypt with Double Ratchet |
|
|
| `create_receipt` | Build receipt WireMessages |
|
|
| `decrypt_wire_message` | Full message decryption pipeline |
|
|
| `self_test` | End-to-end crypto verification in WASM |
|
|
| `debug_bundle_info` | Bundle introspection for debugging |
|
|
|
|
### warzone-mule (future)
|
|
|
|
Placeholder for the mule binary — physical message relay for disconnected networks.
|
|
|
|
---
|
|
|
|
## Cryptographic Stack
|
|
|
|
### 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 |
|
|
| SHA-256 | `sha2` | Fingerprint computation |
|
|
| Argon2id | `argon2` | Passphrase-based key derivation |
|
|
| secp256k1 ECDSA | `k256` | Ethereum-compatible signing |
|
|
| Keccak-256 | `tiny-keccak` | Ethereum address derivation |
|
|
|
|
### Protocol Stack
|
|
|
|
```
|
|
Application (plaintext)
|
|
│
|
|
▼
|
|
Double Ratchet (per-message keys, forward secrecy)
|
|
│
|
|
▼
|
|
X3DH (session establishment, 3-4 DH operations)
|
|
│
|
|
▼
|
|
ChaCha20-Poly1305 (authenticated encryption)
|
|
│
|
|
▼
|
|
Ed25519 (message signing + pre-key signing)
|
|
│
|
|
▼
|
|
WireMessage (bincode serialization)
|
|
│
|
|
▼
|
|
Transport (HTTP POST / WebSocket binary frame)
|
|
```
|
|
|
|
---
|
|
|
|
## Dual-Curve Identity Model
|
|
|
|
Warzone derives two separate cryptographic identities from a single BIP39 seed:
|
|
|
|
```
|
|
BIP39 Seed (32 bytes, 24 words)
|
|
│
|
|
├─── HKDF(info="warzone-ed25519") ──→ Ed25519 signing keypair
|
|
│ │
|
|
│ └─→ SHA-256[:16] = Fingerprint
|
|
│
|
|
├─── HKDF(info="warzone-x25519") ──→ X25519 encryption keypair
|
|
│ (used in X3DH + Double Ratchet)
|
|
│
|
|
└─── HKDF(info="warzone-secp256k1") ──→ secp256k1 keypair
|
|
│
|
|
└─→ Keccak-256[-20:] = Ethereum Address
|
|
```
|
|
|
|
**Messaging identity:** Ed25519 (signing) + X25519 (encryption). The fingerprint is `SHA-256(Ed25519_pubkey)[:16]`, displayed as `xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx`.
|
|
|
|
**Ethereum identity:** secp256k1 keypair derived from the same seed with domain-separated HKDF. The Ethereum address is derived using standard `Keccak-256(uncompressed_pubkey[1:])[-20:]`. EIP-55 checksummed display is supported.
|
|
|
|
This allows a single mnemonic to control both a Warzone messaging identity and an Ethereum wallet address.
|
|
|
|
---
|
|
|
|
## Wire Protocol
|
|
|
|
All messages between clients use the `WireMessage` enum, serialized with bincode:
|
|
|
|
```rust
|
|
enum WireMessage {
|
|
// Session establishment (X3DH + first ratchet message)
|
|
KeyExchange {
|
|
id, sender_fingerprint, sender_identity_encryption_key,
|
|
ephemeral_public, used_one_time_pre_key_id, ratchet_message,
|
|
},
|
|
|
|
// Subsequent DM messages (Double Ratchet encrypted)
|
|
Message { id, sender_fingerprint, ratchet_message },
|
|
|
|
// Delivery / read receipts (plaintext metadata)
|
|
Receipt { sender_fingerprint, message_id, receipt_type },
|
|
|
|
// File transfer: header announces the file
|
|
FileHeader { id, sender_fingerprint, filename, file_size, total_chunks, sha256 },
|
|
|
|
// File transfer: individual chunk (encrypted data)
|
|
FileChunk { id, sender_fingerprint, filename, chunk_index, total_chunks, data },
|
|
|
|
// Group message encrypted with Sender Key (O(1) encryption)
|
|
GroupSenderKey { id, sender_fingerprint, group_name, generation, counter, ciphertext },
|
|
|
|
// Sender Key distribution (sent via 1:1 encrypted channel)
|
|
SenderKeyDistribution { sender_fingerprint, group_name, chain_key, generation },
|
|
}
|
|
```
|
|
|
|
### Transport Encoding
|
|
|
|
- **CLI ↔ Server (HTTP):** JSON envelope with bincode message as byte array
|
|
- **CLI ↔ Server (WebSocket binary):** 64 hex chars (recipient fingerprint) + raw bincode bytes
|
|
- **Web ↔ Server (WebSocket JSON):** `{"to": "fingerprint", "message": [byte_array]}`
|
|
|
|
---
|
|
|
|
## Server Architecture
|
|
|
|
### Framework
|
|
|
|
- **axum 0.7** with tokio async runtime
|
|
- **sled 0.34** embedded database (zero-config, no external DB)
|
|
- **tower-http** for CORS and tracing middleware
|
|
|
|
### Route Structure
|
|
|
|
All API endpoints live under `/v1`:
|
|
|
|
```
|
|
POST /v1/keys/register Register pre-key bundle
|
|
GET /v1/keys/:fingerprint Fetch pre-key bundle
|
|
POST /v1/keys/replenish Upload additional OTPKs
|
|
GET /v1/keys/:fp/otpk-count Check remaining OTPKs
|
|
GET /v1/keys/:fp/devices List registered devices
|
|
GET /v1/keys/list List all registered fingerprints
|
|
|
|
POST /v1/messages/send Send encrypted message
|
|
GET /v1/messages/poll/:fp Fetch-and-delete queued messages
|
|
DELETE /v1/messages/:id/ack Explicit message acknowledgment
|
|
|
|
POST /v1/groups/create Create a group
|
|
GET /v1/groups List all groups
|
|
GET /v1/groups/:name Get group info
|
|
POST /v1/groups/:name/join Join a group
|
|
POST /v1/groups/:name/send Fan-out group message
|
|
POST /v1/groups/:name/leave Leave a group
|
|
POST /v1/groups/:name/kick Kick a member (creator only)
|
|
GET /v1/groups/:name/members List members with aliases
|
|
|
|
POST /v1/alias/register Register an alias
|
|
POST /v1/alias/recover Recover alias with recovery key
|
|
POST /v1/alias/renew Heartbeat / renew TTL
|
|
GET /v1/alias/resolve/:name Resolve alias → fingerprint
|
|
GET /v1/alias/whois/:fp Reverse lookup fingerprint → alias
|
|
GET /v1/alias/list List all aliases
|
|
POST /v1/alias/unregister Remove your own alias
|
|
POST /v1/alias/admin-remove Admin: remove any alias
|
|
|
|
POST /v1/auth/challenge Request a challenge
|
|
POST /v1/auth/verify Verify signature, get bearer token
|
|
|
|
GET /v1/ws/:fingerprint WebSocket upgrade for real-time push
|
|
|
|
GET / Web client static files
|
|
GET /health Health check
|
|
```
|
|
|
|
### Message Routing
|
|
|
|
```
|
|
Incoming message
|
|
│
|
|
├─→ Dedup check (bounded FIFO, 10,000 IDs)
|
|
│ │
|
|
│ ├─→ Duplicate? → silently drop
|
|
│ └─→ New? → continue
|
|
│
|
|
├─→ Try WebSocket push to recipient
|
|
│ │
|
|
│ ├─→ Connected? → instant delivery
|
|
│ └─→ Offline? → queue in sled DB
|
|
│
|
|
└─→ Renew sender's alias TTL
|
|
```
|
|
|
|
### WebSocket Protocol
|
|
|
|
1. Client connects to `/v1/ws/:fingerprint`
|
|
2. Server flushes any queued messages from DB
|
|
3. Server registers a push channel for this connection
|
|
4. Incoming WS messages are routed to recipient's WS or queued
|
|
5. Multiple devices per fingerprint are supported (all receive a copy)
|
|
6. On disconnect, stale senders are cleaned up
|
|
|
|
### Dedup Tracker
|
|
|
|
The `DedupTracker` prevents message duplication across HTTP and WebSocket delivery paths:
|
|
|
|
- Bounded to 10,000 message IDs
|
|
- FIFO eviction when capacity is exceeded
|
|
- Uses `HashSet` for O(1) lookup + `VecDeque` for ordering
|
|
- Thread-safe via `std::sync::Mutex`
|
|
|
|
---
|
|
|
|
## Web Client Architecture (WASM)
|
|
|
|
The web client runs the **exact same crypto** as the CLI through the `warzone-wasm` crate:
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ Browser │
|
|
│ │
|
|
│ ┌──────────────────┐ ┌──────────────────────────┐ │
|
|
│ │ JavaScript UI │ │ warzone-wasm (WASM) │ │
|
|
│ │ (chat.html) │──│ WasmIdentity │ │
|
|
│ │ │ │ WasmSession │ │
|
|
│ │ WebSocket ◄─────┤ │ decrypt_wire_message() │ │
|
|
│ │ localStorage │ │ create_receipt() │ │
|
|
│ │ │ │ self_test() │ │
|
|
│ └──────────────────┘ └──────────────────────────┘ │
|
|
│ │
|
|
│ Storage: │
|
|
│ localStorage: seed_hex, spk_secret_hex │
|
|
│ localStorage: session:<fp> (base64 ratchet state) │
|
|
└─────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
Key design decisions:
|
|
- **Web Crypto** provides cryptographic randomness (`OsRng` in WASM maps to `crypto.getRandomValues`)
|
|
- **No OTPKs** in the web client — cannot reliably store secrets. X3DH works without DH4 (OTPKs are an anti-replay optimization, not a security requirement)
|
|
- **SPK secret** stored in localStorage as hex, restored on page load
|
|
- **Session state** serialized as base64 bincode, stored per-peer in localStorage
|
|
- **bincode** wire format ensures CLI ↔ Web interoperability
|
|
|
|
---
|
|
|
|
## Data Flow Diagrams
|
|
|
|
### 1:1 Direct Message (First Message)
|
|
|
|
```
|
|
Alice Server Bob
|
|
│ │ │
|
|
│ GET /v1/keys/:bob_fp │ │
|
|
│─────────────────────────────→│ │
|
|
│ ← PreKeyBundle (bincode) │ │
|
|
│ │ │
|
|
│ X3DH initiate(bundle) │ │
|
|
│ → shared_secret │ │
|
|
│ Double Ratchet init_alice() │ │
|
|
│ ratchet.encrypt("hello") │ │
|
|
│ │ │
|
|
│ WireMessage::KeyExchange │ │
|
|
│ POST /v1/messages/send │ │
|
|
│─────────────────────────────→│ push via WS (or queue) │
|
|
│ │─────────────────────────────→│
|
|
│ │ │
|
|
│ │ X3DH respond(spk_secret) │
|
|
│ │ → shared_secret │
|
|
│ │ init_bob() │
|
|
│ │ ratchet.decrypt() │
|
|
│ │ → "hello" │
|
|
```
|
|
|
|
### 1:1 Direct Message (Subsequent)
|
|
|
|
```
|
|
Alice Server Bob
|
|
│ │ │
|
|
│ ratchet.encrypt("hi again") │ │
|
|
│ WireMessage::Message │ │
|
|
│ ─── WS binary ─────────────→│ ─── WS push ───────────────→│
|
|
│ │ │
|
|
│ │ ratchet.decrypt()│
|
|
│ │ → "hi again" │
|
|
│ │ │
|
|
│ │ WireMessage::Receipt │
|
|
│ │←──────────────────────────── │
|
|
│←─────────────────────────────│ │
|
|
│ ✓✓ delivered │ │
|
|
```
|
|
|
|
### Group Message (Sender Keys)
|
|
|
|
```
|
|
Alice Server Bob, Carol
|
|
│ │ │
|
|
│ SenderKey::generate("ops") │ │
|
|
│ Distribute via 1:1 channels: │ │
|
|
│ encrypt(SK_dist, Bob) │ │
|
|
│ encrypt(SK_dist, Carol) │ │
|
|
│ ─────────────────────────────→│──── push to Bob,Carol ──→│
|
|
│ │ │
|
|
│ sender_key.encrypt("attack") │ │
|
|
│ WireMessage::GroupSenderKey │ │
|
|
│ POST /groups/ops/send │ │
|
|
│ ─────────────────────────────→│──── fan-out ────────────→│
|
|
│ │ │
|
|
│ │ bob_copy.decrypt() │
|
|
│ │ carol_copy.decrypt() │
|
|
│ │ → "attack" │
|
|
```
|
|
|
|
### File Transfer
|
|
|
|
```
|
|
Sender Server Recipient
|
|
│ │ │
|
|
│ Read file, compute SHA-256 │ │
|
|
│ Split into 64KB chunks │ │
|
|
│ │ │
|
|
│ WireMessage::FileHeader │ │
|
|
│ ─────────────────────────────→│──── push ────────────────→│
|
|
│ │ │
|
|
│ WireMessage::FileChunk[0] │ │
|
|
│ ─────────────────────────────→│──── push ────────────────→│
|
|
│ WireMessage::FileChunk[1] │ │
|
|
│ ─────────────────────────────→│──── push ────────────────→│
|
|
│ ... │ │
|
|
│ WireMessage::FileChunk[N-1] │ │
|
|
│ ─────────────────────────────→│──── push ────────────────→│
|
|
│ │ │
|
|
│ │ Reassemble chunks │
|
|
│ │ Verify SHA-256 │
|
|
│ │ Save to downloads/ │
|
|
```
|
|
|
|
File constraints: max 10 MB, 64 KB chunks.
|
|
|
|
---
|
|
|
|
## Storage Model
|
|
|
|
### Server sled Trees
|
|
|
|
| Tree | Key Format | Value | Purpose |
|
|
|------------|-------------------------------|--------------------------|------------------------------|
|
|
| `keys` | `<fingerprint>` | bincode PreKeyBundle | Identity + pre-key storage |
|
|
| `keys` | `device:<fp>:<device_id>` | bincode PreKeyBundle | Per-device bundles |
|
|
| `keys` | `otpk:<fp>:<id>` | hex pubkey | One-time pre-keys |
|
|
| `messages` | `queue:<fp>:<uuid>` | raw bincode WireMessage | Offline message queue |
|
|
| `groups` | `<group_name>` | JSON GroupInfo | Group membership |
|
|
| `aliases` | `a:<alias>` | fingerprint string | Forward lookup |
|
|
| `aliases` | `fp:<fingerprint>` | alias string | Reverse lookup |
|
|
| `aliases` | `rec:<alias>` | JSON AliasRecord | Full record (TTL, recovery) |
|
|
| `tokens` | `<token_hex>` | JSON {fp, expires_at} | Auth bearer tokens |
|
|
|
|
### Client sled Trees
|
|
|
|
| Tree | Key Format | Value | Purpose |
|
|
|-------------|-------------------------------|--------------------------|----------------------------|
|
|
| `sessions` | `<peer_fp_hex>` | bincode RatchetState | Double Ratchet sessions |
|
|
| `pre_keys` | `spk:<id>` | 32-byte StaticSecret | Signed pre-key secrets |
|
|
| `pre_keys` | `otpk:<id>` | 32-byte StaticSecret | One-time pre-key secrets |
|
|
| `contacts` | `<fingerprint>` | JSON contact record | Contact list |
|
|
| `history` | `hist:<fp>:<ts>:<uuid>` | JSON message record | Message history |
|
|
|
|
### Client Filesystem
|
|
|
|
```
|
|
~/.warzone/
|
|
├── identity.seed # WZS1 magic + salt(16) + nonce(12) + ciphertext(48)
|
|
│ # or plain 32 bytes (legacy/testing)
|
|
└── db/ # sled database directory
|
|
├── conf
|
|
├── db
|
|
└── ...
|
|
```
|
|
|
|
The `WARZONE_HOME` environment variable overrides `~/.warzone`.
|
|
|
|
---
|
|
|
|
## Extensibility Points
|
|
|
|
### Adding New WireMessage Variants
|
|
|
|
1. Add a new variant to `WireMessage` in `warzone-protocol/src/message.rs`
|
|
2. Update `extract_message_id()` in both `routes/messages.rs` and `routes/ws.rs`
|
|
3. Handle in the TUI poll loop (`tui/app.rs`)
|
|
4. Handle in `decrypt_wire_message()` in `warzone-wasm/src/lib.rs`
|
|
5. bincode serialization is automatic (enum tag + fields)
|
|
|
|
### Adding New Transport Traits (future)
|
|
|
|
The design document specifies a `Transport` trait:
|
|
|
|
```rust
|
|
trait Transport {
|
|
async fn send(&self, endpoint: &str, blob: &[u8]) -> Result<()>;
|
|
async fn recv(&self) -> Result<Vec<u8>>;
|
|
}
|
|
```
|
|
|
|
Current transports: HTTPS (reqwest) and WebSocket (axum/tungstenite). Planned: Bluetooth, LoRa, Wi-Fi Direct, USB/file.
|
|
|
|
### Adding New Storage Backends
|
|
|
|
Client storage currently uses sled directly. The pattern for abstraction:
|
|
- `LocalDb` in `storage.rs` already provides a clean API
|
|
- Replace sled calls with trait methods for alternative backends (SQLite, IndexedDB, etc.)
|
|
- Server's `Database` in `db.rs` follows the same pattern
|
|
|
|
### Adding New Server Routes
|
|
|
|
1. Create a module in `routes/`
|
|
2. Implement `pub fn routes() -> Router<AppState>`
|
|
3. Merge into the router in `routes/mod.rs`
|
|
4. Access shared state via `State(state): State<AppState>`
|