authorization between an AI agent and a payment provider
3fields an agent may set
5MCP tools
0of them can move money
53guards deleted, all caught
16attacks refused, with tests
1 · The trust boundary
The agent proposes three fields. Everything that decides what the money does is derived
server-side. There is no amount field for injected text to fill.
2 · Authorized is not the same as able to payTHE GAP
Most systems collapse these into one state. Here they are two, and the distance between them
is a separate authority that must be issued, is single-use, expires in 15 minutes,
and is bound to a hash of that exact purchase.
3 · Delegated authority between agentsTHE DIFFERENTIATOR
Every capability standard models attenuation as set intersection: a child is never wider
than its parent. That is correct for permissions and wrong for money, because budgets add
and sets do not. Two children each no wider than their parent can, between them, hold twice
what the parent ever had.
The refusal comes from ck_delegation_budget_partitioned, a check constraint
on the parent's own row — the budget is partitioned, not compared. And revoking
one hop ends every branch below it without touching a descendant: the leaf's own
revoked_at is still None, and its next spend is refused anyway,
because authority is re-derived from the whole chain on every spend and again at checkout.
A signed token would have to be hunted down and recalled.
4 · The layers
Delete the agent entirely and everything below the dotted line is unchanged, and still
correct. If the agent were the centre of this, the project would be arguing against itself.
5 · Where each rule is enforced
One principle: enforce at the lowest layer that can hold it. A rule in application code
is a rule a different query walks around — so most of them are not in code at all.