Files
featherChat/warzone/docs/CLIENT.md
Siavash Sameni 7b72f7cba5 feat: friend list, bot API, ETH addressing, deep links, docs overhaul
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>
2026-03-29 07:31:54 +04:00

597 lines
20 KiB
Markdown

# 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<Mutex<Vec<ChatLine>>>`.
---
## 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 <words...>`
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 <recipient> <message>`
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": { "<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).
```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 <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` + 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 <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 `FriendList` containing address, alias,
and `added_at` timestamp 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
1. On `/friend <address>`: the client fetches the current encrypted blob from
the server, decrypts it, adds the entry, re-encrypts, and uploads.
2. On `/unfriend <address>`: same fetch-decrypt-modify-encrypt-upload cycle.
3. 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-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:<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.
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:<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
```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>
```