429

meter

The kill-brake for agent spend. 429-as-a-service.

node ≥ 20zero depsMITport 4187

01Ten seconds of insurance

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.

02Make Claude Code obey it

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" } ] } ] } }

03How it holds

Out-of-band

Not a proxy. No traffic reroutes through it — agents ask permission, which is one HTTP call, not an architecture.

Fleet-shared

One counter for every process on every machine in every language. Atomic check-and-increment; a 429 commits nothing.

Human doorbell

The 429 carries a raise URL. A live status page with a big remaining bar and raise buttons — the console is a browser tab.

04The whole API

calldoes
POST /{topic}/spendPreauthorize: 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}/logRecent spends {at, amount, note, allowed}, newest first.
GET /{topic}/sseLive 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.

05Run yours

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.

06Straight talk

07The family

meter is one of ten agent-infra primitives. Same stack, same idioms, one curl each.

gate4180ntfy tells you things. gate asks you things.
bigred4181The big red button for your agent fleet.
trail4182The flight recorder for agent runs.
slate4183The blackboard from the multi-agent papers, as a URL.
relay4184The work queue your agents can provision themselves.
mutex4185flock(1) for agents that live on different machines.
quorum4186Coordination for agents that do not share a parent process.
meter4187The kill-brake for agent spend. 429-as-a-service.
stash4188ntfy moves signals. stash moves bytes.
tally4189StatHat reborn as ntfy. Three months too late — or right on time.