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>
15 KiB
Warzone Server -- Administration Guide
Version 0.0.21
1. Building
Local Build
From the workspace root:
# Debug
cargo build -p warzone-server
# Release (recommended for deployment)
cargo build -p warzone-server --release
Binary output: target/release/warzone-server.
Cross-Compile for Linux (x86_64)
The scripts/build-linux.sh script spins up a Hetzner Cloud VPS, builds
Linux release binaries, and pulls them back to target/linux-x86_64/.
# Full pipeline: build + deploy to all production servers + destroy VM
./scripts/build-linux.sh --ship
# Step-by-step:
./scripts/build-linux.sh --prepare # create VM, install deps, upload source
./scripts/build-linux.sh --build # compile release binaries on the VM
./scripts/build-linux.sh --transfer # download binaries to target/linux-x86_64/
./scripts/build-linux.sh --destroy # delete the VM
# Or all three build steps at once (VM persists):
./scripts/build-linux.sh --all
Minimum Rust Version
Rust 1.75 or later (rust-version = "1.75" in Cargo.toml).
2. Running
Basic
# Defaults: bind 0.0.0.0:7700, data in ./warzone-data
./warzone-server
# Custom bind address and data directory
./warzone-server --bind 0.0.0.0:7700 --data-dir ./data
# With federation enabled
./warzone-server --federation federation.json
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 the sled database |
--federation |
-f |
(none) | Path to federation JSON config file |
Environment Variables
| Variable | Default | Description |
|---|---|---|
RUST_LOG |
warn (production) |
Log filter. Examples: info, warzone_server=debug, trace |
WZP_RELAY_ADDR |
(none) | WZP voice relay address advertised to clients |
systemd Service
A production-ready unit file is provided at deploy/warzone-server.service:
[Unit]
Description=Warzone Messenger Server (featherChat)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=warzone
Group=warzone
WorkingDirectory=/home/warzone
ExecStart=/home/warzone/warzone-server --bind 0.0.0.0:7700 --data-dir /home/warzone/data --federation /home/warzone/federation.json
Restart=always
RestartSec=3
LimitNOFILE=65536
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/warzone/data
PrivateTmp=yes
Environment=RUST_LOG=warn,warzone_server::federation=info
[Install]
WantedBy=multi-user.target
Install and enable:
sudo cp deploy/warzone-server.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now warzone-server
3. Configuration
Federation JSON
Enable federation by passing --federation <path> on startup. The config
file specifies the local server identity, peer connection details, and a
shared secret for authentication.
Format (see federation.example.json):
{
"server_id": "alpha",
"shared_secret": "change-me-to-a-long-random-string-shared-between-both-servers",
"peer": {
"id": "bravo",
"url": "http://10.0.0.2:7700"
},
"presence_interval_secs": 5
}
| Field | Description |
|---|---|
server_id |
Unique name for this server (e.g. "alpha") |
shared_secret |
Pre-shared secret; must match on both sides |
peer.id |
The remote server's server_id |
peer.url |
HTTP base URL of the remote server |
presence_interval_secs |
How often to broadcast online-user lists (default 5) |
4. Federation
Federation connects two Warzone servers over a persistent WebSocket so their users can communicate transparently.
How It Works
- On startup, each server opens an outgoing WebSocket to its peer at
/v1/federation/wsand authenticates with the shared secret. - The connection auto-reconnects on failure.
- Presence (online fingerprints) is synced on the configured interval.
- Messages to users on the remote server are forwarded automatically.
Federated Features
| Feature | Behavior |
|---|---|
| Key lookup proxy | If a key bundle is not found locally, the server queries the peer |
| Message forwarding | Messages addressed to a remote fingerprint are relayed over the WS |
| Alias resolution | /v1/resolve/:address checks the peer if the alias is not local |
| Presence sync | Each server broadcasts its online fingerprints to the peer |
Two-Server Setup
Server A (alpha, e.g. mequ):
{
"server_id": "alpha",
"shared_secret": "s3cret-shared-between-both",
"peer": { "id": "bravo", "url": "http://bravo-host:7700" },
"presence_interval_secs": 5
}
Server B (bravo, e.g. kh3rad3ree):
{
"server_id": "bravo",
"shared_secret": "s3cret-shared-between-both",
"peer": { "id": "alpha", "url": "http://alpha-host:7700" },
"presence_interval_secs": 5
}
Both files use the same shared_secret. Each server's peer.id matches
the other server's server_id.
Federation Status Endpoint
curl http://localhost:7700/v1/federation/status
Returns JSON with connection state, peer info, and presence data.
5. API Reference
All endpoints are prefixed with /v1. The web UI is served at /.
Notation
- Auth = requires
Authorization: Bearer <token>header (write routes). - Public = no authentication needed (read routes).
Health
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/health |
Public | Health check; returns {"status":"ok"} |
Keys
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/keys/register |
Public | Register a pre-key bundle |
| POST | /v1/keys/replenish |
Public | Upload additional one-time pre-keys |
| GET | /v1/keys/:fingerprint |
Public | Fetch a key bundle (falls back to federation peer) |
| GET | /v1/keys/list |
Public | List all registered fingerprints |
| GET | /v1/keys/:fingerprint/otpk-count |
Public | Remaining one-time pre-key count |
| GET | /v1/keys/:fingerprint/devices |
Public | List devices for a fingerprint |
Messages
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/messages/send |
Auth | Send an encrypted message blob |
| GET | /v1/messages/poll/:fingerprint |
Public | Poll queued messages |
| DELETE | /v1/messages/:id/ack |
Public | Acknowledge (delete) a message |
Groups
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/groups/create |
Auth | Create a group |
| POST | /v1/groups/:name/join |
Auth | Join a group |
| POST | /v1/groups/:name/send |
Auth | Send a message to a group |
| POST | /v1/groups/:name/leave |
Auth | Leave a group |
| POST | /v1/groups/:name/kick |
Auth | Kick a member from a group |
| GET | /v1/groups |
Public | List all groups |
| GET | /v1/groups/:name |
Public | Get group details |
| GET | /v1/groups/:name/members |
Public | List members (includes online status) |
Aliases
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/alias/register |
Auth | Register a human-readable alias |
| POST | /v1/alias/unregister |
Auth | Remove your alias |
| POST | /v1/alias/recover |
Auth | Transfer alias to a new fingerprint |
| POST | /v1/alias/renew |
Auth | Renew alias expiry |
| POST | /v1/alias/admin-remove |
Auth | Admin-remove an alias |
| GET | /v1/alias/resolve/:name |
Public | Resolve alias to fingerprint |
| GET | /v1/alias/list |
Public | List all registered aliases |
| GET | /v1/alias/whois/:fingerprint |
Public | Reverse-lookup: fingerprint to alias |
Calls (WZP)
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/calls/initiate |
Auth | Start a 1:1 call |
| POST | /v1/calls/:id/end |
Auth | End an active call |
| POST | /v1/calls/missed |
Auth | Get missed calls for a fingerprint |
| POST | /v1/groups/:name/call |
Auth | Initiate a group call |
| GET | /v1/calls/:id |
Public | Get call details |
| GET | /v1/calls/active |
Public | List active calls |
Devices
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/devices/:id/kick |
Auth | Disconnect a specific device |
| POST | /v1/devices/revoke-all |
Auth | Disconnect all devices (optional keep one) |
| GET | /v1/devices |
Auth | List your connected devices |
Presence
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/presence/batch |
Auth | Batch-query presence for multiple fingerprints |
| GET | /v1/presence/:fingerprint |
Public | Check if a fingerprint is online |
Friends
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/friends |
Auth | Save friend list (encrypted blob) |
| GET | /v1/friends |
Auth | Retrieve saved friend list |
Resolve
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/resolve/:address |
Public | Universal resolve: ETH address, alias, or fingerprint. Checks federation peer if not found locally. |
WZP Voice Relay
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/wzp/relay-config |
Public | Get the WZP relay address for voice calls |
Federation
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/federation/status |
Public | Federation connection status and peer info |
| GET | /v1/federation/ws |
Internal | WebSocket endpoint for server-to-server communication |
Bot API
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/bot/register |
Auth | Register a bot; returns an API token |
| GET | /v1/bot/:token/getMe |
Token | Bot identity info |
| POST | /v1/bot/:token/getUpdates |
Token | Long-poll for new messages (Telegram-compatible) |
| POST | /v1/bot/:token/sendMessage |
Token | Send a message as the bot (Telegram-compatible) |
Bot tokens are scoped to the bot's fingerprint. The getUpdates and
sendMessage endpoints follow the Telegram Bot API conventions so existing
Telegram bot libraries can be adapted with minimal changes.
WebSocket
| Path | Description |
|---|---|
/v1/ws/:fingerprint |
Real-time message delivery. Clients receive instant push of new messages. |
Web UI
| Path | Description |
|---|---|
/ |
Single-page WASM web client |
/wasm/warzone_wasm.js |
WASM JavaScript bindings |
/wasm/warzone_wasm_bg.wasm |
WASM binary |
6. Database
The server uses sled (embedded key-value store). All data lives under
the --data-dir directory.
Trees
| Tree | Purpose |
|---|---|
keys |
Pre-key bundles (public keys only) |
messages |
Queued encrypted message blobs |
groups |
Group metadata and membership |
aliases |
Human-readable alias mappings |
tokens |
Authentication tokens (device sessions) |
calls |
Call records (1:1 and group) |
missed_calls |
Missed call notifications |
friends |
Encrypted friend lists |
eth_addresses |
Ethereum address to fingerprint mappings |
Data Directory Structure
warzone-data/
db # sled database file
conf # sled config
blobs/ # sled blob storage
snap.*/ # sled snapshots
The entire directory should be treated as a unit for backup. Stop the server before copying, or use filesystem-level snapshots (LVM, ZFS, btrfs).
7. Security
Auth Middleware
All write (POST) endpoints require a bearer token in the Authorization
header. Tokens are issued during key registration and tied to a fingerprint.
Read (GET) endpoints are public.
Rate Limiting
- 200 concurrent requests (tower
ConcurrencyLimitLayer) - 5 WebSocket connections per fingerprint (multi-device cap)
Device Management
Users can list connected devices, kick individual devices, or revoke all
sessions via the /v1/devices endpoints. The revoke-all endpoint accepts
an optional keep_device_id to keep the current device active.
What the Server Can See
| Data | Visible |
|---|---|
| Message plaintext | No (E2E encrypted blobs) |
| Sender/recipient fingerprints | Yes |
| Message size and timing | Yes |
| Public pre-key bundles | Yes (public by design) |
| IP addresses | Yes (from HTTP) |
8. Monitoring
Logging
Control verbosity with RUST_LOG:
RUST_LOG=warn ./warzone-server # production default
RUST_LOG=info ./warzone-server # request-level logging
RUST_LOG=warzone_server=debug ./warzone-server # server internals
RUST_LOG=trace ./warzone-server # everything
With systemd:
journalctl -u warzone-server -f
Health Check
curl http://localhost:7700/v1/health
Federation Status
curl http://localhost:7700/v1/federation/status
Returns connection state, peer identity, and synced presence data.
9. Deploy Scripts
The scripts/build-linux.sh script handles the full build and deploy
lifecycle via Hetzner Cloud VMs.
Key Commands
| Command | Description |
|---|---|
--ship |
Full pipeline: build on VM, deploy to all production servers, destroy VM |
--update-all |
Upload pre-built binaries to all production servers and restart |
--update <user@host> |
Update a single production server |
--status |
Check service status and federation on all production servers |
--logs [user@host] |
Tail journalctl logs (defaults to first production server) |
Typical Deploy Workflow
# One command: build, deploy everywhere, clean up
./scripts/build-linux.sh --ship
# Or step by step:
./scripts/build-linux.sh --all # build (VM persists)
./scripts/build-linux.sh --update-all # deploy binaries
./scripts/build-linux.sh --destroy # clean up VM
./scripts/build-linux.sh --status # verify
10. Backup and Recovery
Backup
systemctl stop warzone-server
cp -r /home/warzone/data /backup/warzone-$(date +%Y%m%d)
systemctl start warzone-server
Do not copy the sled directory while the server is running without filesystem-level snapshots.
Recovery
- Stop the server.
- Replace the data directory with the backup.
- Start the server.
Messages queued after the backup was taken are permanently lost. All messages are E2E encrypted and cannot be recovered from any other source.