Tier 1 — New features: - E2E encrypted friend list: server stores opaque blob (POST/GET /v1/friends), protocol-level encrypt/decrypt with HKDF-derived key, 4 tests - Telegram Bot API compatibility: /bot/register, /bot/:token/getUpdates, sendMessage, getMe — TG-style Update objects with proper message mapping - ETH address resolution: GET /v1/resolve/:address (0x.../alias/@.../fp), bidirectional ETH↔fp mapping stored on key registration - Seed recovery: /seed command in TUI + web client - URL deep links: /message/@alias, /message/0xABC, /group/#ops - Group members with online status in GET /groups/:name/members Tier 2 — UX polish: - TUI: /friend, /friend <addr>, /unfriend <addr> with presence checking - Web: friend commands, showGroupMembers() on group join - Web: ETH address in header, clickable addresses (click→peer or copy) - Bot: full WireMessage→TG Update mapping (encrypted base64, CallSignal, FileHeader, bot_message JSON) Documentation: - USAGE.md rewritten: complete user guide with all commands - SERVER.md rewritten: full admin guide with all 50+ endpoints - CLIENT.md rewritten: architecture, commands, keyboard, storage - LLM_HELP.md created: 1083-word token-optimized reference for helper LLM Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
20 KiB
Warzone Client -- Operation Guide
Version: 0.0.21
1. Installation
Build from Source
Requires Rust 1.75+.
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.
# Optional: install to ~/.cargo/bin
cargo install --path crates/warzone-client
Build the WASM Module (Web Client)
Requires wasm-pack.
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<Mutex<Vec<ChatLine>>>.
3. CLI Subcommands
warzone init
Generate a new identity (seed, keypair, pre-keys).
$ 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:
- Generates 32 random bytes (seed) from
OsRng. - Derives Ed25519 signing key and X25519 encryption key from the seed.
- Converts seed to a 24-word BIP39 mnemonic and displays it.
- 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. - Generates 1 signed pre-key (id=1) and 10 one-time pre-keys (ids 0-9).
- Stores pre-key secrets in the local sled database at
~/.warzone/db/. - Saves the public pre-key bundle to
~/.warzone/bundle.bin.
warzone recover <words...>
Recover an identity from a 24-word BIP39 mnemonic.
$ 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.
$ 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.
$ 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 <recipient> <message>
Send an encrypted message. Recipient can be a fingerprint or @alias.
$ warzone send a3f8:c912:44be:7d01:... "Hello!" --server http://wz.example.com:7700
$ warzone send @alice "Hello!" --server http://wz.example.com:7700
Behavior:
- Auto-registers your bundle with the server if needed.
- Checks for an existing Double Ratchet session with the recipient.
- 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::KeyExchangecontaining the X3DH parameters and the first encrypted message. - If a session exists: encrypts with the existing ratchet and sends a
WireMessage::Message. - Updates the local session state.
warzone recv
Poll for and decrypt incoming messages.
$ 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).
$ 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": { "<fp>": "base64_bincode", ... },
"pre_keys": { "spk:1": "base64_bytes", "otpk:1": "base64_bytes", ... }
}
warzone restore <input>
Restore from an encrypted backup. Requires the same seed (passphrase prompt).
$ 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 = 0means 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:
- Deletes the corrupted session from the local database.
- Displays a system message:
[session reset] Decryption failed for <fp>. 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 <fp_or_alias> |
/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 <name> |
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 <name> |
Switch to group (auto-join if needed) |
/gcreate <name> |
Create a new group (you become creator) |
/gjoin <name> |
Join an existing group |
/gleave |
Leave the current group |
/gkick <fp_or_alias> |
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 <path> |
Send a file to the current peer or group |
Constraints:
- Maximum file size: 10 MB
- Chunk size: 64 KB
- Files are sent as
FileHeader+ encryptedFileChunkwire messages - SHA-256 verification on receipt
- Received files are saved to
~/.warzone/downloads/
Device Management
| Command | Description |
|---|---|
/devices |
List your active device sessions |
/kick <device_id> |
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
FriendListcontaining address, alias, andadded_attimestamp per friend.
Commands
| Command | Description |
|---|---|
/friend |
List all friends with online/offline presence |
/friend <address> |
Add a friend (fingerprint or ETH address) |
/unfriend <address> |
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
- On
/friend <address>: the client fetches the current encrypted blob from the server, decrypts it, adds the entry, re-encrypts, and uploads. - On
/unfriend <address>: same fetch-decrypt-modify-encrypt-upload cycle. - On
/friend(no argument): fetches and decrypts the blob, then checks/v1/presence/<fp>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-protocoldirectly, so web-to-CLI interoperability is fully supported. - URL deep links: paths like
/message/@alias,/message/0xABC, and/group/#opsauto-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 (
/installcommand). - 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:<fp> |
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.
-
First message to a peer: X3DH key exchange establishes a shared secret. The ratchet is initialized. The session is saved in
~/.warzone/db/under thesessionstree, keyed by the peer's hex fingerprint. -
Subsequent messages: the ratchet state is loaded, used to encrypt or decrypt, then saved back.
-
Bidirectional: when Bob receives Alice's
KeyExchange, he initializes his side. From then on, both useWireMessage::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:<fp>:<device_id>). 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
# 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>