-
released this
2026-06-23 01:01:23 -04:00 | 0 commits to main since this release[0.16.0] - 2026-06-23
Modular projects & live Overview — the project page becomes a module-aware workspace. Sound and vibration are now first-class modules with independent lifecycles (finish the sound study while vibration keeps running), the internal Overview gains the live-monitoring treatment the client portal already had, and vibration events become browsable in-app. Rounds out a batch of project-page UX work plus deployment-hopper and filter fixes carried since 0.15.0.Added
Per-module status (independent lifecycle). Each project module (Sound / Vibration) now carries its own status — active / on_hold / completed — separate from the parent project. Mark the sound side "Completed" while vibration stays "Active" instead of archiving the whole project. Set from a status control in each module's tab; surfaced as a badge on the header module chips and on the project cards. New PUT /api/projects/{id}/modules/{type}/status; new project_modules.status column.
Live monitoring on the internal project Overview. The internal project page gets the live treatment the client portal already had: a live-monitoring section with per-NRL tiles (current level + status), live/offline counts, a "loudest now" readout, and auto-refresh — reading SLMM's shared cached feed, no extra device hits. Live status chips on the NRL list cards; tiles are clickable through to the NRL detail page. Shown only for projects with live-mode (connected) sound NRLs.
Vibration events, browsable in-app. A project-wide Events sub-tab on the Vibration tab lists SFM events across every vibration location, with a Location filter and sortable columns (timestamp, location, serial, Tran/Vert/Long/PVS/Mic). Each Vibration location card shows its last event. Rows open the shared event-detail modal.
24-Hour session period type. A full-day (day + night) period type for 24/7 jobs, alongside the existing weekday/weekend day/night types; combined reports bucket a 24-Hour session's intervals into Daytime / Evening / Nighttime.
Per-module project cards + quick-open. Redesigned project cards: a module-mix accent strip, per-module stat lines (e.g. "Vibration · 4 locations · 2 units" / "Sound · 2 NRLs · 2 units · 0 recording"), and Sound / Vibration quick-open buttons that deep-link straight into that module's tab.
"Reforward info" button on classified deployment-hopper cards — re-syncs the captured photo's GPS/metadata onto the location (recovery path for the coords-drop bug fixed below).
redeploy.sh helper — rebuild + redeploy a single compose service with --no-deps instead of rebuilding the whole stack.
Changed
Project page restructured around modules. Sound-only actions (Generate Combined Report, Night Report, Report Settings) and the Manual/Remote chip moved out of the global project header into the Sound tab's toolbar; the header now carries project-level concerns only (status, modules, merge). The Overview's locations are split into Vibration locations and NRLs instead of one mixed list.
Project cards: clearer stats. The ambiguous single Locations / Units / Active row (where "Active" collided with the status badge and only counted sound recording sessions) is replaced by per-module stat lines; the misleading single-module subtitle ("Sound Monitoring") is replaced by the project's identity (number · client). Unit counts are derived by the assigned location's type, so each module's unit count reconciles with its location count.
Overview live-monitoring scope. The live section lists only connected / live-mode NRLs; offline / manual-upload NRLs are excluded, and the section is hidden entirely when no NRL is in a live mode.
Fixed
SLM "Start measurement" showed a false "Unknown error" even though the unit was actually starting — the proxy timed out on a slow handler and surfaced an empty error. (Pairs with the SLMM-side 'Start'-state fix in SLMM v0.4.0.)
Empty project dropdown in the pending-deployment classify modal — the projects endpoint returned HTML while the modal expected JSON; now backed by a JSON endpoint.
Classify button stuck on "Classifying…" after reopening the modal post-deploy.
Deployment-capture GPS dropped when assigning to an existing location — the captured coordinates now backfill onto a coordless existing location (and the new Reforward button re-syncs after the fact).
Event date filters were unusable on the unit / vibration-location detail pages (datetime-local inputs with no apply) — replaced with date inputs that apply on change.
Upgrade Notes
Migration: run backend/migrate_add_module_status.py once against the production DB (adds project_modules.status, default active) — e.g. docker compose exec web-app python3 backend/migrate_add_module_status.py. Without it, the per-module status endpoints/UI error.
Ships with SLMM v0.4.0. The Overview live monitoring reads SLMM's shared /monitor fan-out feed, and the SLM start fix pairs with SLMM's 'Start'-state recognition — deploy the matching SLMM v0.4.0 build, not one without the other.
[0.15.0] - 2026-06-18
Operator authentication — the internal app gets a login. The operator-facing surface had zero auth; this adds a deny-by-default login gate and roles, the prerequisite that makes the app safe to put behind a public URL (office-deployment sequencing: auth → expose). Built test-first (10 tasks, 90 passing tests — the project's first auth test suite alongside the portal's).Added
Operator authentication (login + roles), shipped dark behind OPERATOR_AUTH_ENABLED. The internal app gains a deny-by-default login gate (one Starlette middleware over every route except an allow-list: /login /logout /health /static/* /portal/* + the three machine heartbeat endpoints /emitters/report /api/series3/heartbeat /api/series4/heartbeat). Two roles — superadmin (account management at /admin/users) and admin (full app); operator reserved/deferred. Sessions are a 30-day HMAC-signed tv_session cookie re-validated against the DB each request (instant revoke via active / sessions_valid_from). Password reset is superadmin-driven: reset-anyone (temp shown once + forced change), self-service /change-password, and a backend/operator_admin.py seed/break-glass CLI. Brute-force lockout (5 tries / 15 min) + constant-time login (no user-enumeration). Reuses the portal's argon2 hasher + a new shared backend/auth_cookies.py HMAC signer. Spec: docs/superpowers/specs/2026-06-17-operator-auth-design.md, plan: docs/superpowers/plans/2026-06-17-operator-auth.md.
.env.example documenting SECRET_KEY / COOKIE_SECURE / OPERATOR_AUTH_ENABLED for deployment.
Known limitations
SLMM proxy WebSocket endpoints bypass the gate. /api/slmm/{id}/stream|live|monitor are WebSocket upgrades, which a Starlette HTTP middleware never sees — they stay unauthenticated even with the gate on. Pre-existing (not a regression); close it (in-handler tv_session check, as the portal WS already does) before true internet exposure.
No TLS yet — until served over HTTPS the login password crosses the wire in cleartext. Still a large improvement over the prior zero-auth exposure; real internet exposure needs the deployment-phase TLS.
Upgrade Notes
New operator_users table auto-creates on startup — no migration.
Set a real SECRET_KEY (in a gitignored .env; template at .env.example) before internet exposure; set COOKIE_SECURE=true once on HTTPS (leave false on plain HTTP or the browser won't send the cookie).
Rollout (no self-lockout): deploy with OPERATOR_AUTH_ENABLED=false (app behaves exactly as before) → seed a superadmin via docker compose exec web-app python3 backend/operator_admin.py create-superadmin … → confirm you can log in → set the flag true and docker compose up -d web-app. Flipping it back to false is the instant escape hatch.Downloads