Files
voice-cat/docs/deployment.md
Talon bcb7ae8ccb build(cmake): clean up presets, add release/apple presets, cross-platform triplets
Rationalize the preset set to match the project's actual state (past M5):
- Rename dev->skeleton (no-deps stub smoke), m1-dev->dev (default dev preset)
- Drop m2-dev (cache-identical to m1-dev)
- Add release preset (optimized + tests on, symbols kept)
- Strip server-release binaries (-s linker flag)
- Add apple-dev/apple-ios/apple-ios-sim scaffolding presets for XCFramework

Add cmake/voicecat-toolchain.cmake wrapper that auto-resolves the vcpkg
triplet from the host platform (x64-mingw-static/x64-linux/arm64-osx) so
the main presets work on Windows/Linux/macOS without per-OS variants.

Update all docs (building.md, CLAUDE.md, README.md, AGENTS.md, deployment.md,
tech-stack.md, client READMEs) and stale preset-name references in code
comments. No C++ behavior changes — the core was already portable.
2026-06-18 03:16:01 +02:00

6.0 KiB

Deployment & Self-Hosting

The product goal: someone looks at this and thinks "oh, I (or my agent) can stand this up in a few minutes." Everything below is in service of that. Three install paths, all zero-config and encrypted by default.

1. The three paths

docker run -d --name voicecat \
  -p 8384:8384/tcp \      # control (TLS 1.3)
  -p 8384:8384/udp \      # media (encrypted)
  -v voicecat-data:/data \
  ghcr.io/<org>/voicecat:latest

That's the whole thing. On first start it generates its Ed25519 identity + self-signed cert, creates the SQLite database under /data, prints the server fingerprint (for clients to verify), and listens. Control and media share one port number on TCP+UDP to keep firewall rules trivial.

A docker-compose.yml is provided for people who prefer it, but it isn't required.

B. Single static binary

# download for your OS, then:
./voicecat-server          # uses ./voicecat-data/ , prints fingerprint, runs

The server is a single statically linked executable (mbedTLS, libsodium, opus, sqlite, etc. linked in — all permissive licenses). No runtime, no shared libraries to install, no package manager. Linux (primary), macOS, and Windows builds.

C. From source

git clone … && cd voice-cat
cmake --preset server-release      # vcpkg fetches & pins all deps; auto-triplet (Linux/macOS/Windows)
cmake --build --preset server-release
./build/server-release/bin/voicecat-server

One cmake invocation; vcpkg (manifest mode) resolves the dependency graph reproducibly. The server-release preset produces an optimized, stripped binary (-s linker flag) — smaller executables suitable for distribution. Works on Linux (primary), macOS, and Windows; the vcpkg triplet is auto-resolved by cmake/voicecat-toolchain.cmake. No system packages to chase.

2. Zero-config defaults

The server runs with no config file at all. Sensible defaults:

Setting Default
Encryption On, always (not configurable off)
TLS cert / identity Auto-generated on first run, persisted to the data dir
Database Embedded SQLite in the data dir (no external DB)
Guests Enabled (so the very first connect "just works"); easily disabled
Ports 8384 TCP + UDP
A default channel One "Lobby" voice/text channel created on first run
Opus policy 48 kHz, 20 ms frames, mono/VOIP defaults; per-channel overrides allowed
Argon2id cost Auto-tuned to the host on first run

Override only what you care about, via env vars or an optional server.toml:

# server.toml — every key is optional
server_name   = "Cats United"
allow_guests  = false
bind_port     = 8384
data_dir      = "/data"

[tls]                       # only if you want a real CA cert; otherwise self-signed
cert_file = "/data/fullchain.pem"
key_file  = "/data/privkey.pem"

[opus.defaults]             # default Opus policy for new channels
mode = "mono"
bitrate_bps = 24000
frame_ms = 20
fec = true
dtx = true

[opus.limits]               # server-enforced ceilings (bound bandwidth)
max_bitrate_bps = 128000    # channels can't be configured above this

Every key also has an VOICECAT_* env var form, which is what the Docker path uses.

3. Connecting (client side)

  • Pure direct-connect. Enter host:port (and a nickname or account). There is no central directory or server browser — you connect to a server you know.
  • Saved server list. The client keeps a local list of saved servers (host:port, pinned fingerprint, nickname/credentials per server) so you can store several and pick one to join. This lives entirely in the client.
  • On first connect the client shows the server's fingerprint and pins it (TOFU). No accounts or certs needed to try it; if the operator disabled guests, the client prompts for the username/password an admin gave you.

That's the entire flow: run the server, share host:port + fingerprint, friends save it and connect.

3a. Provisioning accounts (admin)

Accounts are admin-provisioned — there is no self-serve registration. Two equivalent ways, both writing the same SQLite store:

# CLI against the server's data dir or a running server
voicecat-admin account add   <username>          # prompts for / generates a password
voicecat-admin account reset <username>
voicecat-admin account del   <username>
voicecat-admin account list

…or from the in-app admin interface (a user with the admin permission), which sends the privileged CreateAccount/ResetPassword/DeleteAccount control messages over TLS (protocol.md §3). Guests need no provisioning; they just pick a nickname (if guests are enabled).

4. Why it stays this easy (design constraints that protect the goal)

  • No external services. No separate database, no Redis, no TURN/STUN server, no reverse proxy required. SQLite is embedded; media is plain UDP.
  • No certificate chore. Self-signed + Ed25519 TOFU means encryption needs zero operator action. A domain owner can drop in a Let's Encrypt cert, but never has to.
  • One port pair. TCP+UDP on the same number; one firewall/port-forward rule.
  • Static linking + permissive licenses. The binary has no install-time dependencies and can be redistributed (including closed-source) without copyleft obligations.
  • Agent-friendly. The run command is a single line with no interactive prompts, the server logs its fingerprint and listen address in machine-readable form, and --help / --print-config expose everything an automation needs. Health endpoint for liveness checks.

5. Operational niceties (planned, not blocking v1)

  • voicecat-server --print-fingerprint and a /healthz TCP check.
  • Graceful reload of server.toml on SIGHUP.
  • voicecat-admin (see §3a) also handles bans and channel admin, talking to the same SQLite file or a running server.
  • Prebuilt images for linux/amd64 + linux/arm64 (Raspberry Pi / cheap VPS friendly).