Files
featherChat/warzone/docs/SERVER.md
Siavash Sameni 76cac77259 v0.0.28: BotFather-only registration, per-instance bot toggle, docs update
Security:
- Bot registration restricted to BotFather (requires botfather_token)
- Direct POST /v1/bot/register without BotFather auth → rejected

Deploy:
- systemd service reads /home/warzone/server.env for EXTRA_ARGS
- deploy/warzone-server.env.mequ: no bots (default)
- deploy/warzone-server.env.kh3rad3ree: --enable-bots
- setup.sh copies per-hostname env file

Docs updated:
- LLM_HELP.md: BotFather flow, plaintext bot messaging, E2E option, bridge
- LLM_BOT_DEV.md: botfather_token requirement, E2E mode, bridge section
- BOT_API.md: full BotFather flow, ownership, numeric IDs, webhook delivery
- SERVER.md: --enable-bots flag, per-instance config, bot system section
- USAGE.md: bot messaging, BotFather, bridge tool

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 09:52:12 +04:00

585 lines
16 KiB
Markdown

# Warzone Server -- Administration Guide
**Version 0.0.21**
---
## 1. Building
### Local Build
From the workspace root:
```bash
# 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/`.
```bash
# 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
```bash
# 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 |
| `--enable-bots` | | *(off)* | Enable Bot API and auto-create BotFather on startup |
### 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 |
### Per-Instance Configuration (`server.env`)
Each server instance can use a `server.env` file for per-instance settings.
Place it in the working directory or alongside the binary. This allows
different instances to have different configurations (e.g., bots enabled on
one server but not another).
Example `server.env`:
```
RUST_LOG=info
WZP_RELAY_ADDR=relay.example.com:3478
ENABLE_BOTS=true
```
### systemd Service
A production-ready unit file is provided at `deploy/warzone-server.service`:
```ini
[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:
```bash
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`):
```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/ws` and 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`):
```json
{
"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`):
```json
{
"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
```bash
curl http://localhost:7700/v1/federation/status
```
Returns JSON with connection state, peer info, and presence data.
---
## 4b. Bot System
### Enabling Bots
Start the server with `--enable-bots` to activate bot functionality. Without
this flag, all bot endpoints return 403.
```bash
./warzone-server --bind 0.0.0.0:7700 --enable-bots
```
### BotFather Auto-Creation
On first start with `--enable-bots`, the server auto-creates the `@botfather`
bot. The BotFather token is printed to the server logs. Users interact with
`@botfather` to register new bots.
### Per-Instance Bot Toggle
Bot support can be enabled independently per server instance:
| Instance | Bots | Config |
|----------|------|--------|
| mequ | Disabled | No `--enable-bots` flag |
| kh3rad3ree | Enabled | `--enable-bots` flag set |
### Bot Webhook Delivery
When a bot has a webhook configured (via `setWebhook`), incoming messages are
delivered live to the webhook URL via HTTP POST instead of being queued for
`getUpdates` polling. This is integrated into the standard message routing
pipeline -- `deliver_or_queue` checks for webhook configuration before
queueing.
---
## 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`:
```bash
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:
```bash
journalctl -u warzone-server -f
```
### Health Check
```bash
curl http://localhost:7700/v1/health
```
### Federation Status
```bash
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
```bash
# 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
```bash
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
1. Stop the server.
2. Replace the data directory with the backup.
3. 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.