Files
church-translate/docs/PUBLIC-ACCESS.md
T
christianandClaude Opus 5 c9eef0672c Church Translate, as it runs at the campuses
Live sermon translation into 100+ languages, running entirely on one Mac per
campus: audio in, voice detection, Whisper on the GPU, NLLB-200 in its own
process, and a built-in web server the congregation reads from on their
phones. No audio leaves the building.

This is the source only. What used to sit beside it — 14 GB of speech and
translation models, 8 GB of voice bundles, 6 GB of natural voices, the Electron
caches, and the app data holding the operator PIN and every service transcript
— is machine state and downloaded assets, and stays in the NAS backup where it
belongs. The vendored node, cloudflared and Windows ffmpeg binaries are out too
(533 MB, re-fetched for packaging); the browser-side runtime beside them is
kept, so a checkout still serves the congregation page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 15:41:21 -04:00

3.5 KiB

Letting people off-site watch

Optional. Everything works on the building's WiFi without any of this. Read on only if you want people on cell data, or watching from home, to follow along.

Because each campus runs its own copy, this is a per-campus decision — you can publish one campus and not another.


The easy way: Cloudflare Tunnel

Free, needs no port forwarding, no firewall changes, no certificate, and no static IP. It runs on the same Mac as the app.

  1. Install cloudflared

    brew install cloudflared
    

    No Homebrew on that Mac? Download the cloudflared-darwin-arm64.tgz binary from Cloudflare's releases page and drop it in /usr/local/bin.

  2. Log in and create the tunnel

    cloudflared tunnel login
    cloudflared tunnel create translate-main
    
  3. Point a hostname at the app

    cloudflared tunnel route dns translate-main translate.watersmedia.org
    
  4. Run it

    cloudflared tunnel run --url http://127.0.0.1:3100 translate-main
    

    To keep it running after a reboot, sudo cloudflared service install.

  5. Tell the app its public name

    Operator page → Public address → https://translate.watersmedia.org → Save. That is what the QR code encodes, so it must be the public name or the code only works for people already on your WiFi.

WebSockets pass through Cloudflare tunnels with nothing extra to configure, which is the main reason this is the lower-risk option.

For a second campus, repeat with its own name — translate-north, north.translate.watersmedia.org.


The other way: your existing nginx proxy

You already run a reverse proxy at 10.1.1.4 fronting check.watersmedia.org, with split-horizon DNS. It can front this too, though it means the traffic goes through the NAS network to reach the booth Mac.

Add a DNS record for translate.watersmedia.org — public to your WAN address, internal to 10.1.1.4 — and a server block. Copy the one that already serves the checklist app, change the name, and point it at the booth Mac, not the NAS:

server {
    listen 443 ssl http2;
    server_name translate.watersmedia.org;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location / {
        proxy_pass http://10.6.3.222:3100;      # the booth Mac

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;

        # ---- required: captions ride a WebSocket ----
        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection "upgrade";

        # A sermon is long and mostly silent between sentences. Without this the
        # proxy closes the caption socket after 60s of quiet and every phone in
        # the room reconnects at once.
        proxy_read_timeout  3600s;
        proxy_send_timeout  3600s;
    }
}

This has one real drawback the tunnel does not: the booth Mac needs a fixed address on the network, or the proxy breaks the next time DHCP moves it.


Checking it worked

curl -s https://translate.watersmedia.org/healthz

Should return {"ok":true,…}. Then open the page on a phone with WiFi turned off — that is the real test. If the page loads but captions never appear, the WebSocket headers are missing.