A model returns an answer. The user screenshots it. Six months later, in a dispute, both sides argue about what was actually said. Who produced it? Which version of the model? What was the exact prompt? Welcome to the audit problem nobody solved when they shipped their first agent.
EVIDIQ's Notary service is our answer. It's a tamper-evident record of every inference your agent runs — anchored on 0G Storage so it can't be quietly rewritten. This post walks through the actual upload flow, why a merkle root beats a database row, and how you call it today.
What 0G Storage Anchoring Means for AI Outputs
AI output notarization is the process of anchoring a signed receipt of an AI inference on decentralized storage so anyone can later prove what was produced, when, and by which model. EVIDIQ performs AI output notarization by hashing receipts, uploading them to 0G Storage Aristotle (chain 16661), and recording the on-chain transaction for tamper-evident auditability.
Numbers without anchoring are just promises. A receipt that lives only in your database can be edited, deleted, or quietly rewritten the moment a lawsuit lands. Anchoring on 0G Storage flips that — once the merkle root is on chain, the only way to "change" the record is to convince the network to reorg, which costs real money.
In practice, an EVIDIQ Notary receipt carries a small set of fields:
inference_id— a UUID the client suppliesmodel_idandmodel_version— what produced the outputinput_hashandoutput_hash— keccak256 of the canonicalized prompt and completionsigner_pubkey— the EVIDIQ key that countersigned the bundletimestamp— server-side, anchored at upload time
The EVIDIQ team picked 0G Storage Aristotle as the underlying layer for a specific reason: it's content-addressed, it's mainnet, and it returns a real on-chain transaction hash we can point auditors to. We needed durability measured in years, not quarters. A Postgres row gets purged when somebody forgets a backup cron. A merkle root on chain doesn't.
The Upload Flow: ZgFile to 0G Mainnet
[IMAGE_PROMPT: Modern flat illustration of a vertical pipeline flow diagram: a JSON document on the left being wrapped in a glowing ZgFile shard, then flowing through a stylized upload arrow into a hexagonal 0G Storage network node, and finally a transaction receipt card emerging on the right with a merkle tree icon. Soft purple and teal palette, developer/tech aesthetic, clean geometric shapes, subtle circuit lines in the background, no text, no real brand logos.]
Here's the part the docs gloss over — what actually happens between notarize_inference and the moment we hand you back a tx hash. We walk this path every time a request hits our Notary endpoint, and we wrote the SDK calls ourselves to keep the timeout budget tight.
The flow looks like this:
- We serialize the signed receipt JSON to a temp file under
/tmp/evidiq/receipts/. - We call
ZgFile.fromFilePath(path)from@0gfoundation/0g-ts-sdkto wrap it as a sharded upload. - We hand the
ZgFiletoindexer.upload(zgFile, { task: 1, feePerByte, tags })and wait. - On success, the indexer returns
{ storageRoot, storageTx }. We attach both to the receipt before signing. - If the indexer stalls, we fall back to the turbo indexer endpoint — same SDK, different URL — within a 30-second budget.
If we don't see a storageTx back inside that budget, the request fails loudly. A "notarized" record without a chain anchor isn't notarized — it's a JSON file lying about its provenance. We refuse to ship that.
The receipt is the merkle leaf. The on-chain storageRoot is the merkle root. Anyone holding the receipt can later call verify_attestation and we'll re-hash, re-verify the merkle proof against the root, and return the original tx hash if everything lines up. The whole point is that a third party — a regulator, a counterparty, a judge — can re-do the check themselves without trusting our database. Doing AI output notarization well means handing the verifier the keys to do exactly that.
Why Decentralized Storage Beats a Database for Audit Trails
Let's be blunt: a centralized audit log is a story you tell yourself until it isn't. If the dispute is large enough, somebody will subpoena the rows, somebody will produce a contradicting export from "the system", and the only thing your lawyer can promise is more depositions. A 0G Storage anchor changes the trust model.
Three concrete differences that matter when an auditor shows up:
- Immutability by cost, not by policy. Rewriting a Postgres row needs one admin token. Rewriting a 0G Storage root needs a chain reorg costing real gas, on a network with no shortcut.
- Public verifiability. Anyone with the tx hash can independently re-fetch the receipt and re-hash it. The EVIDIQ Notary docs describe the exact bytes a verifier should expect.
- No vendor lock for the evidence layer. If EVIDIQ disappears tomorrow, the receipts on 0G Storage still resolve. The same can't be said for rows inside our Postgres.
We built EVIDIQ's Notary around this principle on purpose. The EVIDIQ key countersigns the receipt, but it doesn't gate access to the underlying data — the chain does. A wallet balance alone tells you nothing about an agent's history, and a signed receipt that only we can read isn't much better. Decentralized anchoring closes that gap.
The practical workflow is: your agent calls notarize_inference, gets back the storageRoot and storageTx, stores those two strings wherever you keep your own audit metadata. Six months later, when somebody asks "did this output really come from that model?", you produce the tx hash. The auditor pulls the receipt, hashes it, checks the merkle proof, done.
Six Tools, Four Free — the Notary MCP Surface
[IMAGE_PROMPT: Modern flat illustration of a clean dashboard panel showing six rounded tool cards arranged in a 3x2 grid, each with a small icon (a stamp, a batched stack, a verification checkmark, a document, a stats bar chart, a public key). The four left cards glow green to indicate free, the right two cards glow amber for paid. Soft dark blue background with subtle grid lines, developer/tech aesthetic, no text labels, no real brand logos.]
EVIDIQ's Notary is exposed as an MCP server at evidiq.dev/mcp alongside the rest of our trust surface. The Notary contributes six tools, and — because we're allergic to surprise invoices — only two of them are paid.
Here's the full surface:
notarize_inference— submit a single receipt, get backstorageRoot+storageTx. Free.notarize_batch— submit up to 100 receipts in one call, with per-receipt success/failure returned. Free.verify_attestation— re-hash a receipt, re-check the merkle proof, return the original tx. Free.get_receipt— fetch a previously notarized receipt by inference id. Free.notary_stats— public counters (total notarizations, indexer used, last success). Free.notary_pubkey— returns the EVIDIQ signing public key so you can pre-verify. Free.
Wait — that's six free. The two paid tools are actually the general trust tools that sit next to Notary on the same MCP: verify_agent is the metered one (pay-per-call, x402, USDT0 on X Layer), and any tool that triggers a live endpoint probe piggybacks on it. Notary reads stay open so audit infrastructure isn't paywalled. That was a deliberate call on our end — we didn't want a dispute-resolution dependency to also be a billing dependency. The whole point of AI output notarization is that nobody has to ask us permission to read the record later.
If you want to see the tools in your editor, the EVIDIQ docs walk through installing the MCP server, and the EVIDIQ Sentinel docs explain how Notary receipts get attached to a verified trust report. For batch jobs and pipeline integration, the EVIDIQ Operator docs have a recipe for notarizing every inference your agent produces in a given window.
Worth noting: the receipt format is stable. We version it (receipt_v1) and we commit to keeping the same field names across breaking changes, so anything you store today will still verify in 2027 and beyond.