One object graph, three settlement layers. A budget is always the same thing — a funded ceiling fanned out into capped, revocable streams — but the ceiling can be funded three ways:
| Mode | The ceiling is | Settlement is | Money at risk |
|---|---|---|---|
Control (none) | A ledger record | Ledger entries | None — no money exists anywhere |
Escrow (usdc) | An on-chain deposit in the vault contract | On-chain, per-voucher, against escrow | The deposited amount, held by contract rules |
Card (card) | ONE manual-capture authorization (card authorization-backed budgets) | PSP captures + split transfers per voucher totals | The authorized amount, held by the card network |
The mode is a tag on openBudget (or one environment variable in the quickstarts); streams, vouchers, session keys, credentials, revocation, and reconciliation are byte-for-byte the same code.
the engine and (in escrow mode) by the contract itself.
a provider's claim. Crash and recover: the ledger re-derives from signed artifacts.
units — never billed, on any rail.
settlement layer's own records, to the unit — a mismatch is a loud failure, never a rounding note.
bigint;floating point never touches an amount, a rate, or a fee.
Start with Control — it is the full protocol minus money and it costs nothing to be wrong. Move to escrow when your counterparties settle on-chain; move to card when buyers fund with cards. Both money rails run sandbox-first (testnet, card sandbox); parity with production surfaces is a versioned promise (R9).