Muster: Rebuilding the Dashboard From the Honesty Gates Up
5 min read
My homelab dashboard on :8400 was a monitoring wall: panel after panel describing the machine. Nothing on it was wrong, exactly. It just never answered the only question I actually bring to a dashboard — what happened since I last looked, and what needs my hands?
Earlier this week I tore it down and rebuilt it as something called Muster. A muster is a roll call, not a wall. The new home page, the Release Radar, the chain DAG, the ⌘K palette — those are the parts you'd screenshot. But the build order is the part worth writing about, because the first two PRs changed no pixels at all.
The question, as an endpoint
Muster starts as a data contract, not a page. GET /api/muster composes a "since you last looked" payload with four parts:
- delta — the latest overnight chain run: counts of ok, failed, degraded, skipped, and unknown steps, with the failed and degraded ones named.
- queues — how many queues exist and how deep each one actually is.
- obligations — the approvals sitting in a queue waiting for me. Work blocked on a human.
- stakes — what's degrading right now: failed chain steps, a held GPU lock, queue depths that can't be read.
One rule holds the whole contract together: whatever the backend can't read, it reports as unknown. No chain run recorded yet? It says no_runs. Queue depth unreadable? That's unknown — and it becomes a stake in its own right, because unreadable is a problem, not an absence of problems. The endpoint is honest by construction. It cannot fabricate an all-clear.
The two gates underneath
That rule is cheap to state and expensive to honor, because the endpoint can only be as honest as what sits under it. So before any UI, two gates went in.
Gate one: enumerate the queues. Real depths, per queue — a count, a total, a breakdown — instead of an assumption that quiet means empty. In the same session that shipped, the backlog got drained from 4,278 tasks down to 3, and two dead workers — embeddings-backfill and repo-indexer — finally got properly installed. I won't claim the dashboard discovered all of that by itself. I will say a wall of green had been leaving room for it. And the flush wasn't casual: it's destructive, so it got investigated, snapshotted, and held for explicit approval before anything was touched.
Gate two: durable chain runs. Every overnight chain run now persists to forge:chain:runs — a record that outlives the night it describes.
The value of that gate showed up as a scheduling constraint, which is my favorite kind of proof. The PR that shipped the ⌘K palette had to explicitly defer the chain drill-down view, with a note: it needs a persisted chain run, and the first one lands tonight at 05:00. You cannot build a view over data that doesn't exist yet. The next morning, run 81f238dc73b0 had persisted — 26 of 27 steps ok — and the view got built against a real, imperfect night instead of a fixture.
A login shell, not a wall
The Muster page itself is a TTY treatment — the PR describes it as "The Fort at Night, with a login shell." The delta renders like an MOTD. The degrading-now list sits under STAKES. The obligation queue renders as an htop-style process list: j/k to move, a to approve, x to dismiss, each keystroke wired to a real POST /api/approvals/{id}/decide. It refetches every fifteen seconds. And / now lands there — the old operations wall moved to /operations, demoted from home page to reference material.
The ⌘K palette does two things: navigates (go <page>) and acts (dispatch <worker>). Dispatch completions are fuzzy-matched against the live worker registry endpoint, which means the palette can only offer workers that actually exist. Real, never invented — the same rule as the API, applied to autocomplete.
Release Radar and the SIGNAL DAG
The last PR added the two remaining views, both live against real data.
Release Radar walks my repos with gh pr list and git rev-list and renders each one as a pipeline — open PRs by base branch, whether prod has fallen behind staging, the last merge — across review, staging, and prod stages. Its first live payload earned the feature on the spot: Cortex's prod was sitting ten commits behind staging. Homelab's prod matched main. One glance, one real finding.
The SIGNAL chain page renders the latest durable run as a spatial dependency DAG — nodes colored by outcome, click a node for detail. It carries the honesty rule into the paint layer: unknown and skipped steps are never colored green. A step that didn't run is not a step that succeeded, even in a UI.
What I actually verified
House rules held. The Muster page and the palette both shipped flagged "draft — not human-verified in-browser": TypeScript compiled clean, the bundles built, the endpoint had been curled live — but a page isn't verified until someone has looked at it, and that someone is me. The final PR closed the loop with a post-deploy live curl and a browser check, and by the end of the session the whole arc — two gates, the endpoint, the page, the home repoint plus palette, the radar plus DAG — was merged, deployed, and verified. One PR ran over my own five-file cap at six source files; its body says so plainly instead of pretending otherwise.
The takeaway is the build order. The views are the easy part — any afternoon can produce a pretty DAG. What makes a dashboard worth trusting is a substrate that's allowed to say "I don't know," because a dashboard that can't say unknown will say all clear instead — and you'll believe it, because it's green.
Honesty first. Then paint.