diff --git a/docs/SYNOLOGY_DEPLOYMENT.md b/docs/SYNOLOGY_DEPLOYMENT.md new file mode 100644 index 0000000..0434647 --- /dev/null +++ b/docs/SYNOLOGY_DEPLOYMENT.md @@ -0,0 +1,436 @@ +# Synology NAS Deployment Guide + +This guide covers migrating the terra-view stack from a generic Linux host +(currently the home server at `10.0.0.44`) to an always-on Synology NAS in +the office, including data migration and the minimal external-access +networking layer. + +## Table of Contents + +1. [Architecture overview](#architecture-overview) +2. [Pre-requisites](#pre-requisites) +3. [Phase 1 — Pre-stage on the NAS (no downtime)](#phase-1--pre-stage-on-the-nas-no-downtime) +4. [Phase 2 — Data migration (~10 min window)](#phase-2--data-migration-10-min-window) +5. [Phase 3 — Repoint the watcher (download2-PC)](#phase-3--repoint-the-watcher-download2-pc) +6. [Phase 4 — External access for remote operators](#phase-4--external-access-for-remote-operators) +7. [Phase 5 — Decommission home server](#phase-5--decommission-home-server) +8. [Verification checklist](#verification-checklist) +9. [Rollback plan](#rollback-plan) +10. [Gotchas](#gotchas) + +--- + +## Architecture overview + +The terra-view stack is three containers: + +| Service | Port | What writes to it | Where it lives | +|---------------|-------|-----------------------------|----------------| +| terra-view | 8001 | Operators (UI), watchers (heartbeat) | Synology NAS | +| SFM | 8200 | Watchers (Blastware ACH forwards) | Synology NAS | +| SLMM | 8100 | terra-view (proxied), SLMs on LAN | Synology NAS | + +Everything that **writes** to the stack lives inside the office LAN: + +- **download2-PC** is the series3-watcher host. It has a static office IP and + POSTs to terra-view's heartbeat endpoint plus SFM's Blastware import + endpoint. Both flows are LAN-internal. +- **Sound level meters (NL-43)** sit on the office LAN; SLMM reaches them + via `network_mode: host`. + +The **only** thing that needs to cross the office firewall is operator UI +access from outside the office (laptops, phones, working from home). That +makes the external networking layer trivial — see Phase 4. + +--- + +## Pre-requisites + +On the Synology side: + +- **DSM 7.2+** with **Container Manager** installed (Package Center). + Older "Docker" package works too — same engine, different menu names. +- **x86_64 model** (Plus / Value / XS series). ARM j-series will build but + expect a slower first build. +- **Static LAN IP** reserved for the NAS in the office router's DHCP table. + Devices on the LAN must have a stable target. +- **SSH enabled** — Control Panel → Terminal & SNMP → Enable SSH service. +- **Shared folder** for the stack — e.g. `/volume1/docker/`. + +On the home server side: + +- Working terra-view / SFM / SLMM stack you want to migrate. +- `rsync` available (it almost certainly is). + +You will also need: + +- An admin account on the Synology with sudo privileges. +- Network access between the home server and the NAS during the migration + window (or USB-drive shuttle if not). + +--- + +## Phase 1 — Pre-stage on the NAS (no downtime) + +Goal: get the NAS booting an empty stack so you can validate the build and +networking *before* touching any production data. + +### 1.1 Clone the repos + +SSH to the NAS as admin: + +```bash +sudo mkdir -p /volume1/docker +cd /volume1/docker +sudo git clone terra-view +sudo git clone slmm +sudo git clone seismo-relay +cd terra-view +sudo git checkout main # or whichever branch you ship from +``` + +### 1.2 Build images + +```bash +cd /volume1/docker/terra-view +sudo docker compose build +``` + +First build takes 5–15 min depending on model. + +### 1.3 Boot the empty stack + +```bash +sudo docker compose up -d +``` + +Hit `http://:1001` (dev profile) or `:8001` (prod profile) from +another office machine. You should see an empty fleet roster. If that +works, the NAS can run the stack — proven before any production data is +at risk. + +### 1.4 Stop the NAS stack again + +```bash +sudo docker compose stop +``` + +We're ready for the data migration. + +--- + +## Phase 2 — Data migration (~10 min window) + +The terra-view stack is stateful in three places. All three must be moved +together for consistency. + +| Service | Data location (home server) | +|------------|----------------------------------------------| +| terra-view | `/home/serversdown/terra-view/data/` | +| SLMM | `/home/serversdown/slmm/data/` | +| SFM | `/home/serversdown/seismo-relay/data/` | + +### 2.1 Stop writes on both sides + +On the NAS: + +```bash +cd /volume1/docker/terra-view +sudo docker compose stop +``` + +On the home server: + +```bash +cd /home/serversdown/terra-view +docker compose stop terra-view slmm sfm +``` + +### 2.2 rsync the data dirs + +From the home server (or anywhere with SSH access to both): + +```bash +rsync -avh /home/serversdown/terra-view/data/ admin@:/volume1/docker/terra-view/data/ +rsync -avh /home/serversdown/slmm/data/ admin@:/volume1/docker/slmm/data/ +rsync -avh /home/serversdown/seismo-relay/data/ admin@:/volume1/docker/seismo-relay/data/ +``` + +### 2.3 Fix ownership on the NAS + +Synology admin is usually UID `1026`, GID `100`. Inside containers running +as root, this doesn't matter — but if you've configured `user:` in any +compose file it will. Safe default: + +```bash +ssh admin@ "sudo chown -R 1026:100 \ + /volume1/docker/terra-view/data \ + /volume1/docker/slmm/data \ + /volume1/docker/seismo-relay/data" +``` + +### 2.4 Run any pending migrations + +Some earlier feature work added migration scripts that need to run once +per database. After the rsync, before starting the stack, check what's +pending: + +```bash +ssh admin@ +cd /volume1/docker/terra-view +ls backend/migrate_*.py +``` + +Run each one inside the container (after starting it temporarily) or apply +them on the host with the same Python environment. Idempotent migrations +re-run safely. + +### 2.5 Start the NAS stack + +```bash +ssh admin@ \ + "cd /volume1/docker/terra-view && sudo docker compose up -d" +``` + +### 2.6 Spot-check + +- Dashboard loads with real units +- `/sfm` page lists historical events +- A photo loads on a unit detail page +- SFM/HB badge mix on the active table matches what you saw on the home + server + +If anything's off, see [Rollback plan](#rollback-plan). + +--- + +## Phase 3 — Repoint the watcher (download2-PC) + +The download2-PC is the one client we have to reconfigure. It currently +POSTs to the home server. Two endpoints to change: + +1. **terra-view heartbeat URL** — + `http://:8001/api/series3/heartbeat` + → `http://:8001/api/series3/heartbeat` + +2. **SFM Blastware import URL** — + `http://:8200/db/import/blastware_file` + → `http://:8200/db/import/blastware_file` + + Or, if you want to keep SFM container-internal and not publish 8200 on + the LAN at all, point it through terra-view's existing SFM proxy: + → `http://:8001/api/sfm/db/import/blastware_file` + +Update the config, restart the watcher service, and confirm the next +heartbeat lands in the NAS DB (check the Recent Call-Ins card on the +dashboard). + +> **Tip:** keep the home server running in parallel for 1–2 days. If you +> forget to repoint something, it'll still flow into the old DB and you +> can resync. + +--- + +## Phase 4 — External access for remote operators + +Only the terra-view UI needs to be reachable from outside the office. Two +clean options — pick one. + +### Option A — Tailscale (recommended for small teams) + +Zero port forwards, zero certs, zero public DNS, zero reverse proxy. + +1. Install Tailscale from Synology Package Center, sign in. +2. Install Tailscale on each operator's laptop/phone, sign in to the same + tailnet. +3. Operators access `http://:8001` from anywhere. + +That's the whole setup. The office network has no external exposure at +all. + +### Option B — Reverse proxy with Let's Encrypt + +If you want a `https://terraview.yourdomain.com` URL that any browser can +reach: + +#### B.1 Port forward on the office router + +``` +WAN 443 → :443 +WAN 80 → :80 (only needed for Let's Encrypt HTTP-01; + skip if you use DNS-01 challenge) +``` + +Do **not** forward 1001, 8001, 8100, or 8200. + +#### B.2 Public DNS + +- Free: Synology DDNS (Control Panel → External Access → DDNS) — gives + you `something.synology.me`. +- Better: your own domain with an A record → office WAN IP, or a CNAME → + Synology DDNS hostname (handles dynamic IPs automatically). + +#### B.3 Let's Encrypt certificate + +Control Panel → Security → Certificate → Add → "Get a certificate from +Let's Encrypt." DSM handles renewal. + +#### B.4 Synology reverse proxy + +Control Panel → Login Portal → Advanced → Reverse Proxy → Create: + +``` +Source: Hostname terraview.yourdomain.com + Protocol HTTPS + Port 443 +Destination: Hostname localhost + Protocol HTTP + Port 8001 +``` + +Under "Custom Header", add: + +| Header | Value | +|---------------------|------------------------------------| +| `X-Forwarded-For` | `$proxy_add_x_forwarded_for` | +| `X-Forwarded-Proto` | `$scheme` | +| `Host` | `$host` | + +Tick the WebSocket support checkbox. + +#### B.5 DSM firewall + +Control Panel → Security → Firewall → enable: + +- 443/TCP from `Anywhere` — allow +- 80/TCP from `Anywhere` — allow (cert renewal only) +- Everything else from WAN — deny +- All from LAN — allow + +Optional: geo-block to your country if your operators are domestic only. +Big reduction in scanning noise. + +--- + +## Phase 5 — Decommission home server + +After 1–2 weeks of stable NAS operation: + +1. Take a final `docker compose down` on the home server. +2. Archive `/home/serversdown/{terra-view,slmm,seismo-relay}/data/` to a + backup volume. +3. Free the home server hardware. + +--- + +## Verification checklist + +After Phase 2 (data migration): + +- [ ] `http://:8001/` loads dashboard with real units +- [ ] Recent Alerts, Call-Ins (2 cols), Fleet Summary across the top +- [ ] SFM/HB badge mix on the active table looks sane +- [ ] `/sfm` page lists historical events (the same count as before) +- [ ] A unit detail page loads with photos rendering +- [ ] `/api/recent-event-callins` returns 200 with real data +- [ ] `/api/status-snapshot` returns 200, `sfm_reachable: true` + +After Phase 3 (watcher cutover): + +- [ ] Next heartbeat from download2-PC lands in NAS DB +- [ ] A new event arrives in `/sfm` page on the NAS within the next + Blastware ACH cycle +- [ ] No errors in `docker logs terra-view-terra-view-1` + +After Phase 4 (external access): + +- [ ] (Option A) Operator laptop on tailnet can reach + `http://:8001` +- [ ] (Option B) `https://terraview.yourdomain.com` resolves, cert is + valid, dashboard loads +- [ ] (Option B) Office DSM admin (5001) is **not** reachable from outside + +--- + +## Rollback plan + +The home server stays alive in parallel through Phases 2–3 as a safety +net. If anything goes wrong on the NAS: + +1. On the home server: + ```bash + cd /home/serversdown/terra-view + docker compose up -d + ``` +2. Point download2-PC back at the home server IP. +3. NAS data isn't lost — it's just sitting idle. Investigate, fix, retry. + +The "irreversible" point is when you decommission the home server in +Phase 5. Until then, you can always fall back. + +--- + +## Gotchas + +1. **Synology UID/GID quirks.** Synology admin is usually `1026:100`. + Containers running as root inside don't care, but if your compose + files set `user:`, mismatched UIDs cause SQLite "readonly database" + errors. Easiest fix: omit `user:` and let containers run as root. + +2. **`network_mode: host` for SLMM.** Required for LAN-direct comms with + sound level meters. On Synology this binds to the NAS's interface — + confirm nothing else on the NAS uses ports 8100 or 21 (FTP). + +3. **Auto-start on boot.** Container Manager → Project → Settings → + enable "Auto-restart". Otherwise a DSM update or NAS reboot drops the + stack. + +4. **`restart: unless-stopped` in compose.** Verify every service has it. + DSM occasionally restarts Docker during DSM updates — this flag + ensures everything comes back. + +5. **Hyper Backup.** Schedule a daily snapshot of + `/volume1/docker/terra-view/data/` to a USB drive or off-site. SQLite + + small photo dir = trivially small backups. The DB-Management UI's + built-in snapshots are an additional layer but not a replacement. + +6. **NAT loopback (Option B only).** If your office router doesn't + support hairpinning, machines INSIDE the office can't reach the NAS + by its public hostname — they have to use the LAN IP. Most modern + routers handle this; some ISP-provided ones don't. Test from a laptop + on the office Wi-Fi. + +7. **Let's Encrypt rate limits (Option B only).** 5 issuances per domain + per week. Don't fat-finger DNS or you'll be locked out. Test with the + staging endpoint first if unsure. + +8. **`host.docker.internal` resolution.** terra-view's + `SFM_BASE_URL=http://host.docker.internal:8200` relies on Docker's + internal DNS. Works on DSM 7.2+ in bridge mode. If you see "name not + resolved" errors, fall back to explicit container names with a custom + network in compose. + +9. **SFM stale rows.** The SFM SQLite has a few rows in `monitor_log` + and `ach_sessions` from earlier Python-ACH experiments. Harmless + to bring over — invisible to terra-view's UI under the + watcher-forward pipeline. + +--- + +## Suggested timeline + +For a low-risk migration: + +- **Week 1**: Phase 1. Get the NAS booting an empty stack. No production + touch. +- **Week 2, day 1**: Phase 2. Migrate data. 10-min window. Keep home + server alive in parallel. +- **Week 2, day 1**: Phase 3. Repoint download2-PC. Watch heartbeats + land on the NAS for the rest of the day. +- **Week 3**: Phase 4. Add Tailscale or reverse-proxy access for remote + operators. +- **Week 4–5**: Monitor. Confirm everything's stable. Then Phase 5 + (decommission home server). + +Splitting "make it work on LAN" from "expose it remotely" means you +debug one thing at a time.