Files
featherChat/warzone/docs/CLIENT.md
Siavash Sameni 60a7006ed9 Add documentation: protocol spec, server admin, client guide
docs/PROTOCOL.md (520 lines):
- Identity model (seed → Ed25519 + X25519 via HKDF)
- X3DH key exchange (4 DH operations, ASCII flow diagram)
- Double Ratchet (chain/DH ratchet, skipped keys, state machine)
- KDF chains with domain separation strings
- AEAD (ChaCha20-Poly1305)
- Wire format (WireMessage enum, bincode serialization)
- Pre-key bundle format and lifecycle

docs/SERVER.md (429 lines):
- Build and run instructions
- Full API reference with request/response examples
- Database structure (sled trees)
- Deployment (nginx reverse proxy, systemd unit)
- Security considerations
- Backup and recovery

docs/CLIENT.md (507 lines):
- Quick start guide
- All CLI commands with examples
- Identity management and mnemonic backup
- Web client usage and limitations
- Session and pre-key management
- Threat model table
- Troubleshooting guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:59:19 +04:00

16 KiB

Warzone Client -- Operation Guide


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

2. Quick Start

# 1. Generate a new identity
warzone init

# 2. Register your key bundle with a server
warzone register -s http://wz.example.com:7700

# 3. Send an encrypted message
warzone send a3f8:c912:44be:7d01 "Hello from Warzone" -s http://wz.example.com:7700

# 4. Poll for incoming messages
warzone recv -s http://wz.example.com:7700

3. CLI Commands

warzone init

Generate a new identity (seed, keypair, and pre-keys).

$ warzone init
Identity generated!

Fingerprint: b7d1:e845:0022:9f3a

Recovery mnemonic (WRITE THIS DOWN):

 1. abandon      2. ability      3. able         4. about
 5. above        6. absent       7. absorb       8. abstract
 9. absurd      10. abuse       11. access      12. accident
13. account     14. accuse      15. achieve     16. acid
17. acoustic    18. acquire     19. across      20. act
21. action      22. actor       23. actress     24. actual

Seed saved to ~/.warzone/identity.seed
Generated 1 signed pre-key + 10 one-time pre-keys

To register with a server, run:
  warzone send <recipient-fingerprint> <message> -s http://server:7700

Or register your key bundle manually:
  (bundle auto-registered on first send)

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. Saves the raw seed to ~/.warzone/identity.seed (mode 0600 on Unix).
  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 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
Identity recovered!
Fingerprint: b7d1:e845:0022:9f3a
Seed saved to ~/.warzone/identity.seed

Note: recovery restores the seed and keypair but does NOT restore pre-keys or sessions. You will need to run warzone init-style pre-key generation separately or your contacts will need to re-establish sessions.


warzone info

Display your fingerprint and public keys.

$ warzone info
Fingerprint: b7d1:e845:0022:9f3a
Signing key: 3a7c...  (64 hex chars)
Encryption key: 9d2f...  (64 hex chars)

Requires a saved identity (~/.warzone/identity.seed).


warzone register

Register your pre-key bundle with a server.

$ warzone register -s http://wz.example.com:7700
Bundle registered with http://wz.example.com:7700

Flags:

Flag Short Default Description
--server -s http://localhost:7700 Server URL

This uploads ~/.warzone/bundle.bin to the server. Registration is also performed automatically on the first send.


warzone send

Send an encrypted message to a recipient.

$ warzone send a3f8:c912:44be:7d01 "Hello, are you safe?" -s http://wz.example.com:7700
No existing session. Fetching key bundle for a3f8:c912:44be:7d01...
Bundle registered with http://wz.example.com:7700
Message sent to a3f8:c912:44be:7d01

Arguments:

Argument Description
recipient Recipient fingerprint (e.g. a3f8:c912:44be:7d01)
message Message text (quote if it contains spaces)

Flags:

Flag Short Default Description
--server -s http://localhost:7700 Server URL

Behavior:

  1. Auto-registers your bundle with the server (if not already done).
  2. Checks for an existing Double Ratchet session with the recipient.
  3. If no session exists:
    • Fetches recipient's pre-key bundle from the server.
    • Verifies the signed pre-key signature.
    • Performs X3DH key exchange.
    • Initializes the Double Ratchet as Alice (initiator).
    • Sends a WireMessage::KeyExchange containing the X3DH parameters and the first encrypted message.
  4. If a session exists:
    • Encrypts using the existing ratchet.
    • Sends a WireMessage::Message.
  5. Updates the local session state.

warzone recv

Poll for and decrypt incoming messages.

$ warzone recv -s http://wz.example.com:7700
Polling for messages as b7d1:e845:0022:9f3a...
Received 2 message(s):

  [new session] a3f8:c912:44be:7d01: Hello, are you safe?
  a3f8:c912:44be:7d01: I'm sending supplies tomorrow.

Flags:

Flag Short Default Description
--server -s http://localhost:7700 Server URL

Behavior:

  1. Polls /v1/messages/poll/{our_fingerprint}.
  2. For each message:
    • Deserializes the WireMessage from bincode.
    • KeyExchange: loads signed pre-key secret and (if applicable) one-time pre-key secret from local storage, performs X3DH respond, initializes ratchet as Bob, decrypts the message, and saves the session.
    • Message: loads existing session, decrypts with the ratchet, saves updated session state.
  3. Prints decrypted messages to stdout.

Note: messages are currently NOT acknowledged after polling. They will be returned again on the next poll. Acknowledgment is TODO.


warzone chat

Launch the interactive TUI.

$ warzone chat -s http://wz.example.com:7700
TODO: launch TUI connected to http://wz.example.com:7700

Status: not yet implemented. The TUI will use ratatui and crossterm (dependencies are already in Cargo.toml). Planned for Phase 2.


4. Identity Management

Storage Layout

~/.warzone/
  identity.seed      # 32-byte raw seed (plaintext -- encryption is TODO)
  bundle.bin         # bincode-serialized PreKeyBundle (public data)
  db/                # sled database directory
    sessions/        # Double Ratchet state per peer
    pre_keys/        # signed and one-time pre-key secrets

File Permissions

On Unix, identity.seed is created with mode 0600 (owner read/write only). The sled database directory inherits default permissions.

Seed Security

Current state: the seed is stored as plaintext 32 bytes. This is a known Phase 1 limitation.

Planned (Phase 2): encrypt the seed at rest using:

  • Passphrase input at startup
  • Argon2id key derivation from passphrase
  • ChaCha20-Poly1305 encryption of the seed bytes

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 and store it securely.

The mnemonic is displayed once at generation time and can be recovered from the seed using the protocol library, but the CLI does not currently expose a "show mnemonic" command.

Recovery

warzone recover word1 word2 word3 ... word24

This recreates ~/.warzone/identity.seed with the same seed. The same fingerprint and keypairs are derived deterministically. However:

  • Pre-keys are NOT regenerated. Run warzone init on a fresh directory to generate new pre-keys (this will also generate a new seed, so you would need to coordinate).
  • Sessions are NOT recovered. All contacts will need to establish new sessions.

TODO: a recover flow that also regenerates pre-keys without creating a new seed.


5. Web Client

The web client is served by the server at /. Open it in a browser:

http://localhost:7700/

Features

  • Generate New Identity: creates a random 32-byte seed in the browser.
  • Recover from Mnemonic: paste a hex-encoded seed (not BIP39 words; hex encoding is used as a placeholder).
  • Chat interface: dark-themed monospace UI with message display.
  • Commands:
    • /help -- show available commands
    • /info -- show your fingerprint
    • /seed -- display your seed (hex-encoded)

How It Works

  1. Seed is generated with crypto.getRandomValues(32).
  2. ECDH P-256 keypair is derived (not X25519 -- Web Crypto limitation).
  3. Fingerprint is SHA-256(ECDH_public_key)[0..16] formatted as 4 hex groups.
  4. Seed is saved in localStorage under key wz-seed.
  5. On page load, the client tries to auto-load a saved seed.
  6. Public key is registered with the server via POST /v1/keys/register.
  7. Messages are polled every 5 seconds from /v1/messages/poll/{fingerprint}.

Limitations

  • No cross-client compatibility: the web client uses P-256 while the CLI uses X25519/Ed25519. Messages between the two cannot be decrypted. This will be resolved in Phase 2 (WASM port of the protocol library).
  • No Double Ratchet: message decryption is not implemented in JS. Received messages display as [encrypted message].
  • No BIP39: seed is shown as hex bytes, not mnemonic words.
  • Unencrypted seed storage: localStorage is accessible to any JS on the same origin.

6. 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 fingerprint (hex-encoded).

  2. Subsequent messages: the ratchet state is loaded, used to encrypt or decrypt, then saved back.

  3. Bidirectional: both parties maintain the same session. When Bob receives Alice's KeyExchange, he initializes his side of the ratchet. From then on, both use WireMessage::Message.

Session Storage

Sessions are serialized with bincode and stored in the sessions sled tree. The key is the peer's 32-character hex fingerprint.

Session Reset

There is currently no command to reset a session. If a session becomes corrupted or out of sync:

  1. Delete the local database: rm -rf ~/.warzone/db/
  2. Re-run warzone init to generate new pre-keys.
  3. Re-register with the server.
  4. Your contact must also reset their session with you.

TODO (Phase 2): a warzone reset-session <fingerprint> command.


7. Pre-Key Management

What Are Pre-Keys

Pre-keys enable asynchronous session establishment. When Alice wants to message Bob for the first time:

  1. Alice fetches Bob's pre-key bundle from the server.
  2. The bundle contains Bob's public identity key, a signed pre-key, and optionally a one-time pre-key.
  3. Alice uses these to perform X3DH without Bob being online.

Pre-Key Types

Type Quantity Lifetime Purpose
Signed pre-key 1 (id=1) Long-term (no rotation yet) Medium-term DH key, signed by identity
One-time pre-keys 10 (ids 0-9) Single use Consumed during X3DH, then deleted

When to Replenish

One-time pre-keys are consumed when someone initiates a session with you. After all 10 are used, X3DH falls back to using only the signed pre-key (DH4 is skipped), which provides slightly weaker security properties.

Current state: there is no automatic replenishment. You must manually re-initialize if you expect many incoming new sessions.

TODO (Phase 2): the server will notify the client when one-time pre-key supply is low, and the client will upload fresh ones automatically.


8. Security Model

What Is Encrypted

  • Message body: encrypted with ChaCha20-Poly1305 using per-message keys from the Double Ratchet. Even the server cannot read it.

What Is NOT Encrypted

  • Sender fingerprint: visible to the server and anyone intercepting traffic.
  • Recipient fingerprint: visible to the server (needed for routing).
  • Message size: visible to the server.
  • Timing: when messages are sent and received.
  • IP addresses: visible to the server and network observers.
  • Seed on disk: stored as plaintext (encryption TODO).

Threat Model

Threat Protected? Notes
Server reads messages Yes E2E encryption; server sees only ciphertext
Network eavesdropper reads messages Yes E2E encryption
Server impersonates a user Partially Pre-key signatures prevent forgery of signed pre-keys, but the server could substitute a fake bundle (no key transparency yet)
Compromised past session key Yes Forward secrecy via chain ratchet; break-in recovery via DH ratchet
Stolen device (seed file) No Seed is plaintext on disk (encryption TODO)
Metadata analysis (who talks to whom) No Fingerprints visible to server
Active MITM on first contact Partially TOFU model; no out-of-band verification mechanism in the client yet
One-time pre-keys exhausted Graceful degradation X3DH works without OT pre-keys but with reduced replay protection

Trust Model

Trust on first use (TOFU): the first time you message someone, you trust that the server returns their genuine pre-key bundle. There is no verification step yet.

Planned (Phase 3): DNS-based key transparency where users publish self-signed public keys in DNS TXT records, allowing cross-verification independent of the server.


9. Troubleshooting

"No identity found. Run warzone init first."

You haven't generated an identity, or ~/.warzone/identity.seed is missing.

warzone init

"No bundle found. Run warzone init first."

The pre-key bundle file ~/.warzone/bundle.bin is missing. This happens if you ran recover without a full init.

Re-run warzone init (this will generate a NEW identity). To keep your recovered identity, you would need to manually regenerate pre-keys (not yet supported as a standalone command).

"failed to fetch recipient's bundle. Are they registered?"

The recipient has not registered their pre-key bundle with the server, or you are using the wrong server URL, or the fingerprint is incorrect.

  • Verify the fingerprint (ask the recipient for theirs via warzone info).
  • Verify the server URL.
  • Ask the recipient to run warzone register -s <server>.

"X3DH respond failed" / "missing signed pre-key"

Your signed pre-key secret is missing from the local database. This can happen if:

  • The database was deleted or corrupted.
  • You recovered an identity but did not regenerate pre-keys.

Fix: re-initialize with warzone init (generates a new identity) or restore from backup.

"decrypt failed" / "no session"

  • "no session": you received a WireMessage::Message from someone you have no session with. This means you missed their initial KeyExchange message, or your session database was lost. Ask them to re-send their first message.
  • "decrypt failed": the ratchet state is out of sync. This can happen if one side's state was lost or if messages were duplicated. Reset the session on both sides.

Messages Keep Reappearing on recv

Messages are not auto-acknowledged after polling. This is a known Phase 1 limitation. The same messages will be returned on every recv call.

Workaround: none currently. Acknowledgment will be added in Phase 2.

Corrupted Database

If ~/.warzone/db/ is corrupted:

rm -rf ~/.warzone/db/
warzone init   # regenerate pre-keys (NOTE: generates a new identity)

To keep your existing identity, manually copy identity.seed before deleting, then use warzone recover after re-init.