summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorSt33v <github@f3rr3t.com>2026-07-31 09:57:40 +1000
committerSt33v <github@f3rr3t.com>2026-07-31 09:57:40 +1000
commitcef324224678f6d0cf82531c298524b45b6e3d43 (patch)
tree0400ab145c99156cf2805e39ac0e7baa3c337a9f /doc
parentf926198af74d326218e1d76957877b6ec3038c2f (diff)
Add doc/rtfm.md handbook; fix stale button label and unit description
Commits index.html's "3-day history" button label (deployed earlier this session but not yet committed), drops the now-inaccurate hardcoded day-count from synoptic-morph.service's Description=, and adds doc/rtfm.md as the formation's continuously-maintained handbook per house convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'doc')
-rw-r--r--doc/rtfm.md73
1 files changed, 73 insertions, 0 deletions
diff --git a/doc/rtfm.md b/doc/rtfm.md
new file mode 100644
index 0000000..46f842a
--- /dev/null
+++ b/doc/rtfm.md
@@ -0,0 +1,73 @@
+# bomSynoptic — rtfm
+
+The Fantastic Manual for bomSynoptic. Continuously maintained; folds in decisions once acted on, per the house idea → action → record convention.
+
+## Purpose
+
+Automated fetch/render/publish pipeline for Australian Bureau of Meteorology (BOM) weather imagery, serving two public sites on `cremonde`:
+
+- **pestrel.com** — latest MSLP synoptic chart (`synopticLatest.png`) plus a rolling N-day morph timelapse (`morph.mp4`).
+- **radar.pestrel.com** — looping radar APNGs for Sydney, Brisbane, Canberra, Cairns.
+
+Everything is fetched anonymously from BOM's public FTP (`ftp.bom.gov.au`), per their own anonymous-FTP user guide (`old/Bureau_of_Meteorology_Anonymous_FTP_Service_user_guide.pdf`) — personal/non-commercial use, no API key.
+
+## Architecture
+
+- **`synopticChart.sh`** — fetches the latest `IDY00030` MSLP analysis PDF, renders to a 1920×1080 PNG (ImageMagick `magick -density 300` + Ghostscript), publishes it as `synopticLatest.png`, and archives both the raw PDF and rendered PNG. Deployed to `/opt/synoptic/synopticChart.sh`.
+- **`synopticMorph.sh`** — builds a rolling `WINDOW_DAYS`-day morph timelapse from the archived PNGs. Content-addressed pair cache (`<chartA>__<chartB>/p_NNNN.png`) under `WORK_ROOT` (`/mnt/enclave/synoptic`, deliberately off the cramped root volume — see Current status). Each run fills missing pairs, evicts anything outside the current window, assembles a sequence with linger/dwell holds, encodes via `ffmpeg`, and publishes `morph.mp4`. Triggered by `OnSuccess=` from `synoptic.service` (not its own timer). Deployed to `/opt/synoptic/synopticMorph.sh`.
+- **`radarFetch.sh`** — per-city radar fetch (`RADAR_ID`, e.g. `IDR713` Sydney), pulls dynamic radar layers + static transparency overlays, alpha-composites, publishes the last `FRAME_COUNT` frames as an APNG loop. Deployed to `/opt/radar/radarFetch.sh`, one process serves all four cities via the `RADAR_ID` arg.
+- **`index.html` / `morph.html` / `radar.*.html`** — static frontend, served directly by nginx from `/srv/www/pestrel/` and `/srv/www/radar/`.
+- **`setup.sh`** — one-time root-run installer (packages, directories, web content, systemd units). Idempotent-ish; not generally re-run once a host is live.
+- **`deploy.sh [synoptic|radar]`** — day-to-day `scp` + `systemctl restart` helper for the two fetch scripts. (Restarting requires `sudo` on cremonde — see Bugs.)
+
+## Deployment
+
+| Item | Value |
+|---|---|
+| VPS | `cremonde` (SSH alias, port 40022) |
+| Sites | `pestrel.com`, `radar.pestrel.com` |
+| App install dir | `/opt/synoptic`, `/opt/radar` |
+| Archive / working data | `/var/lib/synoptic`, `/var/lib/radar` |
+| Morph pair-cache | `/mnt/enclave/synoptic` |
+| Published web root | `/srv/www/pestrel/`, `/srv/www/radar/` |
+| Runtime deps | `curl`, `imagemagick`, `ghostscript`, `ffmpeg`, `nginx` |
+
+All app-owned paths (`/opt/synoptic`, `/opt/radar`, `/var/lib/synoptic`, `/var/lib/radar`, `/srv/www/pestrel`, `/srv/www/radar`, `/mnt/enclave/synoptic`) are `st33v`-owned — day-to-day script edits/deploys need no `sudo`. Only `setup.sh` itself (systemd unit installation, package install) needs root.
+
+## Configuration (env var overrides)
+
+- `synopticMorph.sh`: `WINDOW_DAYS` (default `3`), `MORPH_N` (23), `FPS` (12), `LINGER` (8), `DWELL` (36), `FADE_IN`/`FADE_OUT` (1/2s), `GLYPH_FONT` (DejaVu Sans), `GLYPH_SIZE` (84).
+- `radarFetch.sh`: `RADAR_ID` (positional arg, default `IDR713`), `FRAME_COUNT` (6), `FRAME_DELAY` (50cs), `END_PAUSE` (150cs), `TRANSPARENCY_TTL_HOURS` (24).
+
+## Current status (2026-07-31)
+
+**Disk-full incident, diagnosed and resolved (2026-07-30).** Root (`/`, `/dev/root`, 9.7G ext4) hit 100% used / 0 available. Two independent causes, both on root:
+1. `/etc/nginx/logs/{error,access}.log` — 862M of dead debug-level logs from a pre-syslog nginx config, never rotated (current config logs via syslog; these files hadn't been written to in 4+ months). Truncated.
+2. `/var/lib/synoptic/archive/*.png` — the archive PNGs grow unboundedly (every 6-hourly fetch adds one, forever), while the morph timelapse only ever needs the last `WINDOW_DAYS` worth. Fixed two ways: `WINDOW_DAYS` dropped from 30→3 (this session), and PNGs older than the active window were deleted — they're regenerable from the raw PDFs in `archive/raw/` (much smaller, kept indefinitely) via the same `magick -density 300 ... -resize 1920x1080` step `synopticChart.sh` already uses.
+
+Same incident also truncated one archive PNG mid-write (`202607290000.png`), which silently blocked `synoptic-morph.service` (fails closed under `set -euo pipefail`, before it ever reaches its own stale-pair eviction step) until caught and regenerated from the retained raw PDF.
+
+**Progress glyph added (2026-07-30/31).** `morph.mp4` now overlays a ☁ that drifts left→right across the top of the frame (~7% down, outlined for legibility over both ocean and land backgrounds), position mapped linearly to elapsed video time so it visibly pauses during linger/dwell holds.
+
+**Known cruft, not yet cleaned:** ~275 loose frame files (`p04_0012.png`-style naming, no subdirectory) sitting directly in `/mnt/enclave/synoptic/pairs/` — leftovers from the original PoC session (see `doc/synoptic-morph-buildout.md`), predate the current content-addressed pair-directory scheme, untouched by the eviction logic (which only walks `*/` subdirectories). Harmless but unaccounted-for disk use on the enclave volume.
+
+## TODO
+
+- **Atomic archive writes** — `synopticChart.sh` writes the archive PNG directly (`magick ... "${dateTime}.png"` then `cp`), so a disk-full mid-write can truncate it silently (see Current status). Worth hardening to temp-file + atomic `mv`, or at least a post-write `magick identify` sanity check, so a future disk-full event can't quietly corrupt the archive again. Flagged, not yet done.
+- **Radar fetch migration off cremonde** — side-quest, deferred, no urgency (see `doc/migrate-fetch-to-stan.md`): BOM's anonymous FTP is meant for personal/in-country use; cremonde is an overseas VPS. Plan on file: move fetch+composite to `stan` (local AU pi), push finished APNGs to cremonde over WireGuard, leave cremonde as the public TLS-terminating edge. Only worth doing if BOM complains or as a defensive move.
+- **Root-volume headroom monitoring** — nothing currently alerts before `/` fills up again; this session's incident was only caught because a user noticed. No monitoring/alerting exists for any host in scope of this doc.
+- Orphaned pair-cache frame cleanup (see Current status) — low priority, enclave volume has headroom.
+
+## Bugs
+
+- **`deploy.sh` / `systemctl restart` needs interactive `sudo`** — confirmed 2026-07-30: a plain SSH session (no TTY, no sudo) gets `Access denied` restarting `synoptic.service` even though the service's own `ExecStart` runs as `st33v`. Workaround: run the target script directly (`/opt/synoptic/synopticMorph.sh` etc.) rather than through `systemctl restart`, when script + touched paths are all `st33v`-owned (true for both `synoptic-morph` and, incidentally, most ad-hoc fixes). `deploy.sh` itself still shells out through `systemctl restart` and will hit the same wall run non-interactively.
+- **`synoptic.service`'s chart-slot derivation can't backfill a missed slot** — `DeriveTime()` in `synopticChart.sh` always computes "the current wall-clock slot," never "the one that failed." A run that fails (e.g. disk-full) permanently skips that slot's chart; the next successful run (scheduled or manual) just fetches whatever slot is current *then*, not the missed one. Confirmed 2026-07-30 (`202607291800` chart silently skipped, disk-full window). Not a bug in the sense of needing a fix — just a real gap worth knowing about before assuming a manual re-run recovers a specific missed chart.
+- **`synoptic-morph.service`'s systemd unit `Description=` said "30-day"** (`systemd/synoptic-morph.service`) — stale since the `WINDOW_DAYS` 30→3 change this session. Fixed in the repo (dropped the hardcoded day-count); **not yet deployed** — updating `/etc/systemd/system/synoptic-morph.service` + `systemctl daemon-reload` needs interactive `sudo` (same wall as the `deploy.sh` bug above), so it's still stale on cremonde until someone with a `sudo` session pushes it.
+
+## Decisions / history
+
+Session write-ups in `doc/`, roughly chronological:
+- `bom-radar-spec.md`, `bom-radar-rollout.md` — original radar build.
+- `radar-four-cities-and-nav.md` — Canberra/Cairns radars, cross-page nav.
+- `migrate-fetch-to-stan.md` — the deferred cremonde→stan side-quest (see TODO).
+- `synoptic-morph-buildout.md` — original 30-day morph build, incl. the `/tmp` vs `/mnt/enclave` storage-planning confusion from that session (same shape of lesson this session's disk-full incident re-taught at the archive layer).