The kill-brake for agent spend. 429-as-a-service.
An out-of-band, fleet-shared budget counter. Every agent preauthorizes with one curl: 200 means proceed, 429 means stop. Dollars, tokens, calls — the cap doesn't care.
# before the overnight run: cap the fleet at $50/day curl -X PUT 'https://meter.legible.sh/acme-fleet-x7k2?cap=50&period=day&unit=usd' # in every agent, before anything expensive: curl -X POST -d 2.50 https://meter.legible.sh/acme-fleet-x7k2/spend {"topic":"acme-fleet-x7k2","spent":2.5,"cap":50,"remaining":47.5,"period":"day",...} # when the loop asks for too much: 429 {"error":"cap exceeded","spent":49,"asked":5,"cap":50, "raise":"https://meter.legible.sh/acme-fleet-x7k2"} # ...open that URL on your phone. See what the fleet wants. Tap to raise. Or don't.
One paste into .claude/settings.json and meter stops being advisory: every expensive tool call preauthorizes first, and a 429 blocks the call — exit 2, with a message telling Claude why. Zero code changes. meter hook <topic> prints it with your URL baked in.
{ "hooks": { "PreToolUse": [ {
"matcher": "Bash|Task|WebFetch|WebSearch",
"hooks": [ { "type": "command", "command":
"code=$(curl -s -o /dev/null -w '%{http_code}' -m 5 -X POST -d 1 \
\"${METER_URL:-https://meter.legible.sh}/claude/spend\"); \
if [ \"$code\" = \"429\" ]; then \
echo \"meter: claude is over budget — raise the cap: ${METER_URL:-https://meter.legible.sh}/claude\" >&2; \
exit 2; fi" } ] } ] } }
Not a proxy. No traffic reroutes through it — agents ask permission, which is one HTTP call, not an architecture.
One counter for every process on every machine in every language. Atomic check-and-increment; a 429 commits nothing.
The 429 carries a raise URL. A live status page with a big remaining bar and raise buttons — the console is a browser tab.
| call | does |
|---|---|
| POST /{topic}/spend | Preauthorize: body is a number or {"amount", "note"}. 200 fits · 429 doesn't (nothing committed). ?dry=1 checks only. |
| PUT /{topic} | Config: {"cap": 50, "period": "day|month|total", "unit": "usd"}. Query params work too. |
| GET /{topic} | Status JSON for curl, live page for browsers. ?wait=30 long-polls. |
| POST /{topic}/raise | {"by": 10} or {"to": 100} — lift the cap. |
| GET /{topic}/log | Recent spends {at, amount, note, allowed}, newest first. |
| GET /{topic}/sse | Live spend events, SSE. |
Topics are created by first use and named by you — an unguessable name is the access model. Self-hosters add --token to guard cap and raise; spend never needs a token. Brakes must not need keys.
npx meter-sh serve # http://0.0.0.0:4187, in-memory npx meter-sh serve --data-dir ./data # JSONL, replayed on boot npx meter-sh serve --token s3cret # guard cap/raise
Zero dependencies, one node:http process, identical API to the hosted instance. The CLI (meter spend / status / cap / raise / log / hook / serve) is sugar over the same six URLs.
meter is one of ten agent-infra primitives. Same stack, same idioms, one curl each.
| gate | 4180 | ntfy tells you things. gate asks you things. |
| bigred | 4181 | The big red button for your agent fleet. |
| trail | 4182 | The flight recorder for agent runs. |
| slate | 4183 | The blackboard from the multi-agent papers, as a URL. |
| relay | 4184 | The work queue your agents can provision themselves. |
| mutex | 4185 | flock(1) for agents that live on different machines. |
| quorum | 4186 | Coordination for agents that do not share a parent process. |
| meter | 4187 | The kill-brake for agent spend. 429-as-a-service. |
| stash | 4188 | ntfy moves signals. stash moves bytes. |
| tally | 4189 | StatHat reborn as ntfy. Three months too late — or right on time. |