PRISM Dashboard (prism-fe)
Multi-tenant SaaS front end for turbine fleet monitoring: live HMI over SSE, trend diagnostics, alarms and trigger-event forensics.

The operator- and customer-facing surface of PRISM. operator staff see the whole fleet; each customer tenant sees only their sites and turbines. A pnpm workspace with a Vite/React SPA, a Hono/tRPC API on Node 24, and a shared package holding the Postgres schema, dual time-series backends, RBAC and the turbine signal catalog.
What it does
- Fleet overview with summary cards and a turbine status grid: online/offline, run state, kW, power factor, T5 average, alarm count.
- Live multi-unit HMI (premium): a grid of per-turbine cards updated every 1.5 seconds over a tRPC SSE subscription. Each card has a hand-drawn turbine cutaway SVG, dial gauges for load and gas-producer speed, a thermocouple-spread micro-chart, and a freshness footer that shows both the SSE tick age and the PLC cycle age so an operator can tell a stale stream from a stale PLC.
- Per-turbine detail with rolling sparklines, a live values panel and live alarms.
- Trend chart with visible zoom controls and a per-user default time window persisted server-side.
- Alarms: active alarm table plus event log with severity badges and filters.
- Trigger-log forensics: event browser, a rules editor for five trigger modes, a multi-series capture chart with per-tag toggles, and binary downloads in the OEM's own format.
- Admin console: tenants, sites (with location, contacts, driving instructions and a per-site ClickHouse endpoint), drag-sortable turbines, user-to-tenant assignment, and a tenant switcher for staff.
- In-app deploy: an admin form dispatches the promote workflow on the self-hosted CI, so releases to Test and Prod are pushed from inside the product.
- Feedback pill that opens a Forgejo issue pre-filled with server-attested user, role, tenant, environment, build and turbine context.
Engineering notes
The time-series facade. A 24-line module exports an identical five-function read API and picks ClickHouse or InfluxDB from one environment variable. That let the ClickHouse migration run in parallel with the working Influx path and cut over with zero risk.
Fixed-cadence SSE loop. Each tick targets tickStart + interval rather than lastEnd + interval, so a slow tick cannot compound into drift. The alarms fetch is cached with a 5-second TTL because refreshing it every tick ate half the loop budget, and a failed refresh re-stamps the cache so a flapping upstream is not hammered.
Flux query safety. Flux is string-built, so every interpolated identifier passes an allowlist regex. The code explains why a space is permitted (plc_name can be Unit 1) and exactly what stays blocked. A second pattern handles array-indexed tag names like HMI_Analog_Array[32].
The cookie war story. The auth library auto-prefixes cookies with __Secure-. A prod-only the operator domain caused Prod's cookie to clobber the host-scoped cookies on the dev and test hosts, producing a login loop. All three environments now use host-scoped cookies, and the reasoning is recorded next to the config.
Signal catalog as data. Every Centaur 40 signal is mapped to its position in the PLC's HMI analog array, reverse-engineered from the OEM's proprietary HMI template, each with unit, precision and a Fahrenheit-to-Celsius transform. Where the template and the PLC program disagreed, the comments record which source won and why. Run state is derived by bit-masking a 32-bit status word.
Deploy health check compares image SHAs. The post-deploy check compares the running container's image digest to the dispatched tag, an explicit workaround for a catalogued bookkeeping bug in the self-hosted CI platform.
Stack
pnpm workspace on Node 24 with strict TypeScript and noUncheckedIndexedAccess. The web app is Vite 7 and React 19 with file-based TanStack Router, TanStack Query, Table and Form, Tailwind 4 design tokens and MUI X Charts. The API is Hono 4 with tRPC 11 and better-auth over Authentik OIDC. Three Docker images (API, nginx-served SPA, and a Grafana build with dashboards baked in) are built by Forgejo Actions, pushed to the self-hosted OCI registry and deployed by Ansible.
Screenshots


