# Warzone Client -- Operation Guide **Version:** 0.0.21 --- ## 1. Installation ### Build from Source Requires Rust 1.75+. ```bash cd warzone/ cargo build -p warzone-client --release ``` The binary is at `target/release/warzone`. You can copy it anywhere or add `target/release` to your `PATH`. ```bash # Optional: install to ~/.cargo/bin cargo install --path crates/warzone-client ``` ### Build the WASM Module (Web Client) Requires wasm-pack. ```bash cd crates/warzone-wasm wasm-pack build --target web # Output in pkg/ — copy to web client directory ``` --- ## 2. TUI Architecture The interactive client is built on **ratatui** (rendering) and **crossterm** (terminal I/O). The event loop polls at **100 ms** intervals, giving a responsive feel without busy-waiting. ### Module Layout The TUI lives in `crates/warzone-client/src/tui/` and is split into seven modules: | Module | Responsibility | |-----------------|---------------------------------------------------------| | `types` | Core data structures: `App`, `ChatLine`, `ReceiptStatus`, `PendingFileTransfer`, constants (`MAX_FILE_SIZE`, `CHUNK_SIZE`) | | `draw` | Rendering: header bar, message list with timestamps and receipt indicators, input box with unread badge, scroll windowing | | `commands` | All `/`-prefixed command handlers (peer, alias, group, file, history, friends, devices, etc.) and message send logic | | `input` | Key event dispatch: text editing, cursor movement, scroll, quit | | `file_transfer` | Chunked file send: reads file, SHA-256 hash, splits into 64 KB encrypted chunks | | `network` | WebSocket receive loop (with HTTP polling fallback), incoming message decryption, receipt handling, session auto-recovery | | `mod` | Public entry point `run_tui()`: sets up terminal, spawns network task, runs the 100 ms event loop | ### Event Loop ``` loop { terminal.draw(app) // ratatui render pass if event::poll(100ms) { // crossterm poll handle key event // Enter → send; everything else → input.rs } if app.should_quit { break } } ``` Messages arrive asynchronously on a background tokio task (`network::poll_loop`) and are pushed into a shared `Arc>>`. --- ## 3. CLI Subcommands ### `warzone init` Generate a new identity (seed, keypair, pre-keys). ```bash $ warzone init Set passphrase (empty for no encryption): **** Confirm passphrase: **** Your identity: Fingerprint: a3f8:c912:44be:7d01:9e5a:3b2c:7f80:12d4 Mnemonic: abandon ability able about above absent absorb abstract ... SAVE YOUR MNEMONIC — it is the ONLY way to recover your identity. ``` **What happens:** 1. Generates 32 random bytes (seed) from `OsRng`. 2. Derives Ed25519 signing key and X25519 encryption key from the seed. 3. Converts seed to a 24-word BIP39 mnemonic and displays it. 4. Prompts for a passphrase. Encrypts the seed with Argon2id + ChaCha20-Poly1305 and saves to `~/.warzone/identity.seed` (mode 0600 on Unix). An empty passphrase stores the seed in plaintext. 5. Generates 1 signed pre-key (id=1) and 10 one-time pre-keys (ids 0-9). 6. Stores pre-key secrets in the local sled database at `~/.warzone/db/`. 7. Saves the public pre-key bundle to `~/.warzone/bundle.bin`. --- ### `warzone recover ` Recover an identity from a 24-word BIP39 mnemonic. ```bash $ warzone recover abandon ability able about above absent absorb abstract \ absurd abuse access accident account accuse achieve acid \ acoustic acquire across act action actor actress actual Set passphrase (empty for no encryption): **** Confirm passphrase: **** Identity recovered. Fingerprint: a3f8:c912:44be:7d01:9e5a:3b2c:7f80:12d4 ``` Recovery restores the seed and keypair. Pre-keys and sessions are NOT restored; contacts will need to re-establish sessions. --- ### `warzone info` Display your fingerprint and public keys. ```bash $ warzone info Fingerprint: a3f8:c912:44be:7d01:9e5a:3b2c:7f80:12d4 Signing key: 3a7b... (64 hex chars) Encryption key: 9f2c... (64 hex chars) ``` Requires a saved identity (`~/.warzone/identity.seed`). --- ### `warzone tui` / `warzone chat [peer]` Launch the interactive TUI client. ```bash $ warzone chat --server http://wz.example.com:7700 $ warzone chat a3f8:c912:44be:7d01:... --server http://wz.example.com:7700 $ warzone chat @alice --server http://wz.example.com:7700 ``` An optional `peer` argument (fingerprint or `@alias`) pre-sets the active DM target. **Flags:** | Flag | Short | Default | Description | |------------|-------|-----------------------|--------------| | `--server` | `-s` | `http://localhost:7700` | Server URL | --- ### `warzone send ` Send an encrypted message. Recipient can be a fingerprint or `@alias`. ```bash $ warzone send a3f8:c912:44be:7d01:... "Hello!" --server http://wz.example.com:7700 $ warzone send @alice "Hello!" --server http://wz.example.com:7700 ``` **Behavior:** 1. Auto-registers your bundle with the server if needed. 2. Checks for an existing Double Ratchet session with the recipient. 3. If no session: fetches the recipient's pre-key bundle, verifies the signed pre-key signature, performs X3DH, initializes the ratchet as Alice, and sends a `WireMessage::KeyExchange` containing the X3DH parameters and the first encrypted message. 4. If a session exists: encrypts with the existing ratchet and sends a `WireMessage::Message`. 5. Updates the local session state. --- ### `warzone recv` Poll for and decrypt incoming messages. ```bash $ warzone recv --server http://wz.example.com:7700 ``` Fetches messages from `/v1/messages/poll/{fingerprint}`, deserializes each `WireMessage`, performs X3DH respond or ratchet decrypt as appropriate, and prints plaintext to stdout. --- ### `warzone backup [output]` Export an encrypted backup of local data (sessions, pre-keys). ```bash $ warzone backup my-backup.wzb Backup saved to my-backup.wzb (4096 bytes encrypted) ``` The backup is encrypted with `HKDF(seed, info="warzone-history")` + ChaCha20-Poly1305. **Backup file format:** ``` WZH1 (4 bytes) + nonce (12) + ciphertext Plaintext: JSON { "version": 1, "sessions": { "": "base64_bincode", ... }, "pre_keys": { "spk:1": "base64_bytes", "otpk:1": "base64_bytes", ... } } ``` --- ### `warzone restore ` Restore from an encrypted backup. Requires the same seed (passphrase prompt). ```bash $ warzone restore my-backup.wzb Restored 12 entries from my-backup.wzb ``` Merges data without overwriting existing entries. --- ## 4. TUI Features ### Message Timestamps Every message is rendered with a `[HH:MM]` prefix in dark gray, derived from `chrono::Local::now()` at receive/send time. ### Message Scrolling The message area supports scrolling with a "pinned to bottom" model: - `scroll_offset = 0` means the newest messages are visible. - Scrolling up increases the offset; scrolling down decreases it. - The visible window is computed as `items[total - offset - height .. total - offset]`. ### Connection Status Indicator The header bar displays a colored dot after the server URL: - Green dot: WebSocket connection active. - Red dot: disconnected (HTTP polling fallback or reconnecting). ### Unread Badge When `scroll_offset > 0`, the input box title changes from `" message "` to `" [N new] "` showing how many messages are below the current scroll position. This makes it obvious that new content has arrived while reading history. ### Terminal Bell A terminal bell (`\x07`) is emitted on every incoming DM (both `KeyExchange` and `Message` wire types). This triggers a system notification in most terminal emulators. ### Receipt Indicators Sent messages display delivery status after the message text: | Indicator | Meaning | |-----------------|----------------------------------------| | Single tick | Sent (no confirmation yet) | | Double tick | Delivered (decrypted by recipient) | | Double tick blue| Read (viewed by recipient) | ### Session Auto-Recovery When decryption fails on an incoming message, the TUI automatically: 1. Deletes the corrupted session from the local database. 2. Displays a system message: `[session reset] Decryption failed for . Session cleared -- next message will re-establish.` The next incoming `KeyExchange` from that peer will create a fresh session without manual intervention. --- ## 5. Full Command Reference All commands start with `/` and are entered in the TUI input box. ### Peer and Navigation | Command | Short | Description | |------------------------|---------|----------------------------------------------| | `/peer ` | `/p` | Set the active DM peer (fingerprint or @alias) | | `/dm` | | Switch to DM mode (clear group context) | | `/reply` | `/r` | Switch to the last person who DM'd you | | `/info` | | Display your fingerprint | | `/eth` | | Display your Ethereum address (derived from seed) | | `/seed` | | Display your 24-word recovery mnemonic | | `/quit` | `/q` | Exit the TUI | | `/help` | `/?` | Show the built-in help text | ### Alias Management | Command | Description | |-----------------------|--------------------------------------------| | `/alias ` | Register an alias for your fingerprint. Returns a recovery key -- save it. | | `/unalias` | Remove your alias from the server | | `/aliases` | List all registered aliases on the server | Alias rules: 1-32 alphanumeric characters (plus `_` and `-`), case-insensitive, normalized to lowercase. TTL is 365 days of inactivity with a 30-day grace period before reclamation. ### Contacts and History | Command | Short | Description | |------------------------|---------|------------------------------------------| | `/contacts` | `/c` | List all contacts with message counts | | `/history [peer]` | `/h` | Show message history (last 50 messages). Uses current peer if set. | ### Group Commands | Command | Description | |-------------------------|------------------------------------------| | `/g ` | Switch to group (auto-join if needed) | | `/gcreate ` | Create a new group (you become creator) | | `/gjoin ` | Join an existing group | | `/gleave` | Leave the current group | | `/gkick ` | Kick a member (creator only) | | `/gmembers` | List members of the current group | | `/glist` | List all groups on the server | Group messages use Sender Keys for O(1) encryption per message. Each member generates a `SenderKey` distributed via 1:1 encrypted channels. Keys rotate on member join/leave. ### File Transfer | Command | Description | |-------------------|----------------------------------------------| | `/file ` | Send a file to the current peer or group | Constraints: - Maximum file size: 10 MB - Chunk size: 64 KB - Files are sent as `FileHeader` + encrypted `FileChunk` wire messages - SHA-256 verification on receipt - Received files are saved to `~/.warzone/downloads/` ### Device Management | Command | Description | |-----------------------|------------------------------------------| | `/devices` | List your active device sessions | | `/kick ` | Kick a specific device session | --- ## 6. Keyboard Shortcuts ### Text Editing | Key | Action | |------------------|---------------------------------| | Left / Right | Move cursor one character | | Home / Ctrl+A | Move to beginning of line | | End / Ctrl+E | Move to end of line | | Backspace | Delete character before cursor | | Delete | Delete character at cursor | | Ctrl+U | Clear entire input line | | Ctrl+K | Kill from cursor to end of line | | Ctrl+W | Delete word before cursor | | Alt+Backspace | Delete word before cursor | | Alt+Left | Jump one word left | | Alt+Right | Jump one word right | ### Scrolling | Key | Action | |------------------|------------------------------------------| | PageUp | Scroll up 10 messages | | PageDown | Scroll down 10 messages | | Up | Scroll up 1 message (when input is empty)| | Down | Scroll down 1 message (when input is empty)| | End | Snap to bottom (when input is empty) | | Ctrl+End | Snap to bottom (always) | ### Quit | Key | Action | |------------------|---------| | Ctrl+C | Quit | | Esc | Quit | --- ## 7. Friend List The friend list is an E2E encrypted contact list stored on the server as an opaque blob. The server never sees the plaintext. ### Encryption - Key derivation: `HKDF(seed, info="warzone-friends")` produces a 32-byte key. - Encryption: ChaCha20-Poly1305 with AAD `"warzone-friends-aad"`. - Plaintext format: JSON-serialized `FriendList` containing address, alias, and `added_at` timestamp per friend. ### Commands | Command | Description | |------------------------|------------------------------------------------| | `/friend` | List all friends with online/offline presence | | `/friend
` | Add a friend (fingerprint or ETH address) | | `/unfriend
` | Remove a friend | When listing friends, the TUI queries the server's presence endpoint for each friend to show real-time online/offline status. ### How It Works 1. On `/friend
`: the client fetches the current encrypted blob from the server, decrypts it, adds the entry, re-encrypts, and uploads. 2. On `/unfriend
`: same fetch-decrypt-modify-encrypt-upload cycle. 3. On `/friend` (no argument): fetches and decrypts the blob, then checks `/v1/presence/` for each friend. The server stores the blob at `POST /v1/friends` and returns it at `GET /v1/friends`. It has no knowledge of the contents. --- ## 8. Local Storage ### Directory Layout ``` ~/.warzone/ identity.seed # Encrypted seed (Argon2id + ChaCha20-Poly1305) bundle.bin # bincode-serialized PreKeyBundle (public data) db/ # sled database directory sessions/ # Double Ratchet state per peer (keyed by hex fingerprint) pre_keys/ # Signed and one-time pre-key secrets contacts/ # Contact metadata and message counts history/ # Message history per peer sender_keys/ # Sender Key state for group encryption downloads/ # Received files from /file transfers ``` ### Seed Encryption The seed file uses a fixed format: ``` WZS1 (4 bytes magic) + salt (16) + nonce (12) + ciphertext (48) Encryption: Argon2id(passphrase, salt) -> 32-byte key ChaCha20-Poly1305(key, nonce, seed) -> ciphertext ``` An empty passphrase at `init` time stores the seed in plaintext (for testing only). The seed file is created with mode `0600` (owner read/write) on Unix. ### Mnemonic Backup The 24-word BIP39 mnemonic shown during `init` is the only way to recover your identity if you lose `~/.warzone/`. Write it down on paper. You can also view it later with `/seed` in the TUI. --- ## 9. Web Client The web client is served by the server at `/` and uses a **WASM bridge** (`warzone-wasm`) that exposes the exact same cryptographic primitives as the CLI: X25519, ChaCha20-Poly1305, X3DH, Double Ratchet. ### Features - **Same crypto as TUI:** the WASM module wraps `warzone-protocol` directly, so web-to-CLI interoperability is fully supported. - **URL deep links:** paths like `/message/@alias`, `/message/0xABC`, and `/group/#ops` auto-navigate to the corresponding conversation. - **Clickable addresses:** fingerprints and aliases in the chat are rendered as interactive links. - **Service worker cache:** all shell assets (`/`, WASM JS, WASM binary, manifest, icon) are cached by a versioned service worker (`wz-v2`). The cache name is bumped on updates to force refresh. - **PWA support:** includes a manifest and install prompt (`/install` command). - **BIP39 mnemonic:** seed is displayed as 24 words via the WASM bridge (not hex). ### Web-Only Commands | Command | Description | |-------------------|----------------------------------------------------| | `/selftest` | Run WASM crypto self-test (X3DH + ratchet cycle) | | `/bundleinfo` | Debug: show bundle details (keys, sizes) | | `/debug` | Toggle debug mode (verbose output) | | `/reset` | Clear identity and all local data | | `/install` | Show PWA installation instructions | | `/sessions` | List active ratchet sessions | | `/admin-unalias` | Admin: remove any alias (requires admin password) | ### Web Client Storage Data is stored in `localStorage`: | Key | Value | Purpose | |----------------------|--------------------------------|----------------------------| | `wz_seed` | hex seed (64 chars) | Identity seed | | `wz_spk_secret` | hex SPK secret (64 chars) | Signed pre-key secret | | `wz_session:` | base64 ratchet state | Per-peer session | | `wz_contacts` | JSON contact list | Contact metadata | --- ## 10. Session Management ### How Sessions Work A "session" is a Double Ratchet state between you and one peer, identified by their fingerprint. 1. **First message to a peer:** X3DH key exchange establishes a shared secret. The ratchet is initialized. The session is saved in `~/.warzone/db/` under the `sessions` tree, keyed by the peer's hex fingerprint. 2. **Subsequent messages:** the ratchet state is loaded, used to encrypt or decrypt, then saved back. 3. **Bidirectional:** when Bob receives Alice's `KeyExchange`, he initializes his side. From then on, both use `WireMessage::Message`. ### Session Auto-Recovery On decrypt failure, the TUI deletes the corrupted session and displays a warning. The next incoming `KeyExchange` from that peer re-establishes the session automatically. No manual intervention required. ### Multi-Device The server stores per-device bundles (`device::`). Multiple WebSocket connections per fingerprint are supported -- all connected devices receive messages. Ratchet sessions are per-device and not synchronized; use `warzone backup` / `warzone restore` to transfer session state. --- ## 11. Troubleshooting ### "No identity found. Run `warzone init` first." `~/.warzone/identity.seed` is missing. Run `warzone init`. ### "No bundle found. Run `warzone init` first." `~/.warzone/bundle.bin` is missing. This happens if you ran `recover` without regenerating pre-keys. Re-run `warzone init` (generates a new identity). ### "failed to fetch recipient's bundle. Are they registered?" The recipient has not registered with the server, or the fingerprint / alias is wrong, or the server URL is incorrect. Verify with `warzone info` and `warzone register`. ### "X3DH respond failed" / "missing signed pre-key" Signed pre-key secret missing from local database. Database may have been deleted or corrupted. Re-initialize with `warzone init`. ### "[session reset] Decryption failed" The TUI auto-recovery has cleared the corrupted session. Ask the other party to send a new message -- a fresh `KeyExchange` will re-establish the session. ### Corrupted Database ```bash # Back up your seed first cp ~/.warzone/identity.seed ~/identity.seed.bak rm -rf ~/.warzone/db/ warzone init # regenerate pre-keys (NOTE: generates a new identity) # To keep your old identity, recover from mnemonic after: warzone recover <24 words> ```