Why your agents need receipts.

Your AI agent made 10 tool calls today. You logged them. Tomorrow, a customer asks what happened. You show them the log.

But you wrote the log. You could have changed it. They know that. So do regulators.

The scenario

An agent processes a payment. The operator realizes the amount was wrong and quietly edits the log to cover it.

Without receipts: nobody notices
[14:01] agent: search("vendor payment terms") OK
[14:02] agent: read("/invoices/q3.csv") OK
[14:03] agent: transfer(amount=5000500, to="vendor") OK
[14:04] agent: send_email("cfo@acme.com", "Payment confirmed") OK

The operator changed $5,000 to $500 in the log. The log still looks valid. Nobody can tell it was modified. The customer sees a clean record.

With receipts: the chain breaks
[14:01] receipt: search("vendor payment terms") VALID ref=a3f7c2...
[14:02] receipt: read("/invoices/q3.csv") VALID ref=b2e891...
[14:03] receipt: transfer(amount=500, to="vendor") INVALID - action_ref mismatch
       stored ref: d4c019... recomputed ref: f7a2b3... TAMPER DETECTED
[14:04] receipt: send_email("cfo@acme.com") INVALID - chain broken at position 3

The operator changed the amount, but the receipt's action_ref was computed from the original fields. Recomputing from the modified fields produces a different hash. The signature also fails. Two independent catches. The chain breaks at position 3, which also invalidates everything after it.

How it works

Every action gets an identifier derived from its content:

action_ref = SHA-256(JCS({agent_id, action_type, scope, timestamp_ms}))

Change any field, the identifier changes. The receipt is Ed25519 signed, so you can't forge a new signature. And each receipt links to the previous one, so tampering with one breaks the whole chain.

Any third party can verify offline with just the public key. No API call. No trust in the operator.

Try it live

The API is running right now. Click the button to see a real tamper detection.

Add receipts in one line

pip install nobulex
from nobulex import track

@track(agent_id="my-agent")
def transfer(amount, to):
    return payment_api.send(amount, to)

# Every call now produces a signed receipt.
# Exceptions produce a DENY receipt.
# Trust score accumulates automatically.

One real agent. Five minutes.

Install the SDK. Add @track to one function. Run your agent. Look at the receipts. That's it.

GitHub