Document the LAN-only translate.watersmedia.org setup at North Attleboro

PUBLIC-ACCESS.md now describes what actually runs: a local UniFi DNS
record at Apollo Beach and North Attleboro pointing the name at the
Boba-Fett NAS, a DSM reverse proxy rule from port 80 to the booth Mac at
10.1.7.39:3100 with WebSocket headers, X-Forwarded-Host and a 3600 s
read timeout, and an nginx block that redirects https:// to http://.
The hand-written nginx example is kept for non-DSM proxies, with the
booth Mac address corrected and proxy_buffering off for live audio.

docs/examples/waters-church-na/ holds the runbook, the script run on
Boba-Fett, and the UniFi DNS record script used to create the records.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 13:14:57 -04:00
co-authored by Claude Fable 5.1
parent a06cf35cbb
commit d785ebf979
4 changed files with 427 additions and 15 deletions
+59 -15
View File
@@ -57,16 +57,45 @@ For a second campus, repeat with its own name — `translate-north`,
---
## The other way: your existing nginx proxy
## The other way: a name on your existing reverse 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.
You already run a reverse proxy on the NAS at `10.1.1.4` (Boba-Fett, Synology
DSM) fronting `check.watersmedia.org`. 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:
This is what North Attleboro runs as of 2026-10-15, in the **LAN-only** form:
`http://translate.watersmedia.org` works on the building's WiFi at both the
Apollo Beach and North Attleboro sites and nowhere else. Nothing was opened to
the WAN. The pieces, with the exact scripts and a step-by-step runbook under
[`examples/waters-church-na/`](examples/waters-church-na/README.md):
| Piece | Where | What |
| --- | --- | --- |
| Local DNS | UniFi consoles at AB and NA (Settings → Policy Table → DNS) | A record `translate.watersmedia.org` → `10.1.1.4`, TTL 300, created with `unifi-dns-record.py` and a per-console Network API key |
| Proxy rule | DSM → Control Panel → Login Portal → Advanced → Reverse Proxy | HTTP `translate.watersmedia.org:80` → HTTP `10.1.7.39:3100`, WebSocket headers, `X-Forwarded-Host: $http_host`, read timeout 3600 s |
| HTTPS | `/usr/local/etc/nginx/sites-enabled/translate-redirect.conf` on the NAS | `https://` answers a 301 to `http://`; the name has no certificate, so browsers warn before the redirect |
Both NAS pieces come from `boba-fett-translate-proxy.sh`, run as
`ssh -t boba-fett sudo bash translate-proxy.sh` because DSM's reverse-proxy API
is root-only. Going through DSM rather than a hand-edited nginx file means the
rule shows up in the DSM UI and survives DSM updates. DSM adds `Host`,
`X-Real-IP`, `X-Forwarded-For` and `X-Forwarded-Proto` on its own.
Then: Operator page → **Public address** → `http://translate.watersmedia.org`
(no trailing slash), so the QR code and poster use the name instead of the
IP and port.
To let people off-site use the same name later: add a public DNS record for
`translate.watersmedia.org` pointing at the NA WAN address, get a certificate
in DSM (Control Panel → Security → Certificate → Let's Encrypt), replace the
redirect with an HTTPS rule to the same destination, and switch Public address
to `https://`. The app behaves the same either way, including the `/operator`
404 through a proxy.
### If the proxy is plain nginx instead of DSM
DSM writes most of this for you. On a hand-managed nginx the server block
needs all of it:
```nginx
server {
@@ -77,7 +106,7 @@ server {
ssl_certificate_key /path/to/privkey.pem;
location / {
proxy_pass http://10.6.3.222:3100; # the booth Mac
proxy_pass http://10.1.7.39:3100; # the booth Mac
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
@@ -95,21 +124,36 @@ server {
# the room reconnects at once.
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# Live audio: do not hold HLS segments back.
proxy_buffering off;
}
}
```
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.
Either way the booth Mac needs a fixed address or a DHCP reservation, or the
proxy breaks the next time DHCP moves it.
---
## Checking it worked
```bash
curl -s https://translate.watersmedia.org/healthz
curl -s http://translate.watersmedia.org/healthz # https:// once published
```
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.
Should return `{"ok":true,…}`, the same as `curl -s http://10.1.7.39:3100/healthz`.
A proxy that passes the WebSocket answers `HTTP/1.1 101 Switching Protocols` to:
```bash
curl -si -N -H 'Connection: Upgrade' -H 'Upgrade: websocket' \
-H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
http://translate.watersmedia.org/ws | head -1
```
Then open the page on a phone: for the LAN-only setup on the guest WiFi, for a
published one **with WiFi turned off**. That is the real test. If the page loads
but captions never appear, the WebSocket headers are missing. If phones on
guest WiFi cannot connect at all, look at client isolation on that network
before anything else. `/operator` returning 404 through the proxy is by design;
staff use `http://10.1.7.39:3100/operator` on the LAN.