Receipts
A receipt is a signed record of one run: which agent ran, what it cost, and a SHA-256 hash of the run’s journal. Because the signature covers that hash, editing the event log after the fact makes the receipt fail verification. You issue a receipt for a run you own, and anyone can check it with the deployment’s published public key. Use receipts when an auditor or a customer needs evidence that does not depend on trusting your dashboard.
1. A run has finished
A receipt is requested for the run, scoped to the caller's tenant.
- Control plane
- Outside PAL
- Data stores
Issue a receipt
Section titled “Issue a receipt”# Issue and persist a receipt for a run ($TOKEN is a JWT or API key with runs:write)curl -X POST http://localhost:8080/v1/runs/$RUN_ID/receipt \ -H "Authorization: Bearer $TOKEN"The response is {payload, signature, algorithm}. The receipt is stored with the run, and issuing again for the same run replaces the stored one with a freshly signed receipt. Issuing is not refused for a run that has not finished: status records the state at issuance, so issue after the run is terminal.
What the signature covers
Section titled “What the signature covers”The signature covers the canonical JSON of the payload: compact, with keys sorted alphabetically, so any JSON library that sorts keys reproduces the bytes.
| Field | Meaning |
|---|---|
runId, tenantId, agentName |
Always present |
agentVersion |
Digest of the agent’s current version; omitted when blank |
status |
The run’s status at issuance |
costUsd, tokensIn, tokensOut |
Accumulated cost and token counts |
journalHash |
SHA-256 over the run’s journal_events rows in seq order, each hashed as seq|kind|payload plus a newline |
issuedAt |
RFC 3339 timestamp |
version |
Schema version, currently 1 |
softwareMeasurement |
Host-asserted launch measurement of the microVM; omitted for runs without one (Identity and secrets) |
confidentialCompute |
Present only for confidential runs, with attested: false: the measurement is recorded, not verified |
model and provider are reserved and currently always omitted. The journal is the record that durable execution replays on recovery, so one record backs both crash safety and provenance.
Signing algorithms
Section titled “Signing algorithms”| Algorithm | When | Who can verify |
|---|---|---|
| Ed25519 | PAL_RECEIPT_ED25519_SEED is set (32 bytes, base64 or hex) |
Anyone with the published public key |
| HMAC-SHA256 | The seed is unset, or invalid (the control plane logs the error and falls back) | Only the deployment that holds the secret |
Set the Ed25519 seed in any real deployment. The HMAC fallback is for dev.
Verify a receipt
Section titled “Verify a receipt”The signing algorithm, key fingerprint and, for Ed25519, the public key are published without auth:
# Public key and fingerprint for offline verificationcurl http://localhost:8080/.well-known/pal-receiptsYou have three ways to check a receipt:
- The
/proofpage. The dashboard’s public page needs no sign-in. Paste the receipt. - The API.
POST /v1/runs/receipts/verifytakes the receipt body and needs no auth. It returns{valid: true, runId, issuedAt, tenantId}or{valid: false, reason}. - Locally. Fetch the public key once, canonicalize the payload, and run
ed25519.Verify. This does not call your control plane.
When the run exists on the server, the verify endpoint compares the signature and the full payload against what it issued, so an edited costUsd or status carrying the original signature fails.
Full request and response shapes: REST API.