Quickstart: unattended triage (offline)
This walkthrough runs mast’s anchor workload — GKE incident triage with 13
specialists — entirely offline on the built-in echo model. No credentials,
no network, no cluster. You’ll inject an incident, watch the workload pause
for operator approval, kill the daemon, and resume the approval in a fresh
process: the durability pillar, live on your laptop.
You need Go and a clone of the repo (the workload bundle and specialists
ship as files under examples/):
git clone https://github.com/go-steer/mast.gitcd mast1. Build and start the daemon
Section titled “1. Build and start the daemon”mkdir -p /tmp/mast-demogo build -o /tmp/mast-demo/mast ./cmd/mastStart it with the triage workload, graph dispatch, and a SQLite session DB
(that’s the durability — omit --session-db and sessions are in-memory):
/tmp/mast-demo/mast \ --workload=examples/workloads/gke-triage \ --dispatch=graph \ --model=echo \ --listen=:7777 \ --session-db=/tmp/mast-demo/sessions.dbLeave this running and open a second terminal.
2. Inject an incident
Section titled “2. Inject an incident”The workload’s edge trigger is HTTP: POST an incident envelope to
/inject. Each incident UID gets its own session (incident-<uid>).
curl -s -X POST http://localhost:7777/inject -H 'Content-Type: application/json' -d '{ "kind":"Pod","reason":"ImagePullBackOff","namespace":"default","name":"web-1", "uid":"demo-1","message":"back-off pulling image","cluster":"demo"}'The SingleTurn classifier routes the incident to the ImagePullBackOff
specialist. The bundle sets hitl.require_approval: true, so the specialist’s
proposed action parks on a durable HITL interrupt instead of executing.
The daemon log shows HITL PAUSE with the interrupt ID.
3. Inspect the pause
Section titled “3. Inspect the pause”mast sessions list and show read the SQLite DB directly — they work with
or without a running daemon:
/tmp/mast-demo/mast sessions list --session-db=/tmp/mast-demo/sessions.db/tmp/mast-demo/mast sessions show incident-demo-1 --session-db=/tmp/mast-demo/sessions.dbshow prints the pending interrupt (approve-ImagePullBackOff — interrupt
IDs are deterministic per specialist), the approval message, and the exact
resume command.
4. Kill the daemon — the pause survives
Section titled “4. Kill the daemon — the pause survives”In the first terminal, kill the daemon as rudely as possible, then restart the same command:
kill -9 $(pgrep -f 'mast-demo/mast --workload')/tmp/mast-demo/mast \ --workload=examples/workloads/gke-triage \ --dispatch=graph \ --model=echo \ --listen=:7777 \ --session-db=/tmp/mast-demo/sessions.dbThe pause was persisted to SQLite; the fresh process picks it up.
5. Resume with an operator verdict
Section titled “5. Resume with an operator verdict”Via curl against the daemon’s /resume endpoint:
curl -s -X POST http://localhost:7777/resume -H 'Content-Type: application/json' -d '{ "session_id":"incident-demo-1", "interrupt_id":"approve-ImagePullBackOff", "response":{"approved":true,"note":"rollback approved by oncall"}}'Or the same thing through the CLI:
/tmp/mast-demo/mast sessions resume incident-demo-1 \ --interrupt=approve-ImagePullBackOff \ --response='{"approved":true,"note":"rollback approved by oncall"}'The session resumes exactly where it paused and runs to completion. To
refuse instead, mast sessions abort incident-demo-1 --reason="not today"
writes a durable abort marker; later resumes are refused.
6. Look at the metrics
Section titled “6. Look at the metrics”The inject listener also serves Prometheus metrics:
curl -s http://localhost:7777/metrics | grep '^mast_'You’ll see the turn, model-call, token, cost, HITL, and budget families — all listed in the metrics reference.
The whole thing, scripted
Section titled “The whole thing, scripted”scripts/demo-spike2.sh runs three scenarios end to end (graph routing,
durable HITL across kill -9, and a $0.01 budget cap tripping mid-turn):
scripts/demo-spike2.shGoing real
Section titled “Going real”- Swap
--model=echofor a Gemini model id (e.g.--model=gemini-2.5-flash) with Google Cloud ADC available; MCP toolsets from the bundle’stool_catalogget wired to specialists. - Set
MAST_INJECT_TOKENin the daemon’s environment to require bearer auth on/inject,/resume, and/abort(unset = unauthenticated, dev only). - Against a real cluster the roster’s shape starts to matter: the twelve
diagnosers hold read tools only and name the remediation in their finding,
and the one
change-executorspecialist is the only one that could carry it out — parking for your approval before each call that changes anything. Mast refuses to start a roster that blurs that line. A diagnosis reaches the executor by carrying the call itself: the finding’sproposed_changenames a tool from the catalog with arguments checked against that tool’s schema, and once you approve it, those exact calls are what the executor runs. A diagnosis that cannot name an exact call proposes nothing and the incident ends at the finding. When a fix is several calls, one{"verdict":"approve","scope":"change_set"}authorizes the rest of that set — re-checked against the cluster before each one fires, so an approval stops covering a call the moment somebody else moves the object it is about. See the change set, one answer for a set of calls and per-specialist capability, and classify any tools you add withtool_catalog.tools[].mutating: an unclassified tool counts as mutating, so an unclassified read tool will stop and ask. - For production topologies (Cloud Run + Postgres sessions, GKE, systemd)
see
examples/deploy/in the repo. The GKE kustomize base underdeploy/is durable by default — the daemon runs as a StatefulSet with a PVC-backed--session-db, so pauses, abort markers, and shutdown interruption markers survive pod rescheduling. In-memory sessions (omitting--session-db) are a local-development opt-out, not a deploy default. - That base also grants the daemon cluster-wide read and nothing else; the permission to change a namespace is a separate apply, once per namespace. See cluster permissions, including the GKE IAM caveat that decides whether the split bounds anything.