From 684a487203463b3e4aa2f39cb36df1f2fa07fb59 Mon Sep 17 00:00:00 2001 From: serversdown Date: Fri, 12 Jun 2026 03:20:16 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20changelog=20[Unreleased]=20=E2=80=94=20?= =?UTF-8?q?add=20the=20client=20portal=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the read-only client portal under [Unreleased] alongside the SLM live-monitoring work: per-client scoping + interim auth, live location view with the auto-closing WS stream, locations overview map + rollup, the alerts config→surface→24/7 track, operator sharing tools, the field-instrument design + light/dark toggle, the security posture, and upgrade notes (migration, SECRET_KEY, SLMM alert-engine pairing). Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 908f277..5941174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,62 @@ The two builds must ship **together**. Note the `docker-compose.yml` container --- +### Client portal *(new — read-only client-facing view)* + +A scoped, read-only portal at **`/portal/*`** where a client sees only *their* +locations, live. Built inside Terra-View (no new service), reusing the cached +SLMM feed; every route resolves the client through one swappable +`get_current_client` gate, so the interim magic/open-link auth can be replaced +(M4) without touching routes or templates. Strictly read-only — no device control. + +#### Added + +- **Per-client scoping + interim auth.** New `Client`, `ClientAccessToken`, and a + `Project.client_id` FK. A signed (HMAC) session cookie carries the access-token + id, re-validated against the DB each request (revoke kills live sessions, with + server-side expiry). Entry via a magic link (`/portal/enter/{token}`) or a + dev-only plain link (`/portal/open/{id}`, `PORTAL_OPEN_LINKS`, **default off**). +- **Live location view.** KPI cards (Lp/Leq/Lmax/L1/L10) + chart populate + instantly from cache, then upgrade to a real **~1 Hz WebSocket stream** scoped to + the client's unit (a scrubbed bridge to the SLMM fan-out feed). The stream + **auto-closes when the tab is hidden** (Page Visibility) and after a 15-min idle + cap, so an abandoned tab can't pin the device at 1 Hz / burn cellular. +- **Locations overview.** Live status map (level-colored dots, dark/light CARTO + tiles) + a status rollup (live/offline counts, "loudest now"). Leq is the + headline metric. +- **Alerts (config → surface → 24/7).** Threshold-rule config on the SLM detail + page (proxying SLMM's alert CRUD); breach **history + ack** internally and a + read-only, scrubbed history + current-alarm banner + **"your alert limits"** panel + in the portal; enabling a rule pins that device's monitor on so alerts evaluate + round-the-clock. +- **Operator sharing tools.** A **"View client portal"** preview button and a + **"Copy client link"** modal (mint / list / revoke magic links) on the project + page, plus a `backend/portal_admin.py` CLI. +- **Field-instrument design.** Distinctive themed portal — Hanken Grotesk UI + + IBM Plex Mono readouts, panel system, pulsing live dot, staggered reveal — with a + **light/dark toggle** (light default, persisted, no-flash). + +#### Security + +- All scoping enforced server-side (404-not-403, no existence leak); client + endpoints return **scrubbed** projections (no device-health/internal ids); WS + frames whitelisted; operator-set strings HTML-escaped before injection (XSS). + Pre-merge code review hardened cookie expiry, open-links default, and the slug + collision. Remaining hardening (reverse proxy, TLS, `SECRET_KEY`, M4 auth) is + tracked in `docs/CLIENT_PORTAL.md` → "Security hardening backlog". + +#### Upgrade Notes + +- **Migration:** `docker compose exec web-app python3 backend/migrate_add_client_portal.py` + (adds `projects.client_id`; the `clients` / `client_access_tokens` tables + auto-create). +- Set a real **`SECRET_KEY`** in any internet-facing env (signs session cookies), + and keep **`PORTAL_OPEN_LINKS=false`** there. +- Portal alerts depend on the **SLMM `dev`** alert engine (rules/events/evaluator + + cooldown + keepalive coupling) — same build pairing as above. + +--- + ## [0.13.3] - 2026-06-05 Calibration sync from SFM events. Closes the manual data-entry loop on calibration dates — Terra-View now pulls `device.calibration_date` from each seismograph's most recent event sidecar once a day and updates `RosterUnit.last_calibrated` when the device reports something fresher than what's stored. Manual edits still win when they're newer than the latest event; a fresh event arriving later supersedes the manual edit. Adds a "Sync now" button under Settings → Advanced → Calibration Defaults for on-demand runs, and a `docs/ROADMAP.md` to track in-flight + deferred work.