<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Edwin</title>
    <description>The latest articles on DEV Community by Edwin (@edwin1233).</description>
    <link>https://dev.to/edwin1233</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4246298%2F8d281226-c0ea-4587-b42e-1dd1a723a78b.jpg</url>
      <title>DEV Community: Edwin</title>
      <link>https://dev.to/edwin1233</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edwin1233"/>
    <language>en</language>
    <item>
      <title>Building a Proof Gatekeeper: an agent that cannot lie, because the math won't let it</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Sun, 15 Nov 2026 17:23:44 +0000</pubDate>
      <link>https://dev.to/edwin1233/building-a-proof-gatekeeper-an-agent-that-cannot-lie-because-the-math-wont-let-it-2ml4</link>
      <guid>https://dev.to/edwin1233/building-a-proof-gatekeeper-an-agent-that-cannot-lie-because-the-math-wont-let-it-2ml4</guid>
      <description>&lt;p&gt;&lt;em&gt;My build log for zkVerify Agent Games, Season 01 — Proof Gatekeeper track. #AgentGames&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The assignment
&lt;/h2&gt;

&lt;p&gt;In the Proof Gatekeeper event of &lt;a href="https://zkverify.io" rel="noopener noreferrer"&gt;zkVerify&lt;/a&gt;'s Agent Games, your agent receives proof-backed payment requests and has to answer one question per request: &lt;strong&gt;APPROVE or REJECT?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every request carries a zero-knowledge proof and a set of public claims: a verification key, a balance, a minimum threshold, an expiry block, a recipient, an amount, a transfer cap. Some requests are valid. The rest are traps — a substituted verification key, a reused proof, a balance below the threshold, an expired condition, a recipient mismatch, an amount over the cap.&lt;/p&gt;

&lt;p&gt;Scoring is asymmetric on purpose: a correct answer earns points, a wrong &lt;em&gt;reject&lt;/em&gt; costs a little, and a wrong &lt;em&gt;approve&lt;/em&gt; costs double. That is exactly how a real gatekeeper should be incentivised — letting a bad payment through is the expensive mistake.&lt;/p&gt;

&lt;p&gt;The part that makes this event different from a normal "build a classifier" challenge: &lt;strong&gt;your agent must attach a Groth16 proof of its own decision&lt;/strong&gt;, generated with the event's official Circom circuit, and the platform re-verifies that proof against the registered verification key. You don't just claim you checked — you prove it, per task, in under 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The circuit is the referee
&lt;/h2&gt;

&lt;p&gt;The most interesting discovery of this build: the circuit enforces honesty in both directions.&lt;/p&gt;

&lt;p&gt;While testing, I tried to generate a proof for a &lt;em&gt;dishonest&lt;/em&gt; approve — a request whose proof key had been substituted, answered APPROVE anyway. Witness generation itself failed, deep inside the circuit's constraints. The same happened for a dishonest reject of a fully valid request. The circuit's output is a binding hash over the decision and the task, and the constraints are only satisfied when the decision equals the honest evaluation of the inputs.&lt;/p&gt;

&lt;p&gt;In other words: &lt;strong&gt;you cannot produce the proof for a lie.&lt;/strong&gt; The agent's decision logic and the proof are not two separate steps that might drift apart — the proof only exists if the decision is the correct function of the inputs. One trap class falls out of this design for free: a reused proof can never be honestly approved &lt;em&gt;or&lt;/em&gt; rejected with a proof, so the correct play there is a reject with no proof attached — which is precisely what the rules reward.&lt;/p&gt;

&lt;p&gt;This is what proof verification infrastructure like zkVerify's is for: not trusting the agent, not trusting the operator — verifying the statement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checks, in order
&lt;/h2&gt;

&lt;p&gt;My agent evaluates deterministically, cheapest and most decisive checks first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;VK_SUBSTITUTION&lt;/strong&gt; — the proof's verification key differs from the registered key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PROOF_REUSED&lt;/strong&gt; — this proof reference was already submitted (kept in a persistent seen-set; the same task retried by the platform is idempotent, not "reuse").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;THRESHOLD_MISMATCH&lt;/strong&gt; — public balance below the required minimum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EXPIRED_CONDITION&lt;/strong&gt; — current block past the expiry block.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RECIPIENT_MISMATCH&lt;/strong&gt; — recipient differs from the condition's recipient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AMOUNT_OVERFLOW&lt;/strong&gt; — amount above the maximum transfer.&lt;/li&gt;
&lt;li&gt;Otherwise &lt;strong&gt;APPROVE&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every field is encoded as a BN128 field element exactly as the circuit expects — verification-key hashes reduced modulo the field prime, non-numeric task IDs hashed into the field, a deterministic per-task salt. The public signals come out in the circuit's order: binding hash, registered VK hash, proof VK hash, minimum, balance, expiry, current block, recipient, condition recipient, amount, cap, decision, task ID.&lt;/p&gt;

&lt;h2&gt;
  
  
  Engineering notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transport&lt;/strong&gt;: the platform signs every request with HMAC-SHA256 over the raw body; the agent verifies before parsing, and signs every response the same way. Task IDs double as idempotency keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proving&lt;/strong&gt;: snarkjs with the official &lt;code&gt;gatekeeper.wasm&lt;/code&gt; + &lt;code&gt;gatekeeper_final.zkey&lt;/code&gt;, running in a serverless function (Node on Vercel). A proof takes about a second — the 60-second task deadline is generous once the artifacts are bundled with the function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State&lt;/strong&gt;: the proof-reuse seen-set lives behind a small store interface — an Upstash REST set when configured, an in-process map otherwise (with a loud warning, because serverless instances forget).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;: 69 local checks before deploying — preflight handshake, tampered-body rejection (401), the valid approve, and all six traps, each with a locally verifying proof.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  First contact with the platform
&lt;/h2&gt;

&lt;p&gt;Registration generates a one-time HMAC secret, which goes into the deployment's environment — never into the repo. Then the moment of truth: a live practice dispatch from the platform itself.&lt;/p&gt;

&lt;p&gt;The practice task was a VK-substitution trap. The agent answered &lt;strong&gt;REJECT / VK_SUBSTITUTION&lt;/strong&gt;, the platform marked the attached proof &lt;strong&gt;VERIFIED&lt;/strong&gt;, round-trip latency &lt;strong&gt;893 ms&lt;/strong&gt;. Everything I had only been able to test locally — envelope shape, signatures, field encodings, proof format — held up against the real verifier.&lt;/p&gt;

&lt;p&gt;Practice proofs stay off-chain; competition tasks generate on-chain attestations by the platform, which is the zkVerify pattern end to end: prove off-chain, verify and attest on-chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Scored rounds run 16–20 November. The agent stays online around the clock; five rounds remain, 8 tasks each. After that: results, a post-mortem, and — win or lose — a public repo that anyone can audit:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/edwin1233/proof-gatekeeper-agent" rel="noopener noreferrer"&gt;https://github.com/edwin1233/proof-gatekeeper-agent&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Agent:&lt;/strong&gt; "Edwin Gatekeeper", Proof Gatekeeper track, zkVerify Agent Games Season 01&lt;/p&gt;

&lt;p&gt;May the most honest gatekeeper win.&lt;/p&gt;

&lt;h1&gt;
  
  
  AgentGames — @ZKVProtocol
&lt;/h1&gt;

</description>
      <category>zkverify</category>
      <category>agentgames</category>
      <category>zeroknowledge</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
