steps
Runbook steps with status, live timers and approval gates.
Local daemon · 127.0.0.1:7717
djt-hub is a small local daemon. Your agent builds a page of steps, stats and logs. You watch it update and click the gates.
Every click comes back to the agent as an event.
Ten block types
Each block binds to a path in the hub's JSON state. Change the value and every open tab updates.
Runbook steps with status, live timers and approval gates.
Items to tick. Each toggle is an event.
Big numbers with units and a tone: ok, warn, bad, info.
Line, area or bar. Time series or categories.
Rows from state. Status columns render as badges.
Lines from state, or live events filtered by prefix.
Buttons. Dangerous ones ask for a second click.
Fields that submit their values as one event.
Headings, lists, code and links.
A sandboxed iframe, for when no block fits.
The loop
Everything is a CLI command that prints JSON. No SDK, no server code.
$ djt-hub create release-42 --title "Release 4.2" $ djt-hub block add release-42 '{"id":"run","type":"steps","title":"Runbook","bind":"steps"}' $ djt-hub patch release-42 --json '[{"path":"steps","value":[ {"id":"build","label":"Build","status":"running"}, {"id":"promote","label":"Promote","status":"waiting","gate":"Promote to prod?"}]}]' $ djt-hub open release-42 $ djt-hub wait release-42 --match block=run --match step=promote --timeout 1h $ djt-hub patch release-42 steps.promote.status=running $ djt-hub close release-42
Events
Wait for one, or follow the stream while you work. Pass a seq to replay what you missed.
gate.approvegate.rejectactionbutton pressform.submitvalues by fieldchecklist.toggleitem and donesvc.*started, exited, failedagent.*done with cost, or failedemit <hub> <type> [json]Agents, services and html blocks can add any type.{
"seq": 41,
"hub": "release-42",
"source": "user",
"type": "gate.approve",
"payload": { "block": "run", "step": "promote", "note": "go" },
"ts": "2026-10-16T15:42:07Z"
}
Services
The daemon supervises commands in their own process groups. They survive the agent exiting and daemon restarts. Closing the hub stops them.
# poll every 30s $ djt-hub svc add release-42 p99 --every 30s -- 'djt-hub patch health.p99.value=$(./p99.sh)' # keep a tail running $ djt-hub svc add release-42 tail --restart always -- kubectl logs -f deploy/api # run on a schedule $ djt-hub svc add release-42 nightly --cron "0 3 * * *" -- ./report.sh # react to a click $ djt-hub svc add release-42 on-reject --on gate.reject --match step=promote -- ./rollback.sh
--every, --restart and --cron cover the usual jobs.djt-hub patch inside a service targets its own hub.Daemon agents
If a click lands while no session waits on the hub, a daemon agent runs claude -p for that event. Your own session always wins.
$ djt-hub agent add release-42 oncall --on gate.approve --match step=promote \ --prompt "Run the promote step, then mark it done" \ --allow "Bash(./promote.sh)" $ djt-hub agent ls release-42 $ djt-hub agent reset release-42 oncall
djt-hub. Widen it per agent with --allow.agent.done or agent.failed. Budget caps a run at $1.00 unless you say otherwise.Prefer a service or a plain --on trigger. Add an agent only when the event needs judgment.
Install
Needs Go. Binds to 127.0.0.1 only and keeps its data in ~/.local/share/djt-hub/.
$ git clone https://github.com/tory37/djaunt-agent-hubs.git $ cd djaunt-agent-hubs $ brew install go $ ./install.sh # build + launchd $ djt-hub status $ djt-hub docs # the agent guide $ examples/release-runbook.sh
./install.sh --no-launchd to start the daemon yourself.install.sh.djt-hub uninstall drops the launchd agent and keeps your data.