agentvault

architecture

The circuit is the law.

Five moves, three layers of enforcement, one payment rail. A record type can only be consumed by its own program’s transitions — so the permit is unusable outside spend(), and spend() is the policy.

you ──create_vault──▶ [ VaultPermit · encrypted ] ──▶ your agent
 ▲                                                        │  GET /api/premium/…
 │                                                        ▼
 SpendReceipt ◀── spend() ◀── policy ok ◀── 402 + priced quote
 (view key)          │
                     ├── in-circuit: cap · budget · category · expiry · revoked
                     └── credits.aleo::transfer_private ──▶ merchant (sealed)

01 / create_vault

Seal

The owner signs once. The policy becomes an encrypted record only the agent can consume — and a private AdminCap only the owner holds.

assert(per_tx_cap <= budget)
assert(per_tx_cap > 0)
assert(category_mask > 0)
// the chain checks freshness, then seals

02 / HTTP 402 → spend

Spend

The paywall quotes a nonce-priced amount. The SDK pre-checks it against the permit locally — then proves the spend and the merchant unlocks on the payment’s proof.

{ "scheme": "prepaid", "network": "aleo-testnet",
  "maxAmountRequired": "1579592",
  "extra": { "programId": "agent_vault_v1.aleo",
    "functionName": "spend" } }

03 / approval.requested

Ask

Above the owner’s threshold — set under the cap, changeable live from the console — the purchase pauses before any proving until the owner answers.

approval.requested  1.579592 cr > 1.2 cr
approval.resolved   approved · 2.0s round-trip
// the cap is the law; the threshold is your say under it

04 / spend.refused

Refuse

Out-of-policy quotes die locally in about a millisecond — no proof, no payment, nothing sent — and every refusal lands in the owner’s decline feed with its rule.

per_tx_cap · budget · category · expiry · revoked
// + owner_denied and approval_timeout, from the SDK layer

05 / revoke

Revoke

The AdminCap is consumed, one public bit flips — and the agent’s next spend is rejected at finalization with a valid proof. Consensus, not a prompt.

finalize: assert(!revoked[vault_id])
→ status: "rejected" · state reverted, fee only

where enforcement lives · every rule in its layer, on purpose

Three layers, no trust between them.

circuit — enforced by consensus

A violating spend cannot produce a valid proof; a revoked or expired one is rejected even with a valid proof. The chain will not accept it — nobody has to decline it.

amount ≤ per-tx cap

Asserted inside spend(). There is no valid proof of an over-cap payment; the transaction can't exist.

amount ≤ remaining budget

The successor permit carries the decremented budget; overdraw has no valid witness.

category ∈ allowlist

(mask >> category) & 1 == 1 — the 64-bit allowlist is checked in-circuit, without ever being revealed.

expiry height

Checked at finalization against real chain height — client clocks don't get a vote.

revocation

One public mapping bit. After revoke, finalization rejects valid proofs — the kill-switch judges the proof, not the intent.

spend-once

Records are UTXOs: consuming the permit produces a serial number the protocol will never accept twice.

sdk — the agent's runtime

Everything here fails fast and costs nothing — and none of it widens what the circuit allows.

local policy pre-check

The same asserts as the circuit, run in ~1 ms against the decrypted permit — a doomed spend never burns 30–75 s of proving.

approval threshold

The owner's comfort line UNDER the cap. Off-circuit on purpose: the deployed circuit is immutable, and your comfort line changes at dashboard speed. Every surface labels it this way.

quote-TTL guard

Refuses to start proving into a quote that would expire first — money never chases a dead offer.

private fees, always

A public fee would print the payer's address next to the vault id in the same transaction. The SDK refuses the combination.

facilitator — the merchant's policy

The paywall holds a view key only — it can read its own incoming payments and cannot move funds.

program + function gate

Only payments made through agent_vault_v1.aleo/spend unlock content. A direct transfer of the exact amount is refused — proven on-chain in the evidence.

amount-nonce quote binding

Every live quote gets a unique price tail; redemption requires the exact match. A copied txid off the explorer unlocks nothing.

one txid, one unlock

Replay is a refusal: a transaction id retires the moment it redeems a quote.

Everyone else’s spend policy is either their secret or everyone’s. Here, it is sealed — and consensus enforces it anyway.
the part that is only possible on aleo

honest limits

  • Custody is Model A. The agent holds its own private records; a rogue agent could move them outside the vault — and compliant merchants reject those payments, as the circuit-bypass transaction proves on-chain.
  • The threshold is not in the circuit. The cap is. That layering is the design, and every surface says so.
  • Testnet only. No real value has ever touched this codebase.

sealed until agent_vault_v2

  • ▚▞ private velocity — rolling windows whose tempo the chain never sees
  • ▞▚ allowlist membership proofs — "this merchant is allowed" without the list
  • ▚▞ reversible freeze · in-permit refill
  • ▞▚ in-circuit quote binding — the payment commits to its quote