Guides

Mandates & budgets

A standing mandate is the “don't ask me again” lane: the human signs caps once, and runs inside those caps go silent. It exists because asking on every $0.50 action is consent fatigue — and it is safe because the caps are server-authored, passkey-signed, counted in cents by a fail-closed ledger, and revocable in one tap without the agent's cooperation.

Minting rides the approval ceremony

There is no separate mandate flow. The approval page offers a checkbox whose terms are server-authored — the platform, the per-action cap, the daily budget, the validity window — and folds them into the same WebAuthn challenge as the approval itself. One assertion signs the approval and the standing grant, indivisibly: a mandate can never exist without a biometric signature over its exact caps. A fresh mint supersedes the previous active grant for the same platform and key.

The autonomy box

LimitDefaultWhat it does
Per-action cap$2.00The most any single covered run may cost. A pricier quote steps up and asks the human.
Daily budget$5.00Total covered spend in a rolling 24-hour window. Exhausted budget → step up and ask.
Validity30 daysThe mandate expires on its own; expiry inside a quote's window refuses before anything runs.
Hard stop$5.00The price tier no mandate can ever cover, regardless of its minted caps.

These are the shipped defaults (tunable server-side); whatever the values, the human signs the exact numbers shown on their approval page, and 'always' actions sit above every mandate — the hard rung no standing consent reaches.

Scope: per platform, bound to a verified key

Coverage requires an identity the door verified — a managed bak_ key. Anonymous and custom-custody callers on a 'mandate' action ask every time, and network-wide mandates are deliberately deferred (they are the prompt-injection drain vector).

Evaluated twice, like policy

  • At quote: a covered quote is born plain — zero human interaction. An uncovered or over-budget one steps up to needs_approval, with the remaining budget stated in the response and on the approval page, so both sides see why it asked.
  • At confirm, pre-flight: the mandate is re-read live and the budget re-summed. Revocation or expiry inside the quote's 10-minute window refuses before anything runs — quote intact, and the refusal invites a fresh quote, which asks the human directly.

The ledger counts cents, fail-closed

One spend row per executed confirm, keyed by the quote id and written before the winner-only consume — so exactly-once budget decrement is structural, inherited from the exactly-once execution machinery. Durable sums (per mandate, per key per day, per door per day) are the authority. Both failure directions close: a budget sum that cannot be read refuses the confirm; a spend row that cannot be written refuses before the consume. A leaked quote id, ctx token, or transcript is never worth a mandate's budget — spend authority lives only in this server-side ledger, never in any token an agent holds.

Revocation is one tap, agent-free

bubblio.dev/approvals lists every mandate this passkey granted, remaining budget computed live, with a revoke button. Revocation is winner-only and immediate: the next quote skips the dead mandate, and a confirm on a quote it covered re-reads it live and refuses. See the security model for where this sits in the fail-open/fail-closed map.