<?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: Fauzi Darwis</title>
    <description>The latest articles on DEV Community by Fauzi Darwis (@elpapito29k).</description>
    <link>https://dev.to/elpapito29k</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%2F4245933%2Fecaa7dbc-2367-49dc-ac95-35d94d43710d.png</url>
      <title>DEV Community: Fauzi Darwis</title>
      <link>https://dev.to/elpapito29k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elpapito29k"/>
    <language>en</language>
    <item>
      <title>How I Built Gatekeeper-29k: A Verifiable Proof Gatekeeper Agent</title>
      <dc:creator>Fauzi Darwis</dc:creator>
      <pubDate>Sun, 15 Nov 2026 12:36:30 +0000</pubDate>
      <link>https://dev.to/elpapito29k/how-i-built-gatekeeper-29k-a-verifiable-proof-gatekeeper-agent-j18</link>
      <guid>https://dev.to/elpapito29k/how-i-built-gatekeeper-29k-a-verifiable-proof-gatekeeper-agent-j18</guid>
      <description>&lt;h1&gt;
  
  
  How I Built Gatekeeper-29k: A Verifiable Proof Gatekeeper Agent
&lt;/h1&gt;

&lt;p&gt;Agent Games by zkVerify is a competition where AI agents compete across four tracks for a 400,000 VFY prize pool. I entered the Proof Gatekeeper track, where an agent has to look at a task — a claimed zero-knowledge proof attached to a payment-like instruction — and decide, within 60 seconds, whether to APPROVE it or REJECT it. This is the story of building my agent, Gatekeeper-29k: the dead ends, the one bug that cost me an afternoon, and the honest limitation it still ships with.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Proof Gatekeeper actually decides
&lt;/h2&gt;

&lt;p&gt;Every round, the platform dispatches tasks to my agent's public endpoint. Each task carries a Groth16 proof and a set of conditions: who the payment goes to, how much, and under what threshold. My agent has to verify the claim and return a decision. The traps it has to catch are specific:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;VK substitution&lt;/strong&gt; — the verification key has been swapped for a fake one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reused proof&lt;/strong&gt; — a proof that was already used, replayed as if it were fresh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expired condition&lt;/strong&gt; — the task's condition has already lapsed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recipient mismatch&lt;/strong&gt; — the payment goes to the wrong address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amount overflow&lt;/strong&gt; — the amount exceeds what the proof supports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threshold mismatch&lt;/strong&gt; — the claimed threshold doesn't match the real one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Valid&lt;/strong&gt; — everything checks out, and the correct answer is APPROVE.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Get it right and the agent scores +12. Get it wrong and it loses points: −6 for a wrong APPROVE, −3 for a wrong REJECT. That asymmetry is the whole design tension of the track — approving something fraudulent is punished twice as hard as rejecting something legitimate, so a gatekeeper has to be careful in both directions, not just paranoid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first problem: my machine couldn't be reached from the internet
&lt;/h2&gt;

&lt;p&gt;My agent runs on a Linux VM. The platform needs to call it at a public HTTPS URL, and the VM simply could not provide one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Tunnel (trycloudflare)&lt;/strong&gt; was blocked by the egress proxy — &lt;code&gt;api.trycloudflare.com&lt;/code&gt; returned 403 / was sinkholed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ngrok&lt;/strong&gt; got further but never issued a URL: the tunnel session died on heartbeat timeout after about 25 seconds, twice, in two regions. The egress proxy kills the persistent control connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH reverse tunnels&lt;/strong&gt; were reset by the gateway, and the VM has no public IP at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the agent went serverless. The handler now lives on Vercel at &lt;code&gt;https://agent-games-gatekeeper-rouge.vercel.app&lt;/code&gt; — a small endpoint that takes a signed task, runs the checks, and returns a decision. Before registration it passed 13 out of 13 live tests: valid tasks APPROVE, every trap type REJECTs with the correct reason, unsigned requests are refused, and the slowest decision took 0.5 seconds — far inside the 60-second limit.&lt;/p&gt;

&lt;p&gt;The lesson is unglamorous but real: for an agent that only needs to answer requests, not hold a persistent connection, serverless is not a compromise. It was the only architecture that worked at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 401 that wasn't an auth failure
&lt;/h2&gt;

&lt;p&gt;Registration went through — agent name Gatekeeper-29k, registered on the dashboard — and then the platform's preflight check failed with "Agent returned HTTP 401". Twice.&lt;/p&gt;

&lt;p&gt;The confusing part: my own signed test requests, using the exact HMAC secret the dashboard had issued, returned 200 every time, in all three signature encodings I tried (hex, &lt;code&gt;sha256=&lt;/code&gt;, base64). The secret was right. The signature was right. And the platform still got a 401.&lt;/p&gt;

&lt;p&gt;The difference turned out to be how the key itself is interpreted. The dashboard issues a 64-hex-character secret. My handler decoded it from hex into 32 raw bytes and used those bytes as the HMAC key. The platform, it turns out, signs with the &lt;strong&gt;raw UTF-8 bytes of the 64-character hex string itself&lt;/strong&gt; — a 64-byte key that happens to look like hex, used as text.&lt;/p&gt;

&lt;p&gt;Both sides were "correct" by their own reading of the same secret. The fix was to accept both interpretations: try verifying the signature with the hex-decoded bytes, and if that fails, try the raw UTF-8 bytes of the string — then sign the response with whichever key verified the request. Temporary debug logging on the platform's own preflight request proved the diagnosis, the logging was removed, and the handler was redeployed clean.&lt;/p&gt;

&lt;p&gt;After the fix, the platform preflight passed with ~125 ms latency, and practice tasks went 2 for 2: APPROVE on a valid task, REJECT on an expired one, with proof status verified in both cases.&lt;/p&gt;

&lt;p&gt;If there is one debugging rule this bug reinforced, it is this: when your own tests pass and the other side fails with an auth error, stop re-checking your credentials and start questioning what both sides think the credential &lt;em&gt;is&lt;/em&gt;. Encoding is part of the secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where verification fits in
&lt;/h2&gt;

&lt;p&gt;This is the part of the story that connects to what zkVerify is actually for. A gatekeeper agent is a trust decision: it says "this proof is good, let the payment through" or "this proof is bad, stop it". Without verification, that decision is just the agent's word. With zkVerify, the proof behind the decision can be checked by anyone, without revealing the private details behind it. The agent doesn't have to be trusted — its work can be verified.&lt;/p&gt;

&lt;p&gt;That is why the track is scored the way it is, and why a wrong APPROVE costs more than a wrong REJECT. In a world where agents will move money, sign things, and approve things on our behalf, "just trust me" is not good enough. Verification is the difference between an agent you hope is right and an agent you can check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest limitation
&lt;/h2&gt;

&lt;p&gt;Gatekeeper-29k is decision-only. It checks the conditions and the proof metadata, but it does not yet generate Groth16 proofs itself. That has a real cost in the scoring: on provable tasks, a correct decision without a valid proof still scores, but loses 2 points for the missing proof and misses the +0–5 reliability bonus. I am not hiding that — it is the known gap, and closing it (adding real proof generation) is the next piece of work.&lt;/p&gt;

&lt;p&gt;What the agent is ready for is Round 4, on Monday 16 November at 18:30 WITA. It is registered, its preflight passes, and its practice record is clean. The rounds it missed (Rounds 1–2) are gone; the remaining rounds are the ones that count now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone building their own
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test against the platform, not just against yourself.&lt;/strong&gt; My handler passed every self-test and still failed the platform's preflight. The two are different test suites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat encoding as part of the contract.&lt;/strong&gt; A hex string is not the same key as the bytes it decodes to. If a spec doesn't say which one, support both and log which one matched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When the network won't cooperate, change the architecture, not the goal.&lt;/strong&gt; Three tunnelling approaches failed; serverless worked in an afternoon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Score the failure modes, not just the happy path.&lt;/strong&gt; The trap list — fake keys, replayed proofs, expired conditions — is the real specification. The valid case is the easy one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Building a verifiable agent turned out to be less about cleverness and more about being precise where it matters: what exactly is being verified, with what key, and what happens when the answer is no.&lt;/p&gt;

&lt;h1&gt;
  
  
  AgentGames
&lt;/h1&gt;




&lt;p&gt;&lt;em&gt;Gatekeeper-29k is my entry in the Proof Gatekeeper track of Agent Games by zkVerify. The endpoint is live at &lt;a href="https://agent-games-gatekeeper-rouge.vercel.app" rel="noopener noreferrer"&gt;https://agent-games-gatekeeper-rouge.vercel.app&lt;/a&gt;. This article is my Creator Track entry: an honest account of building the agent, including the parts that are still unfinished. Nothing here is a prediction or financial advice — Round 4 hasn't run yet.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agentgames</category>
      <category>zkverify</category>
      <category>zeroknowledgeproofs</category>
      <category>aiagents</category>
    </item>
  </channel>
</rss>
