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>
430 lines
10 KiB
Markdown
430 lines
10 KiB
Markdown
# Warzone Server -- Operation & Administration
|
|
|
|
---
|
|
|
|
## 1. Building
|
|
|
|
The server is part of the Cargo workspace. From the workspace root:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"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):**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"to": "b7d1:e845:0022:9f3a",
|
|
"message": [/* bincode-serialized WireMessage as byte array */]
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"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):**
|
|
```json
|
|
[
|
|
"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:**
|
|
```json
|
|
{
|
|
"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)
|
|
|
|
```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:
|
|
|
|
```bash
|
|
./warzone-server --bind 127.0.0.1:7700
|
|
```
|
|
|
|
### systemd Service
|
|
|
|
```ini
|
|
[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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
|
|
1. Stop the server.
|
|
2. Replace the data directory with the backup.
|
|
3. 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.
|