Establish the design spec in docs/ before implementation: - README: overview, locked decisions, principles, glossary - architecture: shared C++ core + C ABI, native UIs (Swift/C#), threading model, server design (SFU relay) - protocol: TCP/TLS control plane, protobuf Envelope + message catalog, connection lifecycle, extensibility rules - voice: UDP media frame format, per-channel Opus config, multi-stream model, two-sided noise reduction, VAD/PTT, jitter buffer, iOS ReplayKit screen-audio - security: mandatory encryption (TLS 1.3 + exported-key AEAD), TOFU server identity, admin-provisioned accounts, anti-replay - tech-stack: permissive-only deps (mbedTLS, libsodium, opus, miniaudio, webrtc-apm, ...), build tooling, no GPL/LGPL - deployment: zero-config self-host (Docker / binary / source) - roadmap: M0-M5 milestones, resolved decisions Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.6 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
A. Docker (recommended)
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
cmake --build --preset server-release
./build/voicecat-server
One cmake invocation; vcpkg (manifest mode) resolves the dependency graph reproducibly.
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-configexpose everything an automation needs. Health endpoint for liveness checks.
5. Operational niceties (planned, not blocking v1)
voicecat-server --print-fingerprintand a/healthzTCP check.- Graceful reload of
server.tomlonSIGHUP. 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).