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>
10 KiB
Warzone Server -- Operation & Administration
1. Building
The server is part of the Cargo workspace. From the workspace root:
# Debug build
cargo build -p warzone-server
# Release build (recommended for deployment)
cargo build -p warzone-server --release
The resulting binary is at target/release/warzone-server (or
target/debug/warzone-server). It is a single statically-linked binary with
no runtime dependencies beyond libc.
Minimum Rust Version
Rust 1.75 or later (set via rust-version = "1.75" in Cargo.toml).
2. Running
# Default: bind 0.0.0.0:7700, data in ./warzone-data
./warzone-server
# Custom bind address and data directory
./warzone-server --bind 127.0.0.1:8080 --data-dir /var/lib/warzone
CLI Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--bind |
-b |
0.0.0.0:7700 |
Address and port to listen on |
--data-dir |
-d |
./warzone-data |
Directory for sled database files |
Logging
The server uses tracing-subscriber. Control log level with the RUST_LOG
environment variable:
RUST_LOG=info ./warzone-server
RUST_LOG=warzone_server=debug ./warzone-server
RUST_LOG=trace ./warzone-server # very verbose
3. API Reference
All API endpoints are under the /v1 prefix. The web UI is served at /.
Health Check
GET /v1/health
Response:
{
"status": "ok",
"version": "0.1.0"
}
Use this for monitoring, load balancer health probes, and uptime checks.
Register Key Bundle
POST /v1/keys/register
Content-Type: application/json
Request body:
{
"fingerprint": "a3f8:c912:44be:7d01",
"bundle": [/* bincode-serialized PreKeyBundle as byte array */]
}
The bundle field is a JSON array of unsigned bytes (the raw bincode
serialization of a PreKeyBundle).
Response:
{
"ok": true
}
Behavior: stores the bundle in the keys sled tree, keyed by the
fingerprint string. Overwrites any existing bundle for the same fingerprint.
Fetch Key Bundle
GET /v1/keys/{fingerprint}
Path parameter: the fingerprint string, e.g. a3f8:c912:44be:7d01.
Response (200):
{
"fingerprint": "a3f8:c912:44be:7d01",
"bundle": "base64-encoded-bincode-bytes..."
}
The bundle value is standard base64-encoded bincode. The client decodes
base64, then deserializes with bincode to recover the PreKeyBundle.
Response (404): returned if no bundle is registered for the fingerprint.
Send Message
POST /v1/messages/send
Content-Type: application/json
Request body:
{
"to": "b7d1:e845:0022:9f3a",
"message": [/* bincode-serialized WireMessage as byte array */]
}
Response:
{
"ok": true
}
Behavior: the message bytes are stored in the messages sled tree under
the key queue:{recipient_fingerprint}:{uuid}. The UUID is generated
server-side to ensure unique keys.
The server does NOT parse, validate, or inspect the message contents. It is an opaque blob.
Poll Messages
GET /v1/messages/poll/{fingerprint}
Response (200):
[
"base64-encoded-message-1",
"base64-encoded-message-2"
]
Returns a JSON array of base64-encoded message blobs. Each blob is a
bincode-serialized WireMessage. An empty array means no messages.
Behavior: scans the messages sled tree for all keys prefixed with
queue:{fingerprint}. Messages are NOT deleted by polling; they remain until
explicitly acknowledged.
Acknowledge Message
DELETE /v1/messages/{id}/ack
Path parameter: the message storage key (currently the full sled key
including the queue: prefix and UUID).
Response:
{
"ok": true
}
Behavior: removes the message from the messages tree.
Note: the current implementation requires knowing the exact sled key to acknowledge. A proper message-ID-based index is planned for Phase 2.
4. Web UI
The server serves a single-page web client at the root path /.
GET /
Returns an HTML page with embedded CSS and JavaScript. The web client provides:
- Identity generation: generates a random 32-byte seed in the browser
using
crypto.getRandomValues(). - Identity recovery: paste a hex-encoded seed to recover.
- Fingerprint display: shows the user's fingerprint in the header.
- Key registration: automatically registers a public key with the server on entry.
- Message polling: polls
/v1/messages/poll/{fingerprint}every 5 seconds. - Slash commands:
/help,/info,/seed.
Web Client Limitations
- Uses ECDH P-256 (Web Crypto API) instead of X25519. Cross-client compatibility with the CLI is not yet implemented. (Phase 2)
- Does not use BIP39 mnemonics; seed is displayed as hex.
- Message decryption is not yet wired (Double Ratchet in JS is TODO).
- The seed is stored in
localStorage(unencrypted).
5. Database
The server uses sled (embedded key-value store). All data lives under the
directory specified by --data-dir.
Trees (Tables)
| Tree | Key format | Value | Purpose |
|---|---|---|---|
keys |
fingerprint string (UTF-8 bytes) | bincode PreKeyBundle |
Pre-key bundle storage |
messages |
queue:{fingerprint}:{uuid} (UTF-8 bytes) |
bincode WireMessage |
Message queue |
otpks |
(reserved) | (reserved) | One-time pre-key tracking (not yet used server-side) |
Data Directory Structure
warzone-data/
db # sled database file
conf # sled config
blobs/ # sled blob storage (if any)
snap.*/ # sled snapshots
The exact file layout is managed by sled internally. The entire directory should be treated as a unit for backup.
What the Server Stores
- Pre-key bundles: public keys only. The server never holds private keys.
- Encrypted message blobs: opaque binary data. The server cannot read message contents.
- Metadata visible to server: sender fingerprint, recipient fingerprint, message size, timestamps (implicit from storage order).
6. Deployment
Single Binary
The recommended deployment is a single warzone-server binary behind a
reverse proxy for TLS termination.
Reverse Proxy (nginx)
server {
listen 443 ssl http2;
server_name wz.example.com;
ssl_certificate /etc/letsencrypt/live/wz.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/wz.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:7700;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support (for future real-time push)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
When using a reverse proxy, bind the server to localhost only:
./warzone-server --bind 127.0.0.1:7700
systemd Service
[Unit]
Description=Warzone Messenger Server
After=network.target
[Service]
Type=simple
User=warzone
ExecStart=/usr/local/bin/warzone-server --bind 127.0.0.1:7700 --data-dir /var/lib/warzone
Restart=always
RestartSec=5
Environment=RUST_LOG=info
[Install]
WantedBy=multi-user.target
7. Monitoring
Health Endpoint
curl http://localhost:7700/v1/health
# {"status":"ok","version":"0.1.0"}
Use this for:
- Load balancer health checks
- Uptime monitoring (e.g., with
uptime-kuma, Prometheus blackbox exporter) - Deployment verification
Logs
All request activity is logged via tracing. In production, pipe to a log
aggregator or use journalctl -u warzone-server.
8. Security Considerations
The Server Is a Dumb Relay
The server never sees plaintext message content. It stores and forwards opaque encrypted blobs. Even if the server is fully compromised, an attacker gains:
- Encrypted message blobs (useless without recipient's private keys)
- Public pre-key bundles (public by design)
- Metadata: who is messaging whom, when, and how often
What the Server CAN See
| Data | Visible to server |
|---|---|
| Message plaintext | No |
| Sender fingerprint | Yes (in WireMessage) |
| Recipient fingerprint | Yes (used for routing) |
| Message size | Yes |
| Timing | Yes |
| IP addresses | Yes (from HTTP) |
| Pre-key bundles (public keys) | Yes |
Mitigations for Metadata (Future)
- Sealed sender (Phase 6): hide sender identity from the server.
- Padding: fixed-size messages to prevent size-based analysis.
- Onion routing (Phase 6): hide IP addresses via relay chains.
Access Control
The current server has no authentication. Anyone can:
- Register a key bundle for any fingerprint
- Poll messages for any fingerprint
- Send messages to any fingerprint
TODO (Phase 2): authentication via Ed25519 challenge-response. Clients sign requests to prove they own the fingerprint they claim.
9. Backup and Recovery
Database Backup
The sled database can be backed up by copying the entire data directory while the server is stopped:
systemctl stop warzone-server
cp -r /var/lib/warzone /backup/warzone-$(date +%Y%m%d)
systemctl start warzone-server
Warning: copying the sled directory while the server is running may produce an inconsistent snapshot. Stop the server first or use filesystem-level snapshots (LVM, ZFS, btrfs).
Recovery
- Stop the server.
- Replace the data directory with the backup.
- Start the server.
Messages queued after the backup was taken will be lost. Since all messages are E2E encrypted, there is no way to recover them from any other source.
Data Loss Impact
- Lost key bundles: users must re-register. No security impact (public data).
- Lost message queue: undelivered messages are permanently lost. Senders will not know delivery failed (no delivery receipts yet).
- Corrupted database: sled includes crash recovery. If the database is corrupt beyond recovery, delete it and start fresh. Users re-register.