Your agent just made a $40,000 decision — rebalanced a treasury, accepted a settlement, signed an offer letter. The inference is gone. The reasoning is gone. All you've got is a JSON blob from a model that won't remember it tomorrow.
Here's the thing: a wallet balance alone tells you nothing about what the agent thought when it acted. That's the gap EVIDIQ Notary closes. We don't keep your model output. We hash it, anchor it, and let you prove it later.
This post is a deep-dive on the part most teams gloss over — the AI output notarization layer itself, and specifically why we picked 0G Storage over Postgres, S3, or IPFS-with-a-pinbot.
What 0G Storage Anchoring Means for AI Outputs
AI output notarization is the practice of hashing a canonical inference receipt and writing its root to decentralized storage so the artifact is tamper-evident and durable. EVIDIQ Notary implements this by uploading canonical JSON receipts to 0G Storage Aristotle — mainnet, chain id 16661 — and returning a verifiable on-chain transaction hash anyone with the hash can re-fetch and re-verify.
Three properties matter, and a database can give you exactly zero of them:
- Immutability — the receipt can't be edited without invalidating the merkle root on-chain.
- Public verifiability — anyone holding the tx hash can re-fetch the bytes and recompute the digest.
- Censorship resistance — no single operator can quietly delete the row when the audit lands.
We chose 0G Storage for one reason that trumps the others: it returns an actual on-chain transaction. IPFS gives you a CID, which is great until the last pinner drops. A database gives you a row id, which means nothing to a counterparty six months from now. A 0G Storage upload returns a storageTx that lives on Aristotle mainnet forever. That's the receipt your auditor, your regulator, or your opposing counsel will actually accept.
The EVIDIQ team treats the anchor as a public good, not a competitive moat. Anyone can run the same flow, which is exactly the point — if our notarization wasn't independently reconstructable, it wouldn't be notarization, it'd be marketing. Done right, AI output notarization is a primitive the whole ecosystem can build on. Done wrong, it's a SaaS dashboard with a "verified" badge.
The Upload Flow: ZgFile to 0G Mainnet

Here's the actual pipeline a single notarize_inference call walks through. We built it to be boring on purpose — boring is debuggable.
- The caller hands us a receipt object — agent id, prompt hash, model identifier, response digest, timestamp, optional metadata.
- We canonicalize it. No key reordering, no whitespace games. Same input, same bytes, same hash, every time.
- We hash the canonical bytes with keccak256 — the same primitive Ethereum uses, because that's what the verification side will speak.
- We write the bytes to a temp file and wrap it in
ZgFile.fromFilePath(...)from the@0gfoundation/0g-ts-sdk. - We call
indexer.upload(zgFile)with a 30-second timeout budget. The SDK writes to a turbo indexer by default; if it's saturated, it falls back to a slower path that still lands on mainnet. - The indexer returns two values: a
storageRoot(the merkle root of the chunk tree) and astorageTx(the on-chain transaction committing that root). - We sign the canonical hash with the EVIDIQ notary key (EIP-191) and return
{ root, tx, signature, hash, timestamp }.
Step 6 is where most teams get lazy and ship "we uploaded it" as the answer. We don't. We surface the merkle root separately from the tx hash because they're not the same thing — the root is what you recompute from the bytes, the tx is what you look up on-chain to find the commitment. Conflating them is how you end up with "verified" badges that mean nothing.
The 30-second budget is deliberate. We've watched production runs hit indexer backpressure during mainnet spikes; we cap, retry once with a different indexer, and surface a clear error rather than hanging the caller. A notary that silently takes 90 seconds isn't a notary, it's a hostage situation.
This is the same anchor layer that backs every EVIDIQ trust report. When the scoring layer says "proceed" or "caution" about an agent, the underlying receipt — what the agent claimed, what it was probed for, what its endpoint returned — is committed with this exact flow. The full AI output notarization path runs the same way whether you're notarizing one inference or a thousand.
If you want the SDK call in real shape rather than prose, the EVIDIQ Notary docs have the TypeScript snippet.
Why Decentralized Storage Beats a Database for Audit Trails
A centralized audit log is a polite fiction. It exists on hardware someone controls, behind credentials someone can change, under a backup policy someone can disable. Every one of those someones is a counterparty risk.
Compare the two honestly.
A traditional audit log — Postgres with append_only=true and a paranoid DBA — gives you:
- Fast queries ✓
- Easy redaction ✓ (also: easy un-redaction)
- Single-tenant simplicity ✓
- Multi-tenant durability ✗ — depends on the vendor's SLA
- Independent verification ✗ — the database admin is the source of truth
- Censorship resistance ✗ — one court order and the row is gone
A 0G Storage anchor gives you:
- Independent verification ✓ — anyone with the tx hash re-fetches from a public node
- Censorship resistance ✓ — Aristotle mainnet, no admin override
- Permanent storage ✓ — the data lives as long as the chain does
- Cheap queries ✗ — you fetch by tx, not by
SELECT * WHERE agent_id = ? - Easy redaction ✗ — by design; redaction would defeat the point
- Fast queries ✗ — but you shouldn't be running hot-path reads against an audit anchor anyway
The trade-off is real. We're not pretending it isn't. You index your receipts in your own database for fast lookup. The 0G anchor is the slow, public, permanent copy — the one you point at when the disagreement happens. Hot-path read against a notarization layer is a category error; it's a witness, not a cache.
This is also why we don't store the raw model output on 0G Storage by default. The receipt contains the prompt hash and response digest. The original bytes stay with the agent unless the caller explicitly opts in. Less data on-chain means smaller blast radius if our indexer ever leaks.
If you're running compliance-sensitive workloads — finance, healthcare, anything where the auditor's letter matters — pair this with EVIDIQ Sentinel docs for live capability probing and you've got a real audit story, not a vibes-based one. Sentinel probes the endpoint; Notary anchors the proof. Together they close the loop.
Six Tools, Four Free — the Notary MCP Surface

The notary ships as a remote MCP server. Six tools, four free, two gated by x402. The split is deliberate — read paths are open, write paths that consume storage and bandwidth cost money.
The free tools:
get_receipt(hash)— fetch a previously notarized receipt by its canonical hash. No payment, no rate limit beyond the obvious.verify_attestation(hash, signature)— recompute keccak256 over the canonical bytes and recover the EIP-191 signer. Pure cryptography, no state.notary_stats()— aggregate counters: total receipts, total bytes anchored, uptime, current indexer.notary_pubkey()— return the EVIDIQ notary signing key, so verifiers don't have to hardcode it.
The paid tools (x402, paid per call in USDT0 on X Layer):
notarize_inference(receipt)— the upload flow above. One inference, one receipt, one tx.notarize_batch(receipts[])— the same flow, batched. Cheaper per-call, same anchoring guarantees.
The payment challenge follows x402 v2 — the server returns 402 Payment Required with accepts[].amount quoted in USDT0, the client signs an EIP-3009 transferWithAuthorization, and we settle on X Layer before the upload starts. We never hold the funds; the payment is atomic with the call. If you've used the main EVIDIQ verify_agent tool, the shape is identical.
Two design choices worth calling out:
- Verification is free on purpose. We make more money when trust is cheap to check. If verifying cost money, nobody would verify. AI output notarization only matters if verification is frictionless.
- Batch is the right default for production. If you're notarizing one inference per user request, fine. If you're an inference gateway doing 10K calls/minute,
notarize_batchis the only economically sane option.
For agent frameworks wiring this up automatically — LangChain callbacks, Vercel AI SDK middleware, the usual suspects — see the EVIDIQ docs install section. The skill itself is at evidiq.dev/skill.md.
If you're operating the notary side rather than consuming it — running your own EVIDIQ Notary node, rotating keys, or wiring your own indexer — the EVIDIQ Operator docs cover that path. We wrote them after watching three customers ask the same questions in a row.
