9.1 KiB
Deployment & Self-Hosting
Managed server deployment checkpoint
The .NET server preserves protocol v2 and the native schema/credentials. Publish the Windows self-contained executable (no installed .NET runtime required):
./dotnet/publish-server.ps1
./dotnet/artifacts/server/win-x64/VoiceCat.Server.exe --help
./dotnet/artifacts/server/win-x64/VoiceCat.Server.exe account add Operator --admin --data-dir ./voicecat-data
./dotnet/artifacts/server/win-x64/VoiceCat.Server.exe --data-dir ./voicecat-data --allow-guests false
Account add/reset use a hidden password prompt, redirected standard input, or
VOICECAT_ADMIN_PASSWORD. Passwords are never accepted as command arguments or logged.
Account delete/list work against the same database, including while the server runs.
Provisioning grants administrator access only through the local command's --admin;
in-band account creation remains non-admin. Restrict access to the data directory.
Defaults are 0.0.0.0:8384 TCP+UDP, guest access enabled, 64 connections, 15-second TLS
handshakes, 45-second idle expiry and 15-second sweeps. Override with flags or environment:
| Flag | Environment variable |
|---|---|
--data-dir |
VOICECAT_DATA_DIR |
--bind |
VOICECAT_BIND_ADDRESS |
--port |
VOICECAT_BIND_PORT |
--name |
VOICECAT_SERVER_NAME |
--allow-guests |
VOICECAT_ALLOW_GUESTS |
--max-connections |
VOICECAT_MAX_CONNECTIONS |
--handshake-seconds |
VOICECAT_HANDSHAKE_TIMEOUT_SECONDS |
--idle-seconds |
VOICECAT_IDLE_TIMEOUT_SECONDS |
--reaper-seconds |
VOICECAT_REAPER_INTERVAL_SECONDS |
--auth-burst |
VOICECAT_AUTH_BURST |
--auth-refill-seconds |
VOICECAT_AUTH_REFILL_SECONDS |
Command arguments override environment values. --print-config validates and prints JSON
without creating files; --print-fingerprint prints the persisted leaf-certificate SHA-256
pin. Startup emits one JSON ready event with both fingerprints and actual TCP/UDP ports.
Bind accepts IP literals; IPv6 listeners are IPv6-only. Open/forward both protocols.
An exclusive data-directory instance lock prevents duplicate managed server processes.
Ctrl+C and Unix SIGINT/SIGTERM stop all transport tasks; shutdown has a ten-second deadline.
Fatal listener/media/reaper failure exits the host rather than leaving a broken listener.
Password authentication is limited before Argon2 by source address and username across connections: burst 5, refill one attempt per ten seconds. Starting at three failed attempts, backoff grows from one to thirty seconds. Success clears backoff but does not restore tokens. State is bounded to 4096 keys; idle entries retire after ten minutes when full. Throttle and credential failures share the generic auth error. Limits are process-local.
The publish script uses separate runtime lock files so deployment and development restore graphs remain reproducible. The checked deployment target is currently Windows x64; Linux container publishing, service packaging, TOML/reload support and long-running operational validation remain before broad production rollout. The native deployment paths and planned operational features below remain available as the migration oracle.
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; 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-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).