PURSER

Give AI agents money without giving them the keys.

Purser is a spending limit AI agents can't talk their way around. It lives in a NEAR contract, and every payment has to clear it.

Hackathon build, NEAR testnet only. Read what it won't do.

completed + outstanding <= budget

NEAR TESTNET / SECURITY, SWARMS, AGENTS purser.demo.arjuna-purser.testnet ->

A spending limit inside the agent is only a suggestion.

Agents are starting to buy data, compute and API access. Today's controls break in three places.

  1. The limit

    The limit lives with the spender.

    A bug, a jailbreak or a bad tool walks straight past a cap in a config file.

  2. The balance

    Two agents read the same balance.

    Both see enough, so both spend. A deliberately naive baseline, on NEAR testnet:

    120 / cap 100

    Each agent saw 100 left and spent 60. Real transfers, from a throwaway account built to fail: tx FR2SJm -> tx 4r9umk ->
  3. The timeout

    A timeout gets mistaken for a failure.

    A timed-out payment can still land. Release the hold, and you've overspent without knowing it.

Three agents ask for 30. There is 40 left.

  1. All three hit the contract in the same block.
  2. The first reserves 30 before any money moves. Now there's 10 left.
  3. The other two get refused. The budget stops at 10.

10 / 100 left

A real run: testnet block 276744286, task demo-20261108071557. 1 unit = 0.001 NEAR. The animation illustrates it.

One budget, one real run

Every drop is a payment the contract admitted. Nothing went below 10.

Every payment takes the same path.

The AI proposes. The contract does the rest.

  1. Open a task

    One shared budget with a hard cap, set by create_task.

  2. Name the crew

    Register each agent. On the NEAR path, list who can get paid.

  3. An agent proposes

    A recipient, an amount and a request_id. The contract builds the rest.

  4. The contract checks

    Active agent? New request? Allowed recipient? Room under the cap?

  5. The budget is reserved first

    Check and reservation run as one step, before any promise exists. That's why a race can't overspend.

  6. The contract pays

    It builds the transfer from the fields it validated. On the EVM path, the MPC signer signs what the contract built.

  7. Only proof closes it

    The settlement callback confirms the payment. Silence keeps the hold.

Where the money sits

StateBudgetWhat moves it on
ReservedHeldA signature, a settled transfer or a proven failure
SignedHeldProof of the outcome, or a timeout
UnknownHeldLater proof, or the owner reconciling the observed outcome
ConfirmedSpentFinal
FailedReleasedSet only on a proven failure
CancelledReleasedEVM only: a confirmed nonce-burn and the owner's release

Why NEAR

Purser builds on what the protocol already enforces.

Check and reserve land in one atomic step

NEAR runs a call as one step until its first promise. Purser checks the budget and reserves inside that step, so two requests can't both pass.

What an agent can't get away with.

What the contract did, each time.

The numbers come with receipts.

0 overruns in 50 concurrent races, checked against the merchant's balance.

Duplicate authorizations
0 of 150 request pairs
Valid requests blocked
0 of 400
Holds released on silence
0 of 10 runs
Attack suite
8 core cases, 10 of 10 each
Property sequences
10,002 + 2,002
Gas per authorization
5.74 Tgas
Added time
+256 ms, 2 blocks
Storage per commitment
375 bytes

Measured on a local nearcore sandbox with the production wasm, 10 repeats. These are not testnet or mainnet figures, and this is not an audit. Regenerate with make metrics or read the full report ->.

What Purser won't do

The full list is in the README ->.

Check our work.

You need Rust 1.95, cargo-near, Node 26 and pnpm 10. The first two commands cost nothing. Testnet steps also need near-cli-rs 0.30.1.

  1. make prove

    Attack cases and property tests, with the race's block heights.

  2. make test

    Every unit, property and sandbox suite.

  3. OPERATOR_ACCOUNT_ID=<you>.testnet make deploy-testnet

    Contract and crew on testnet, from a funded near-cli account.

  4. ./scripts/demo.sh

    Seven outcomes: six live on testnet, one labeled sandbox replay.

  5. make board

    The operator board, with the live invariant and the Revoke control.