<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>QED Proof blog</title>
    <link>https://qedproof.site/blog/</link>
    <atom:link href="https://qedproof.site/blog/feed.xml" rel="self" type="application/rss+xml" />
    <description>Notes on verifying what AI agents actually did.</description>
    <language>en</language>
    <item>
      <title>Your agent says it's done. That isn't evidence.</title>
      <link>https://qedproof.site/blog/agents-say-done-is-not-evidence/</link>
      <guid isPermaLink="true">https://qedproof.site/blog/agents-say-done-is-not-evidence/</guid>
      <pubDate>Sat, 26 Sep 2026 12:00:00 GMT</pubDate>
      <description>An AI agent's success report is the one source that can't settle whether the work landed. Why QED Proof reads the destination instead, and why it fails toward &quot;Couldn't check&quot;.</description>
      <content:encoded><![CDATA[<p>Every AI agent that does real work ends the same way: it tells you it finished. <em>Pushed to main. Posted the update. Raised the budget. Deployed.</em></p>
<p>That sentence is doing a great deal of load-bearing work. Dashboards count it. Alerts stay quiet because of it. Some teams now pay agents, or pay <em>for</em> agents, on the strength of it. And it comes from the one party with no way to be independent about the answer: the agent itself.</p>
<h2>Self-report fails in boring ways</h2>
<p>The failures that matter aren&#39;t dramatic. They&#39;re the ones where the agent is sincerely wrong:</p>
<ul>
<li>It committed to a local branch and the push failed on authentication. The log line says <code>pushed</code>.</li>
<li>The CI run it waited on was a different commit&#39;s.</li>
<li>The post API returned 200 and the platform rejected the media afterwards.</li>
<li>It edited the budget on the wrong campaign.</li>
</ul>
<p>In each case the agent&#39;s own trace looks like success, because the trace records what the agent <em>did and believed</em>, not what the destination <em>holds</em>. Tracing and observability tools are genuinely useful — they just answer a different question. They tell you what your agent said. They can&#39;t tell you whether it was true.</p>
<h2>The only record that settles it</h2>
<p>Whether a commit is on <code>main</code> is a fact about GitHub, not about the agent. Whether a URL answers is a fact about the URL. So QED Proof has one rule it never bends: <strong>evidence comes from the destination, never from the agent&#39;s report.</strong></p>
<p>When a claim arrives, QED Proof waits until the work should be visible, then reads the destination itself over access you grant read-only. It compares what it finds with what was claimed and decides one of five verdicts: <strong>Verified</strong>, <strong>Failed</strong>, <strong>Mismatch</strong>, <strong>Late</strong>, or <strong>Couldn&#39;t check</strong> — the five <a href="/glossary/verdict/">verdicts</a>.</p>
<h2>Failing in the safe direction</h2>
<p>The last verdict is the most important design decision in the product. A verifier can fail for reasons that say nothing about the agent: a timeout, a revoked permission, an action nobody has built a verifier for yet. In every one of those cases the verdict is <strong>Couldn&#39;t check</strong> — never Verified.</p>
<p>A trust product that passes something that didn&#39;t happen is finished. One that occasionally says &quot;I couldn&#39;t see&quot; is merely honest. So the code is written so that there is no path from an error to a green tick, and &quot;Couldn&#39;t check&quot; is shown as QED Proof&#39;s limitation, not the agent&#39;s failure.</p>
<h2>Then make the verdict hold on its own</h2>
<p>A verdict you have to take our word for would just move the trust problem one step along. So every verdict becomes a receipt: signed with Ed25519, appended to a Merkle log that can&#39;t be edited, and anchored on-chain. Anyone holding the receipt can check all three with the open-source checker and a public RPC — no QED Proof account, no QED Proof API. <a href="/blog/check-a-receipt-without-trusting-us/">How that check works</a> is a post of its own.</p>
<h2>What this covers today</h2>
<p>QED Proof verifies GitHub (a commit is on the branch, a pull request is open, CI passed on a commit), X posts, Slack messages and any public URL today. Meta and GitLab connect read-only now, with their verifiers next. The <a href="/integrations/">integrations page</a> keeps that list honest, because a page about false &quot;done&quot; claims shouldn&#39;t make any.</p>
]]></content:encoded>
    </item>
    <item>
      <title>How to check a QED Proof receipt without trusting us</title>
      <link>https://qedproof.site/blog/check-a-receipt-without-trusting-us/</link>
      <guid isPermaLink="true">https://qedproof.site/blog/check-a-receipt-without-trusting-us/</guid>
      <pubDate>Sat, 26 Sep 2026 12:00:00 GMT</pubDate>
      <description>A QED Proof receipt carries three independent proofs — a signature, a log inclusion proof and an on-chain anchor. Here is what each one proves, and how to check all three yourself.</description>
      <content:encoded><![CDATA[<p>If you have to trust the verifier, you haven&#39;t removed trust — you&#39;ve moved it. So a QED Proof receipt is built to be checked by someone who has no account, no relationship with us, and every reason to be sceptical.</p>
<p>It carries three proofs. Each answers a different question, and each can be checked on its own.</p>
<h2>1. The signature: did QED Proof issue this, unchanged?</h2>
<p>The receipt is canonicalised — serialised to exactly one byte sequence — and signed with <a href="/glossary/ed25519/"><strong>Ed25519</strong></a>. The signing key lives in a cloud KMS and can&#39;t be exported. The public keys are published at <code>https://api.qedproof.site/.well-known/poaw-keys.json</code>.</p>
<p>Checking it proves two things: this receipt came from the key it names, and not one byte of it has changed since. Edit the verdict, the evidence or the timestamp and the signature stops verifying.</p>
<p>What it can&#39;t prove on its own: that we didn&#39;t quietly issue a <em>different</em> receipt for the same claim, or backdate one. That&#39;s what the next two proofs are for.</p>
<h2>2. Inclusion: is it really in the log?</h2>
<p>Every receipt becomes a leaf in an <a href="/glossary/append-only-log/"><strong>append-only Merkle log</strong></a>. The receipt comes with an <a href="/glossary/inclusion-proof/">inclusion proof</a>: the short list of sibling hashes that combine, level by level, from its leaf up to the log&#39;s root.</p>
<p>Recomputing that path proves the receipt is part of the log at a specific position. And because the log only ever appends, anything already in it can&#39;t be edited or dropped without changing every root after it.</p>
<h2>3. The anchor: when did that log exist?</h2>
<p>The log&#39;s root is published on-chain through the <a href="/glossary/ethereum-attestation-service/"><strong>Ethereum Attestation Service (EAS)</strong></a> on <strong>Base</strong>, every ten minutes. Once a root is in a block, it can&#39;t be moved.</p>
<p>Checking the anchor proves the log — and therefore your receipt — existed in that state no later than that block. Not even QED Proof can backdate it afterwards. (Today the log is anchored on Base Sepolia, Base&#39;s test network; mainnet is the next step.)</p>
<h2>Running the check</h2>
<p>The checker is part of <a href="/open-source/">Proof of Agent Work</a>, the open-source spec behind QED Proof. Point it at a receipt, the published keys and any public Base RPC:</p>
<pre><code>$ uv run check.py receipt.json poaw-keys.json \
    --rpc https://sepolia.base.org

signature   ✓
inclusion   ✓  leaf 8 of 9
anchor      ✓  block 47,309,207
trust level 2  proven by 21:38:22
</code></pre>
<p>Nothing in that path touches a QED Proof server. If we disappeared tomorrow, every receipt we had issued would still verify.</p>
<h2>What a receipt deliberately leaves out</h2>
<p>A receipt records fingerprints and the facts a verifier read — a commit SHA, an HTTP status — never the content itself. You can share a receipt link publicly without sharing your code, your post or your email. The proof is about what happened, not about what it said.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Pipelines: prove only what costs you money</title>
      <link>https://qedproof.site/blog/pipelines-prove-what-costs-you-money/</link>
      <guid isPermaLink="true">https://qedproof.site/blog/pipelines-prove-what-costs-you-money/</guid>
      <pubDate>Sat, 26 Sep 2026 12:00:00 GMT</pubDate>
      <description>Proving that an agent pushed a commit keeps nobody up at night. Proving that it changed an ad budget does. Pipelines — designed, not yet live — let you define which actions deserve a receipt.</description>
      <content:encoded><![CDATA[<p><em>Pipelines are designed and accepted, but not live yet. This post describes where the product is going, not what it does today.</em></p>
<p>QED Proof&#39;s first verifiers prove things we chose: a commit landed, a pull request opened, a URL answers. They&#39;re useful, and they&#39;re the easiest place to start. But the actions that actually cost a business money or trust are more specific than &quot;a commit&quot;, and they differ from team to team.</p>
<p>Take a marketer whose agent manages an ad account. They don&#39;t want a receipt for everything the agent touches. They want proof <strong>only when it changes a budget</strong>. They want to know the change really landed, at the value claimed. And above all, they want to hear about <strong>any budget change the agent didn&#39;t report</strong>.</p>
<h2>A pipeline is a rule, built from our parts</h2>
<p>A pipeline has five parts:</p>
<table>
<thead>
<tr>
<th>Part</th>
<th>What it is</th>
<th>For the budget example</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Connector</strong></td>
<td>a vetted, read-only connection to one destination</td>
<td>the Meta ad account, <code>ads_read</code> only</td>
</tr>
<tr>
<td><strong>Trigger</strong></td>
<td>the agent&#39;s claim, or a watch on the destination&#39;s own change history</td>
<td>both</td>
</tr>
<tr>
<td><strong>Filter</strong></td>
<td>conditions on the destination&#39;s own event fields</td>
<td>a budget change, on campaigns A or B, of $50 or more</td>
</tr>
<tr>
<td><strong>Check</strong></td>
<td>a published verifier profile, narrowed</td>
<td>the new budget matches the claim, made by the agent&#39;s app, within ten minutes</td>
</tr>
<tr>
<td><strong>Outcomes</strong></td>
<td>a receipt, and alerts where you already look</td>
<td>always a receipt; alert on a mismatch, a late change, or an unclaimed one</td>
</tr>
</tbody></table>
<p>You write it once — in a form, or as JSON through the API; one schema, two editors.</p>
<h2>Rules that keep it honest</h2>
<p>Letting people define their own proof is exactly where a verification product could go wrong, so pipelines keep the invariants that make a receipt worth anything:</p>
<ul>
<li><strong>Facts only ever come from the connector.</strong> No step can take a fact from the agent or from its claim. The claim says what to look for; the destination says what happened.</li>
<li><strong>Your conditions can only narrow a Verified, never create one.</strong> A condition that fails turns the verdict into a Mismatch. It can&#39;t turn a failed check into a pass.</li>
<li><strong>The rule is recorded in the receipt.</strong> The pipeline is hashed and its version travels with every verdict, so whoever holds the receipt can see exactly what was checked. Editing a pipeline makes a new version; old receipts keep the old one.</li>
<li><strong>No custom code, yet.</strong> Pipelines are assembled from our reviewed parts only.</li>
</ul>
<h2>The case logging never catches</h2>
<p>The most valuable output isn&#39;t a receipt for a claim at all. In watch mode, QED Proof reads the account&#39;s own change history and matches every change against open claims. A change inside your filter that <strong>no claim explains</strong> is an <a href="/glossary/unclaimed-change/">unclaimed change</a>: an agent that did something and didn&#39;t say so. It becomes a signed entry in the same log, and an alert.</p>
<p>That is the failure plain logging can never surface, because the agent that didn&#39;t mention the change didn&#39;t log it either.</p>
<h2>Where it starts</h2>
<p>The first connector is Meta Ads, where the account&#39;s activity log records budget and bid edits with their old and new values and the app that made them; Google Ads follows. Meta already connects read-only today — see <a href="/integrations/">integrations</a>. When pipelines ship, this post will say so at the top.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
