Proving a Verdict Exists Is Easy. Proving Nothing Was Deleted Is Not.
Existence proofs don't cover completeness. AgentBadge now chains every verdict (keccak256(prev ‖ artifactHash)) and anchors the head to the Arc Memo contract on a heartbeat — with a public proof API any third party can fold offline.
Per-verdict anchoring proves each verdict exists — but not that the history is complete. AgentBadge adds a linear verdict hash-chain plus a heartbeat head-anchor on the Arc Memo contract: edit/delete/insert all break the fold, empty windows still re-anchor (epochSeq+1), and anyone can verify inclusion offline via /api/eaas/chain/proof/:verdictId — no trust in our API required.
A signed verdict lands in a hash, the hash lands on-chain — and a week later that same verdict still verifies. That's the easy part. The question nobody asks a paid evaluator is the uncomfortable one: how do you prove you didn't quietly drop the verdicts that made your score look bad? Existence proofs don't cover completeness. An archive can be honest about everything it shows you while lying about what it removed.
This week we closed that hole on AgentBadge's evaluation layer: every verdict now extends a linear hash-chain, and the chain's head is anchored to the Arc Memo contract on a heartbeat — even when nothing new ships. Anyone with a curl can recompute the head themselves and compare it to what's on-chain. No trust in our API required.
This is article 11 in the Arc Campaign series — the transparency counterpart to C10, which proved who paid. This one proves what was said.

Why Isn't Per-Verdict Anchoring Enough?
Because it answers the wrong question. Each verdict gets a memo anchor — memoId = keccak("eaas:verdictId") with the artifact hash on-chain. Point at a verdict, check the anchor, done. But anchoring proves each entry independently. Delete entry 47 of 200 and every remaining anchor is still valid. The proof set has a hole shaped exactly like the verdict you deleted.
This is the same gap certificate-transparency ran into: RFC 6962's original design proved inclusion, not consistency — Microsoft's ADR-0017 rewrite added signed tree heads precisely because "the log showed me the cert" is not "the log shows everyone the same history." An evaluator without consistency guarantees is a marketing claim, not an evidence source.

How Does a Linear Hash-Chain Fix Deletion?
Every verdict append writes a ChainEntry: entryHash = keccak256(prevHash ‖ artifactHash) — each entry cryptically married to its predecessor, pinned to a genesis hash. Edit an entry's content, its hash breaks. Delete an entry, the next one's prevHash dangles. Insert one, the sequence number and link both fail. The whole chain verifies in one O(n) fold, and verifyChain reports the exact first broken sequence — not just "invalid", but where.
Our store is a JSON file, deliberately. The threat model isn't a hardened database — it's that any replay of history diverges from the anchored head. Mutability accepted; detection guaranteed.

What's the Heartbeat Anchor For?
Liveness. A chain that only anchors when it has new work silently downgrades during quiet periods — no anchor means "unknown", not "unchanged". So createChainFlusher anchors the head every ARC_CHAIN_FLUSH_MS (default 24h), new entries or not:
- New entries → anchor the fresh head.
- Empty window → re-anchor the same headHash with
epochSeq + 1. Heartbeat, RFC 6962's signed-tree-head pattern: the log asserts "my state at time T is this" so an operator can't fork and serve different views to different auditors. - Restart after a missed window → immediate catch-up flush.
Each anchor is one memo(self, 0x, memoId, memoData) call — memoId = keccak("chain:eaas-verdicts:<epoch>"), memoData the ABI-encoded (headHash, count, prevAnchoredHeadHash). O(1) on-chain cost per window regardless of verdict volume. The memo namespace is deliberately split: eaas: per verdict, chain: per epoch — no collision, independently auditable.

What Can a Third Party Verify Without Trusting Us?
Everything, via three free rate-limited endpoints — an RFC 9162-inspired proof surface:
GET /api/eaas/chain→{domain, headHash, count, lastFlushAt, chainOk, anchor:{epochSeq, txHash, blockNumber, explorerUrl}, chainId}GET /api/eaas/chain/entries?from&to&limit→ pagedChainEntry[](cap 100)GET /api/eaas/chain/proof/:verdictId→{seq, path, head}— inclusion suffixGET /api/eaas/verdicts/:id/verify→ gainschain:{included, seq?, headHash}
The audit recipe, no SDK, no account:
# 1. pull the inclusion proof
curl https://agentbadge.xyz/api/eaas/chain/proof/0x<verdictId>
# 2. fold it yourself: h = path[0].prevHash;
# for each entry: h = keccak256(h ‖ e.artifactHash)
# 3. compare with the on-chain Memo event for
# memoId = keccak("chain:eaas-verdicts:<epochSeq>")
If your fold hits the anchored headHash, the verdict is provably inside the history the operator attested — at epoch time, on a public chain. If the operator serves you a rewritten store, the fold lands on a different hash than the anchor and the lie is arithmetic, not opinion.

How Is This Different from Rekor or Predge?
Rekor-style transparency logs anchor software supply chain artifacts with Merkle trees — a general log for anyone's blobs. Predge-style agent chains attest signed calls: proof that a proxy relayed a request/response pair. Ours chains verdicts — the semantic evaluation artifact itself — inside the same API that issues them, and ships a public proof surface rather than a query console. Three differences that matter:
- Granularity: one entry per priced verdict, not per HTTP call.
- Completeness story: heartbeat epochs +
chainOkexpose deletion and silent windows, which call-level attestation can't. - Verification cost: linear fold beats Merkle when proofs are suffixes — a verifier needs entries after the target, nothing before, and no tree bookkeeping.
The trade is real: linear scans are O(n) not O(log n). For an append-heavy verdict log where audits fetch suffixes, that's the right-sized data structure — not a generic log bolted on.
Try It
Buy a verdict for a cent: bun run examples/eaas-client.ts --endpoint https://agentbadge.xyz --policy deliverable-present. Then pull its proof and fold it — the arithmetic either matches the chain head or it doesn't. The code: hackathon/server/src/server/lib/eaas/chain*.ts, and the dogfood: scripts/eaas-chain-dogfood.mts (3 verdicts → force flush → real Memo event on Arc testnet).
Don't certify. Measure.
Series: ← C10 — payer binding | Index | C12 keyless signer →