Public proof sandbox

Give an agent $100 and try to make it misbehave.

An AI agent gets a fake budget and attempts a real action. Some attempts go through. Some are blocked outright. Some stop and wait for you — and the agent cannot approve those itself, because the signing key lives in your browser where no agent can reach it.

Under the hood: a non-extractable holder key in your browser, a separate agent capability, then Cedar policy → budget reservation → audit ledger → AP2 emission → public verification.

How this works

  1. 1 An agent asks to spend. It wants to register a domain, send mail, or launch a service — and it has to ask, every time.
  2. 2 The governor answers: allow, block, or ask you. Within budget and policy, it proceeds. Over the line, it is refused. In between, it stops and waits for a human.
  3. 3 You get a receipt anyone can check. Every outcome is signed. Download it and verify it yourself, offline, without trusting this page.

A blocked attempt is a success here. You are watching the refusals work, not watching a demo succeed.

This is not a mock
The decisions are made by the same policy engine, the same budget arithmetic, the same append-only ledger and the same signing code that run the real product. One policy pack exists and this evaluates it.
What is faked
The money, and only the money. No provider is contacted, no domain is bought, no mail is sent. Sandbox signing keys and data are separate from production, so nothing here can touch a real account.

What happened

Proof timeline

Choose a scenario and run it. A blocked or safely failed attempt counts as a successful governed outcome.

  1. Allowed?
  2. Money set aside
  3. Written down
  4. Receipt signed
  5. Anyone can check

    Human decision required

    The agent has stopped. Only the browser-held holder key—not the agent capability—can sign this request digest and one-time nonce.

    HolderA non-extractable browser-held key signs the mandate and any holder decision.
    AgentA short-lived random capability can attempt only the selected fixed action.
    SproutPadA sandbox-only issuer signs fake-money AP2 and proof artifacts.

    This demonstrates the control flow. It does not represent real settlement, provider parity, uptime, or independent certification.

    Inspect raw signed artifact
    No artifact yet.
    Verify a downloaded proof independently

    Download the single-file Node 22 verifier. The API-served reference manifest binds its raw SHA-256; verify that hash before execution. It then uses only the proof and public keys. It needs no SproutPad account, repository checkout, package install, or database. Save both JWKS files with the proof for a fully offline check later.

    This artifact proves the presented fake payment and its signed AP2 chain. Cumulative cap enforcement comes from the runtime’s atomic reservation, ledger, and session state; one downloaded artifact is not proof of a complete external payment history.

    (
    set -eu
    d="$(mktemp -d)"
    trap 'rm -rf "$d"' EXIT
    curl --proto '=https' --tlsv1.2 -fsS -o "$d/manifest.json" \
      https://sproutpad.ai/.well-known/sandbox-reference.json
    curl --proto '=https' --tlsv1.2 -fsS -o "$d/verifier.mjs" \
      https://sproutpad.ai/verify-sandbox-proof.mjs
    expected="$(jq -er \
      'if .state == "available" then .verifierSha256 else .verification.verifierSha256 end' \
      "$d/manifest.json")"
    test "$(shasum -a 256 "$d/verifier.mjs" | awk '{print $1}')" = "$expected"
    node "$d/verifier.mjs" \
      ./sbp_…-SANDBOX-FAKE-MONEY.json \
      https://sproutpad.ai/.well-known/sandbox-proof-jwks.json \
      https://sproutpad.ai/.well-known/sandbox-jwks.json \
      --tamper-check
    )
    Stable shared reference

    Checking the published reference manifest…

    Inspect the reference manifest

    A browser is optional. The separate headless-holder recipe keeps its P-256 private key outside the agent process and uses the same mandate and gate digests. Holder-key control does not by itself prove human attention and cannot satisfy the human-separation conformance claim.

    Fast path: one request, simulated holder

    This calls the same fake-money policy, budget, ledger, compensation, and signed-proof engine, but the server controls an ephemeral holder key. The response explicitly says holderSeparation: false and humanDecisionClaim: not_asserted. It cannot demonstrate human approval; use the interactive or separate headless-holder flow below for that boundary.

    IDEMPOTENCY_KEY="guided-demo-$(openssl rand -hex 16)"
    curl -fsS -X POST https://api.sproutpad.ai/v1/sandbox/simulations \
      -H 'content-type: application/json' \
      -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
      --data '{"scenario":"simulated_failure"}' | jq

    Generate a fresh key for each different scenario or request body. Reuse the captured key only to retry this identical request after an ambiguous network failure.