CAPTION.Ninja + Caption Local

Self-hosted live captioning: CAPTION.Ninja's capture/editor/overlay pages and private caption relay, plus Caption Local Whisper speech-to-text. Nothing is sent to caption.ninja, Google or any cloud STT provider.

Services

Service Hostname What
app $DOMAIN nginx serving the caption.ninja pages
relay wss://$DOMAIN/relay private caption relay (Node, ws), rooms source and output
stt $STT_DOMAIN Caption Local (faster-whisper, CPU), also serves its own capture page

Captions flow: capture (microphone → STT) publishes to room source → editor reviews and forwards to room output → overlay (OBS browser source / audience).

Basic usage

  1. Set up Docker Swarm, abra and coop-cloud/traefik
  2. Make the images available on the server (see Images)
  3. abra app new caption-ninja --secrets
  4. abra app config YOURAPPDOMAIN: set DOMAIN and STT_DOMAIN (both need DNS). The STT host needs RAM for the model: about 1.5 GB for small. Set CAPTION_THREADS to at most your core count.
  5. abra app deploy YOURAPPDOMAIN. On first start stt downloads the Whisper model into the models volume, so it can take several minutes to turn healthy.

Using it

Read the tokens (keep them private; read tokens are for viewers, write tokens publish):

abra app run YOURAPPDOMAIN relay -- sh -c 'for s in src_read src_write out_read out_write; do echo "$s=$(cat /run/secrets/$s)"; done'
abra app run YOURAPPDOMAIN stt -- cat /run/secrets/stt_api_key
  1. Open https://$STT_DOMAIN/, enter the stt_api_key service token, then pick a microphone.
  2. Under Send captions to caption.ninja → Use a private relay, enter: relay wss://$DOMAIN/relay, caption website https://$DOMAIN/, source room source + src_write token, editor output room output. Enable sharing.
  3. Open the generated editor link and enter src_read and out_write.
  4. In the editor, create the view-only OBS/overlay link with out_read.

Alternatively use https://$DOMAIN/capture-local.html with service address https://$STT_DOMAIN.

Note: the other caption.ninja pages (index.html, translate.html, …) still default to the public relay wss://api.caption.ninja. Only capture-local.html, editor.html and overlay.html support the private relay.

Images

Upstream publishes no Docker images, so images/build.sh builds them from pinned upstream commits. It needs Docker, git and no registry credentials:

scp -r images SERVER:caption-ninja-build/
ssh SERVER 'bash caption-ninja-build/images/build.sh'

The images are tagged with their registry names, so a single-node swarm can deploy straight from the local tags. To publish them to the registry from a machine that has docker login git.autonomic.zone:

for img in $(ssh SERVER "docker images --format '{{.Repository}}:{{.Tag}}' | grep caption-ninja-"); do
  ssh SERVER docker save "$img" | docker load && docker push "$img"
done

To upgrade, bump the commits in images/build.sh and the tags in compose.yml.

Security notes

  • Caption Local only accepts Host: localhost (upstream expects an SSH tunnel), so Traefik rewrites the Host header for $STT_DOMAIN; stt is still only reachable through Traefik, and every API call needs the token.
  • The STT token is a single shared token, not per-user auth. Anyone with it can use your CPU for transcription. Rotate it with abra app secret + redeploy.
  • Relay origins are https://$DOMAIN and https://$STT_DOMAIN; add more exact origins with RELAY_EXTRA_ORIGINS (e.g. https://steveseguin.github.io).
  • To revoke relay tokens, generate new secret versions and redeploy (restart disconnects all clients and clears the 2-minute replay buffer).
S
Description
No description provided
Readme
32 KiB
Languages
Shell 93.5%
Dockerfile 6.5%