Sensors: read-only tokens, live stream, Prometheus, status sensors, widgets, embeds

- sensors:read tokens open the sensor endpoints (here and on managed machines
  through the hub) and nothing else; full tokens are never accepted from a URL
- /api/sensors/stream: a shaped query that keeps answering, over SSE
- /metrics: Prometheus text format
- services, updates, ZFS and SMART as background status sensors
- widget library over the sensor API; custom widgets on the dashboard
- /embed/widget: one live widget in its own bundle, the only frameable path
- Dashboard tab is now Monitors

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-19 07:37:10 -07:00
co-authored by Claude Fable 5.1
parent 468f45251d
commit b05a053920
20 changed files with 950 additions and 56 deletions
+12 -12
View File
@@ -176,18 +176,18 @@ something long in one, switch away and back, refresh — it's all still there.
the daemon's sampling clock; the chart window is always 300 samples, so 30 s at 100 ms
and 10 min at 2 s. Still to do: persistent
history (SQLite ring, e.g. 10 s × 7 days), SMART (`smartctl -j`), longer time ranges.
- **Sensors** — **step 1 shipped 2026-09-19** (`internal/sensors`,
`internal/metrics/sensors.go`, reference in `SENSORS.md`): everything measured is a
sensor with a stable dotted ID; catalog, single value (`?format=text`), per-sensor
history, and a **shaped query** where the client sends the object it wants and gets it
back filled in (GraphQL's idea without GraphQL: sensors differ per machine, so a static
schema would collapse to `sensor(id:)` anyway). `/api/metrics` and the Dashboard are
untouched so far. **Remaining, in order:** (2) read-only scoped tokens + Prometheus
`/metrics`; (3) push stream + widget library, Dashboard rebuilt on its own sensors;
(4) embeddable widget page + Glance config (Glance's `custom-api` only refetches on page
load, an `iframe` of our embed is what's live); (5) non-metric sensors — services,
SMART, ZFS, updates, containers. One sampler rate, set in one place, is part of (3):
today the open dashboard still drives it.
- **Sensors** — **shipped 2026-09-19** (`internal/sensors`, `internal/metrics/sensors.go`,
`internal/api/status_sensors.go`, `web/src/sensors.ts`, `widgets.tsx`, `Embed.tsx`;
reference in `SENSORS.md`): everything measured is a sensor with a stable dotted ID;
catalog, single value, history, a **shaped query** (the client sends the object it wants
and gets it back filled in — GraphQL's idea without GraphQL, since sensors differ per
machine and a static schema would collapse to `sensor(id:)`), the same query as a live
SSE stream, Prometheus `/metrics`, read-only `sensors:read` tokens (never full tokens in
a URL), status sensors (services, updates, ZFS, SMART), a widget library, custom widgets
on the Monitors tab (was "Dashboard"; the view id and URL are still `dashboard`), and a
frameable `/embed/widget` for Glance and friends. **Remaining:** port the stock Monitors
modules onto sensors; per-unit service and container sensors; pattern-restricted tokens;
one-request fleet query.
- **Multi-host** — **Phase 1 shipped 2026-09-18** (`internal/nodes`, `internal/tlsauto`,
`MachineSwitcher.tsx`, `Machines.tsx`). One binary, hub and spoke: a host whose
`config.json` lists `nodes` is a hub; any other is a standalone node with its full UI.