CI fleet
Actions Runners
Many self-hosted GitHub Actions runners on one Apple Silicon Mac, and the page that says whether any of them is broken.
GitHub-hosted macOS minutes bill at ten times the rate against a private account's included allowance, and one iOS repo exhausting that allowance blocks Actions account-wide — taking the cheap Ubuntu jobs in unrelated repos down with it. A fleet on hardware you own removes that shared fate. The cost is owning the host's health: idle listeners are nearly free, two simultaneous Xcode builds are not.
The dashboard exists because GitHub has no cross-repo Actions view, and half of the question — is that runner even alive — is not answerable from GitHub at all. One Node process with no npm dependencies and no build step polls GitHub and the machine, keeps everything it learns in SQLite forever, and serves one page: Fleet, Runs, Analytics, Lint, Alerts, Capacity, Hosts and Control.
- 0
- runtime dependencies, no build step
- 6
- kinds of drift between launchd and GitHub
- 123
- fields checked against PyYAML, zero mismatches
- 10×
- what hosted macOS bills against the allowance



What it proves
Three things worth reporting
The most useful thing it knows is when two systems disagree
Drift is the set of states where launchd and GitHub disagree about a runner, and each one is silent from whichever side you happen to be looking at. A runner registered on GitHub with no LaunchAgent queues jobs forever; a listener GitHub calls offline looks fine locally. One rule exists because no runner plist sets KeepAlive — a crashed service is never revived by launchd, and the only symptom is one repo's jobs queuing while every other repo looks healthy.
The numbers exclude what would make them wrong
Cancelled runs are out of every duration percentile, because a cancelled run's duration measures how long until something killed it. Jobs that never reached a runner are out of concurrency, because they present as day-long intervals overlapping everything. The host tile leads with the kernel's memory-pressure level and the swap-in rate, not swap used — on macOS that is an accumulator, not a gauge, and it stays high long after nothing needs it.
Alerts fire on transitions, and the control plane has no shell
A rule evaluated every fifteen seconds that notified whenever it was true would send 240 notifications an hour for one dead runner. Each condition opens once and closes once, with a sustain window and a storm guard. Every action the daemon can take is execFile with an argv array against an entity it already discovered — a runner name containing a shell command is a name that fails to match a known runner.
More open source
Fleet Runner →