Skip to content

Configuration

h8d13 edited this page Oct 7, 2026 · 14 revisions

How config resolves

Two separate mechanisms, fully disjoint:

  • Env vars (table below): read directly at startup. They own all deploy/infra knobs (server, webRtc, limits). None have a config.ini counterpart.
  • config.ini (next to docker-compose.yaml, mounted read-only): rate limiters and login lockout only. The server reads it and never writes it. Missing keys use the defaults from code, so new limiters need no migration.

No key lives in both. Set ports, debug, WebRTC and the user cap via env; tune the limiters in config.ini.


Operator knobs

Variable Default Range / values What it does
CAESAR_IMAGE_TAG latest latest, x.y.z, edge, or a local tag Compose only: which ghcr.io/h8d13/caesar image the caesar service runs. Pin a release with x.y.z, follow master with edge, or run your own build (see Home).
CAESAR_SITE localhost single hostname (:port for prod-dev only) Public host the instance answers on. Read by both containers: Caddy site address, and the server's WebAuthn RP ID + expected origin. Single host only, the server throws at boot on a space/comma value (see Caddy vs app).
CAESAR_PORT 4991 positive integer Internal HTTP port the server listens on (behind Caddy). docker-compose.yaml feeds the same value to Caddy's --to caesar:<port>, so both sides move together.
CAESAR_MAX_REQUEST_BODY_BYTES 65536 (64 KiB) positive integer (bytes) Hard cap on buffered request bodies (/login, /login-2fa). Caps memory an unauthenticated peer can make the server hold before auth or rate limiting runs. Uploads have their own path and limits.
CAESAR_DEBUG false in prod, true in dev boolean Verbose server logs.
CAESAR_MAX_USERS 0 (unlimited) non-negative integer Cap on active (non-deleted) registered users. Bootstrap (first signup) always bypasses. New invites refused at cap; signup refused at cap. UI shows X / Y users on the Invites screen when set.
CAESAR_WEBRTC_WORKERS 3 in docker-compose.yaml (1 bare server) non-negative integer Mediasoup worker processes. Each worker binds CAESAR_WEBRTC_PORT + i. Must fit the published port range in docker-compose.yaml (40000-40002). Primary CPU/concurrency lever.
CAESAR_WEBRTC_PORT 40000 positive integer Base port for mediasoup workers. Worker N binds base+N.
CAESAR_WEBRTC_ANNOUNCED_ADDRESS empty (auto) IP literal LAN/public IP advertised in ICE candidates. Set only when clients can't reach the host via its default address (e.g. testing from another LAN device against prod-dev).
CAESAR_WEBRTC_MAX_BITRATE 30000000 (30 Mbps) positive integer (bps) Per-user transport cap (sum of inbound + outbound across that user's mic/cam/screenshare). Applied via setMaxIncomingBitrate / setMaxOutgoingBitrate.
CAESAR_WEBRTC_LOG_LEVEL warn in prod, debug in dev debug, warn, error, none Mediasoup worker log verbosity.
CAESAR_WEBAUTHN_RPNAME Caesar string 2FA hardware keys. Display name shown in browser 2FA prompts.
CAESAR_TRUSTED_PROXY_HOPS 1 non-negative integer Reverse proxies in front of the app. Client IP (for rate limiting + audit) is the Nth X-Forwarded-For from the right. 1 matches the bundled Caddy; 0 = exposed directly (trust socket peer only).
CAESAR_TRUSTED_CLIENT_IP_HEADER empty header name Single-value real-IP header to trust for CDN setups (e.g. cf-connecting-ip behind Cloudflare). Overrides CAESAR_TRUSTED_PROXY_HOPS. Empty = don't trust such headers (spoofable).

Internal / build-time

Avoid touching, unless you know what you doing

Variable Set by Purpose
CAESAR_ENV Dockerfile.prod (= production) Marks runtime as production. Dev path leaves it unset.
CAESAR_BUILD_VERSION Docker --build-arg: CI image (tag or short hash). Unset: git short hash, or dev without git (local image builds) Burned into the bundle at build time. Surfaced in UI.
XDG_CONFIG_HOME Dockerfile.prod (= /home/node/.config) Data path parent: everything lives in $XDG_CONFIG_HOME/caesar, the mount point of the data volume.

Caddy vs app: who reads what

The caddy service has no config file and no environment. Compose substitutes .env / shell values straight into its command line:

command: >-
  caddy reverse-proxy
  --from ${CAESAR_SITE:-localhost}       # site address
  --to caesar:${CAESAR_PORT:-4991}       # upstream

compose.prod-dev.yaml adds --internal-certs (self-signed) with fixed localhost:8443 / caesar:4991. The app only sees what sits in the caesar service's environment: block. With a Custom Caddyfile the site address is whatever you write there, keep it equal to CAESAR_SITE.

CAESAR_SITE reaches both, and means something different on each side:

side what it takes from CAESAR_SITE
Caddy site address: which Host matches, which name ACME requests a cert for, and (if you write host:port) which port to listen on
server RP_ID = hostname with the port stripped; EXPECTED_ORIGIN = scheme + full value. Scheme is http only for bare localhost, https otherwise

Two consequences worth knowing before you edit either file:

  • One host per instance. Caddy happily takes example.com, www.example.com as one site block, and the app would then derive a nonsense RP ID from it: hardware 2FA and passkeys stop verifying while password login keeps working. The server rejects such a value at boot instead. Aliases get their own redir block, see Home.
  • Ports are Caddy's job. The prod caddy service publishes 80/443 only, so CAESAR_SITE=example.com:9000 just makes Caddy listen where nothing is published. localhost:8443 is the one supported ported form (prod-dev).

Nothing else in the server checks request origin: it is same-origin by design (CSP + Cross-Origin-Opener-Policy / -Resource-Policy: same-origin, no CORS allowlist). CAESAR_SITE is not a CORS setting.


Where to set them

  • Prod (single instance): .env next to docker-compose.yaml, start from .env.example. Every variable in it reaches the server (env_file), and compose also uses it for CAESAR_SITE / CAESAR_PORT on the Caddy side. Apply with docker compose up -d.
  • Prod (multi-instance): per-service environment: block in docker-compose.override.yaml (see Multi-instances).
  • Dev (compose.dev.yaml): .env.dev at the repo root (gitignored), same variables.

Rate limiters & login lockout (INI only)

No env overrides. The shipped config.ini lists every key commented out at its default. Uncomment and change what you need, then:

docker compose restart caesar

Sections: [rateLimiters.*] (each limiter has maxRequests + windowMs) and [loginLockout] (maxFailures, windowMs, baseLockMs, maxLockMs). Defaults live in apps/server/src/helpers/ini-config.ts; a test keeps the shipped file in sync with them.

A typo'd key or bad value stops the boot and names it (docker logs caesar):

[caesar] fatal during boot: Error: invalid config.ini:
✖ Unrecognized key: "loginn"
  → at rateLimiters

config.ini must exist when the container is created: a missing bind-mount source gets created as a directory, and the server refuses to boot saying so. Remove that directory, download the file, docker compose up -d.

In dev (compose.dev.yaml): same format, optional, at apps/server/data/config.ini in the checkout.

Discord Alternative per your rules. Fork of Sharkord.

Clone this wiki locally