Receipts
ed25519 signatures + Merkle root on Solana devnet every five actions. How to verify independently.
A receipt is a small signed JSON blob that proves a specific action happened with specific parameters on a specific account at a specific time.
Shape
Two layers: an inner ReceiptPayload (what gets signed) and an outer SignedReceipt envelope (the signature + the pubkey beside it).
{
"receipt": {
"receipt_reference": "rcpt_cal_1700000000000_abcd1234",
"timestamp": "2026-04-17T09:15:22.000Z",
"user_id": "did:privy:...",
"agent_id": null,
"tool": "calendar",
"action": "create",
"params_hash": "sha256-...",
"result_hash": "sha256-...",
"invoice_reference": "inv_cal_...",
"amount_usdc": 0.01,
"chain": "solana-devnet",
"payment_tx": "devnet-sim"
},
"signature": "<base64 ed25519 over canonical JSON of receipt>",
"public_key": "<base64 raw 32-byte ed25519 pubkey>"
}Verifying a receipt
- Fetch the public key from /api/receipts/public-key (returns the same base64 32-byte pubkey as the
public_keyfield). - Canonicalise the inner
receiptobject (sort keys, no whitespace). Don't includesignatureorpublic_key— only the inner payload. - Verify the ed25519 signature of the canonical JSON with that public key. The
signaturefield is base64-encoded. - If the signature checks, the receipt is authentic. If it doesn't, we faked it and you caught us.
Independent verification via two storage networks
Each receipt row on /demo/hackathon renders two independent public-archive links so a single provider outage cannot break verification:
filecoin: <cid>via the cron at/api/cron/filecoin-anchor(Lighthouse provider). Click it to fetch the sameSignedReceiptJSON athttps://<cid>.ipfs.dweb.link.0g: <rootHash>via the sister cron at/api/cron/og-anchor(0G Storage testnet, Turbo indexer). Click it to land on/api/og/storage/[rootHash], our public verifier passthrough returning a JSON envelope with the rootHash + indexer endpoint + verification instructions.
The bytes on each network are byte-identical to the bytes our server signed; if either doesn't match what /api/receipts returns, something has been tampered with. Both networks are provenance, not the source of truth. The ed25519 signature in step 3 above is what proves authenticity. The two mirrors prove the bytes are public + immutable + independently retrievable from whichever network is up.
Optional on-chain agent identity (ERC-7857)
Each agent has an additional identity surface: an ERC-7857 Intelligent NFT on 0G Galileo Testnet (0G AgenticID standard). The agent JSON at /agents/calendar.json exposes an optional og_agent_id field whose explorer_url points at the token on chainscan-galileo.0g.ai once a tokenId has been minted. The on-chain IntelligentData[] array carries SHA-256 hashes of the agent's name, description, capabilities, system prompt, and model. Until the operator funds the mint wallet at https://faucet.0g.ai and runs scripts/og-agent-id-mint.mjs, the field is omitted from the JSON: the deploy never claims a tokenId it does not have.
Merkle root and Solana devnet
Every five receipts, the server computes a Merkle root and publishes it via our Anchor publish_root program on Solana devnet. That gives you a public commitment that makes silently-rewriting history detectable. See the audit-trail post for the full pipeline.