Files
captions-deploy/docs/CONFIGURATION.md

7.3 KiB

Configuration guide

This deployment exposes a single public origin. Nginx serves the room UI and proxies the speech-to-text API, capture page and private relay internally.

1. docker-compose.yml

Four application services:

Service Loopback port Notes
caption-local 8765 Whisper STT; state in the caption-local-keys volume at /data/rooms.db
captionninja-relay 8787 Reads secrets/relay.json
captionninja-web 8080 Public UI and same-origin proxy; bind with CAPTION_WEB_BIND
wiki 8081 Documentation wiki

All application ports bind to 127.0.0.1. Do not widen them; public traffic must go through Traefik and the nginx proxy.

caption-local reads optional secrets/caption-local.env. Without a configured master and relay address, room creation returns 404.

2. docker-compose.prod.yml

Publishes only UI_HOST and WIKI_HOST through Traefik. caption-local and the relay have no Traefik routers. The UI router reaches nginx; nginx forwards:

  • /api/* and /api/admin/* to caption-local:8765
  • /health, /relay-config, /transcribe, /streams/*, /v1/* to caption-local:8765
  • /static/*, /capture-local.html and /admin.html to caption-local:8765
  • /ws to captionninja-relay:8787
  • /room/<id>, /room/<id>/edit and /watch/<id> to the static room, editor and audience pages

Requests to caption-local carry Host: 127.0.0.1:8765 because TrustedHostMiddleware only accepts localhost names. There is no longer a public whisper-host middleware or a separate capture/relay hostname.

3. traefik.yml

Static Traefik config: HTTP/HTTPS entrypoints, ACME resolver, Docker provider with exposedByDefault: false. The ACME email is passed on the command line from .env.production.

4. .env.production

Tracked and non-secret:

LETSENCRYPT_EMAIL=you@example.com
UI_HOST=ui.example.org
WIKI_HOST=wiki.example.org

CAPTURE_HOST and RELAY_HOST are no longer used.

5. Secrets

secrets/relay.json

Created by captionninja/relay/init-config.cjs:

{
  "origins": ["https://ui.example.org"],
  "master": "<base64url, at least 32 bytes>"
}
  • origins must list every exact page origin that opens a WebSocket. With the single-origin model this is normally just the UI origin. Include http://localhost:8080 for local testing.
  • The last WebSocket header usually carries the public origin, not a capture subdomain.
  • Recreate the relay after editing the file.

The container runs as uid/gid 1000, so the bind-mounted file must be readable by that user:

chown 1000:1000 secrets/relay.json
chmod 600 secrets/relay.json

secrets/caption-local.env

CAPTION_ROOMS_DB=/data/rooms.db
CAPTION_RELAY_MASTER=<same master as relay.json>
CAPTION_ADMIN_KEY=<at least 24 ASCII characters, optional>
CAPTION_ROOM_CREATION=open
RELAY_ADDRESS=wss://ui.example.org/ws
RELAY_SITE=https://ui.example.org/
CAPTION_COOKIE_SECURE=1
CAPTION_ALLOWED_ORIGINS=https://ui.example.org
Variable Meaning
CAPTION_ROOMS_DB SQLite room registry path; must be on the persisted /data volume
CAPTION_RELAY_MASTER Shared base64url master; enables room creation and config API
RELAY_ADDRESS Relay WebSocket address handed to browsers
RELAY_SITE Public UI base used to build admin/audience links
CAPTION_ADMIN_KEY Protects /admin.html and /api/admin/rooms
CAPTION_ROOM_CREATION open (default) or off
CAPTION_COOKIE_SECURE auto by default, or force 1/0
CAPTION_TOKEN_TTL Relay token lifetime in seconds; default 3600
CAPTION_SESSION_TTL Room cookie lifetime in seconds; default 30 days
CAPTION_MAX_ROOMS Active room cap; default 400
CAPTION_CREATE_LIMIT Room creations per IP per window; default 5
CAPTION_CREATE_WINDOW Rate-limit window in seconds; default 3600
CAPTION_ALLOWED_ORIGINS Extra exact origins for /transcribe; normally empty
CAPTION_ALLOW_INSECURE_LAN Permit ws:///http:// relay URLs for private LAN offline use
CAPTION_LOG_REQUESTS Metadata-only request logs; no tokens, IDs or captions

The env file is optional (required: false). Do not put CAPTION_API_KEY or relay tokens here; the shared-token API model has been replaced by room sessions.

secrets/traefik.htpasswd

Only needed with docker-compose.auth.yml. It adds a Basic Auth perimeter to the UI and wiki. It is not the room access model.

6. Web UI image

web/Dockerfile builds the static UI from captionninja/ and serves it with nginx.

Variable Purpose
RELAY_URL Relay URL written into /site-config.js for legacy pages
CAPTURE_URL Legacy capture-page URL for compatibility links
CAPTION_UPSTREAM Internal Whisper upstream; default caption-local:8765
RELAY_UPSTREAM Internal relay upstream; default captionninja-relay:8787
TRANSLATION_MODELS Bergamot model download policy

No secrets or relay tokens are injected into static pages.

7. Room API

All routes are same-origin; room routes use the caption_room cookie.

Method Route Auth
POST /api/rooms public, rate-limited, must be enabled
POST /api/rooms/<id>/session admin secret in body or Bearer
GET /api/me room cookie
GET/PATCH /api/rooms/<id> room cookie
POST /api/rooms/<id>/rotate room cookie; returns a new admin link once
DELETE /api/rooms/<id> room cookie
GET /api/rooms/<id>/config room cookie
GET /api/public/rooms/<id> public, rate-limited; output read token only
GET /api/admin/rooms admin key
POST /api/admin/rooms/<id>/rotate admin key
DELETE /api/admin/rooms/<id> admin key

Public pages (/, /watch/<id>, /room/<id>, /capture-local.html, /admin.html, /health, static assets) do not require a room cookie. They only reveal public data; room APIs still enforce auth.

8. Offline LAN profile

Use .env.lan.example as a template. Set CAPTION_WEB_BIND to the host LAN IP, replace the relay host placeholders, and add the LAN origin to secrets/relay.json. Set CAPTION_ALLOW_INSECURE_LAN=1 only for an isolated private network. Run once with CAPTION_OFFLINE=0 to cache the model, then set CAPTION_OFFLINE=1 for the offline event.

cp .env.lan.example .env.lan
docker compose --env-file .env.lan -f docker-compose.yml up -d --build

Only the web UI is exposed. The Whisper service and relay remain loopback-only. Use http://localhost:8080 for microphone capture on the host; use the LAN URL for audience and editor access from other devices.

9. Running and verification

docker compose up --build -d
docker compose --env-file .env.production \
  -f docker-compose.yml -f docker-compose.prod.yml config -q

docker compose --env-file .env.production \
  -f docker-compose.yml -f docker-compose.prod.yml up -d
docker compose --env-file .env.production \
  -f docker-compose.yml -f docker-compose.prod.yml ps

Expected flow: open UI_HOST, create a room, hold the admin link, caption, edit and share the audience link. Verify the relay accepts browser connections from the UI origin; a wrong origins entry or mismatched master shows as a relay denial in the browser.