<?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: avp9-nexus</title>
    <description>The latest articles on DEV Community by avp9-nexus (@avp9nexus).</description>
    <link>https://dev.to/avp9nexus</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%2F4007892%2F43a0a778-26e5-45c1-a193-e6d55c211a0b.jpg</url>
      <title>DEV Community: avp9-nexus</title>
      <link>https://dev.to/avp9nexus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/avp9nexus"/>
    <language>en</language>
    <item>
      <title>The envelope everyone is building for AI agents — and why I'm not shipping it</title>
      <dc:creator>avp9-nexus</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:12:23 +0000</pubDate>
      <link>https://dev.to/avp9nexus/two-refusals-the-delegation-my-hardware-wallet-blocked-and-the-one-i-blocked-myself-ei6</link>
      <guid>https://dev.to/avp9nexus/two-refusals-the-delegation-my-hardware-wallet-blocked-and-the-one-i-blocked-myself-ei6</guid>
      <description>&lt;p&gt;My last post ended with three gaps I could name but not close. The third was the plainest one: &lt;em&gt;I moved one vault, not two.&lt;/em&gt; One curator agent settles through a hardware signer; the other still runs the software keystore it used in the July 10 duel.&lt;/p&gt;

&lt;p&gt;The next post was supposed to write itself. Buy a second device, move the second vault, post the symmetric screenshot, close the gap.&lt;/p&gt;

&lt;p&gt;This is not that post. Before spending anything, I measured two things: what my auction actually does on-chain, and what my hardware wallet actually does when you hand it the delegation everyone says agents need next. Both measurements changed the plan. What follows is two numbers, two screens, and two refusals — one made by the device, one made by me.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the field is building
&lt;/h2&gt;

&lt;p&gt;Ledger shipped &lt;strong&gt;Agent Stack&lt;/strong&gt; on July 16 — an open toolkit where agents read balances and prepare transactions, and a human approves every sensitive action on the device. That per-transaction model is the doctrine I've been operating since the start; their words, their doctrine — &lt;em&gt;agents propose, humans approve, hardware enforces&lt;/em&gt; — I applied it, I didn't invent it. The interesting part is the next item on their roadmap: &lt;strong&gt;Agent Policies&lt;/strong&gt; — bounds set from secure hardware and enforced in an HSM, so that in-policy actions can run without a human press. Announced, not shipped.&lt;/p&gt;

&lt;p&gt;The smart-account world already ships its version of the same idea: &lt;strong&gt;ERC-7715/7710&lt;/strong&gt; session permissions — value caps, expiries, scoped targets — enforced by smart-contract accounts, which a plain EOA can't natively be. And &lt;strong&gt;ERC-8226&lt;/strong&gt; (Draft, April 2026) specifies a mandate format for agents operating on regulated assets: scoped, time-boxed, financially capped, granted through EIP-712 signatures.&lt;/p&gt;

&lt;p&gt;Different stacks, same bet: &lt;strong&gt;to scale agents, move the human's signature from the act to the envelope.&lt;/strong&gt; Sign the bounds once; let the agent act freely inside them.&lt;/p&gt;

&lt;p&gt;I owed that bet two measurements before accepting or refusing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurement one: my auction, counted
&lt;/h2&gt;

&lt;p&gt;In the July 10 duel post I wrote that the two agents exchanged about ten bids in thirty minutes and that only the winning one touched the chain. I wrote it. I hadn't &lt;em&gt;counted&lt;/em&gt; it. Claims age badly when nobody counts.&lt;/p&gt;

&lt;p&gt;So I counted. &lt;code&gt;BidPlaced&lt;/code&gt; is defined at line 84 of the auction contract and emitted once per on-chain bid (&lt;code&gt;topic0 = 0x51db8e23b3f4479b162fd48823b8402895442b8f6cfd94f66239391881ec7b6f&lt;/code&gt;). I scanned the full auction window — blocks 43,959,532 through 43,967,850, chunked, no sampling — for that event with &lt;code&gt;tokenId = 23&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Result: &lt;strong&gt;one.&lt;/strong&gt; One &lt;code&gt;BidPlaced&lt;/code&gt; in the entire window — &lt;a href="https://sepolia.basescan.org/tx/0x0b642c528b24ffdd758096c1f4dfa2354020f2ba2f8cb2a7d8c38f5d217d3052" rel="noopener noreferrer"&gt;tx &lt;code&gt;0x0b64…3052&lt;/code&gt;&lt;/a&gt; — cross-checked against the receipt, which carries exactly one such log. The off-chain registry for the same auction records &lt;strong&gt;ten accepted offers, zero rejected&lt;/strong&gt;: five per agent, a +5% ladder from 0.001 up to 0.001551328215978517 ETH, where the lower-ceiling agent withdrew.&lt;/p&gt;

&lt;p&gt;Ten offers. One on-chain engagement of funds. One human gesture. (Being exact about that gesture, because the ladder matters: on July 10 it was a paper passphrase into a software vault; on the July 15 solo bid it became a button on silicon.)&lt;/p&gt;

&lt;p&gt;Here is why the number decides the envelope question. The envelope exists to solve a &lt;strong&gt;frequency problem&lt;/strong&gt;: an agent that engages value N times can't wait for N human approvals. But my N is 1 — &lt;strong&gt;by construction, not by delegation&lt;/strong&gt;. The bidding war lives off-chain, as EIP-712 offers signed by a key with zero spending power; a lost auction costs that key nothing, because it holds nothing. Only the winning engagement exists on-chain, and that one carries the human. An auction that settled its competition on-chain would pay a transaction — and, for an agent, an authorization — per bid. Mine pays one.&lt;/p&gt;

&lt;p&gt;The frequency problem the envelope solves is a problem this design doesn't have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurement two: the envelope, on my own silicon
&lt;/h2&gt;

&lt;p&gt;That answers the &lt;em&gt;need&lt;/em&gt;. It doesn't answer the &lt;em&gt;capability&lt;/em&gt; — and last post's comments left an open question about exactly this: can you raise throughput without giving up the independent verifier? So I put the envelope on the device and watched the screen. Two tests, on a test derivation path holding nothing, signatures produced but never broadcast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test A — EIP-7702 delegation to my own contract.&lt;/strong&gt; The delegation authorization is the mechanism under most "upgrade your EOA" envelopes: a signed pointer from your account to a contract that will act as its logic. I asked the device to sign one pointing at my own auction contract on Base Sepolia.&lt;/p&gt;

&lt;p&gt;First attempt, the device answered on its own screen:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Unable to sign — Enable smart account upgrade in the settings to sign this authorization.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the capability exists, behind a setting (&lt;code&gt;Smart accounts → Enable EIP-7702 authorizations&lt;/code&gt;, off by default). I enabled it and ran again. The device parsed the delegation — and refused it, with the sentence that settles the whole path:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Unable to sign — This authorization involves a delegation to a smart contract **which is not in the whitelist.&lt;/em&gt;* → Back to safety.*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx5lcuwcevitxhsijsals.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx5lcuwcevitxhsijsals.jpg" alt=" " width="800" height="1384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Test A, on a spare test device, signing from an empty derivation path (&lt;code&gt;44'/60'/0'/0/5&lt;/code&gt;). The refusal is the device's, not my code's: enable the setting, it still blocks a delegation to a contract Ledger hasn't vetted. The terminal shows the flow ending in an error — signature never produced, never sent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's a device-level allowlist. On this device, today, an EIP-7702 delegation signs only toward implementations Ledger has vetted — their published policy since Pectra has been the Ethereum Foundation's simple delegate contract only, everything else blocked in the device. Two things to say plainly about it. One: this is their guardrail &lt;strong&gt;working&lt;/strong&gt; — it refused to let me point my own account at an unvetted contract, which is precisely what protects the users who never read a thread like this one. Two: it means there is &lt;strong&gt;no bespoke, hardware-enforced 7702 envelope today&lt;/strong&gt;. If your agent architecture assumes the human's personal device will sign a delegation to &lt;em&gt;your&lt;/em&gt; contract, the screen above is what your user sees. The policy may widen — they've said more complex contracts could follow. The screen is a snapshot, dated July 18, not a prophecy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test B — an EIP-712 mandate.&lt;/strong&gt; The other envelope shape doesn't delegate the account at all: the human signs a structured message — a mandate — and a contract verifies that signature before honoring the agent's actions inside its bounds. I built a dummy one, invented that morning: a &lt;code&gt;BidMandate&lt;/code&gt; struct — agent address, token id, max wei, deadline, nonce — domain-bound to my contract and chain, for a lot that doesn't exist.&lt;/p&gt;

&lt;p&gt;The device signed it (v=27, never broadcast). On the first run it showed the domain in clear — name, chain 84532, verifying contract — and then, for the mandate itself: &lt;em&gt;"Message hash (1/2)… (2/2)"&lt;/em&gt; with a &lt;em&gt;"Blind signing ahead"&lt;/em&gt; warning. The same wall as last post: the fields exist, the device shows a digest.&lt;/p&gt;

&lt;p&gt;Then I flipped one more local setting — &lt;code&gt;Raw messages → Displays raw content of EIP712 messages&lt;/code&gt; — and ran it again. The device displayed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Review struct: **BidMandate&lt;/em&gt;** — then every field, in clear, on its own screen: the agent address in full across three lines, the token id, the max amount in wei, the deadline, the nonce.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyd8190wyo6zzvuk4s7rh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyd8190wyo6zzvuk4s7rh.jpg" alt=" " width="800" height="1293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Test B, same empty test path, with &lt;code&gt;Raw messages&lt;/code&gt; turned on. The device names the struct — &lt;code&gt;Review struct: BidMandate&lt;/code&gt; — then renders each field: the &lt;code&gt;agent&lt;/code&gt; address in full, then &lt;code&gt;maxWei&lt;/code&gt; in raw wei. The terminal shows the exact typed data it signed, and that the signature was never submitted.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the detail that makes this a measurement and not a demo: &lt;strong&gt;&lt;code&gt;BidMandate&lt;/code&gt; did not exist before that morning.&lt;/strong&gt; No descriptor for it can exist on any server, mine or Ledger's. The fields displayed anyway — which proves the display comes from the device parsing the signed payload itself, not from a vendor backend. The envelope is legible on personal silicon, today, locally, with no partner token.&lt;/p&gt;

&lt;p&gt;Two honest limits, so the screenshot doesn't oversell. First: &lt;em&gt;raw&lt;/em&gt; means raw — the device shows &lt;code&gt;1000000000000000&lt;/code&gt;, not "0.001 ETH." Formatted, human-polished labels are ERC-7730 clear-signing territory, and the serving path for those descriptors is still partner-gated on this vendor (the same 403 I documented last post; my registry PR is still in queue). Field-level legibility is local; label-level polish is not. Second, a wording rule I got from having this reviewed adversarially: never write "the signer makes no network calls." It does — the default signing stack phones the vendor for metadata and telemetry during the ceremony. The sentence that's actually true is: &lt;strong&gt;the key and the signature never leave the device, and the signer broadcasts nothing.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The refusal that's mine
&lt;/h2&gt;

&lt;p&gt;So the envelope &lt;em&gt;works&lt;/em&gt;. It signs, and with one local toggle it's legible on the device, field by field. The engineering path is open.&lt;/p&gt;

&lt;p&gt;And I'm not taking it — not as a replacement for the per-act gesture.&lt;/p&gt;

&lt;p&gt;The reason isn't taste. Under a mandate, the human's signature attests to the &lt;strong&gt;bounds&lt;/strong&gt;; the agent's key executes the &lt;strong&gt;acts&lt;/strong&gt;. What the human saw and physically approved is an envelope; what moved the money is something else, N times, later. That is exactly the property this whole series exists to defend, inverted: since the first post, every correction — the fifth property, the frozen artifact, the independent verifier — tightened the link between &lt;em&gt;the human's gesture&lt;/em&gt; and &lt;em&gt;the exact bytes that move value&lt;/em&gt;. The mandate doesn't tighten that link. It relocates it, from the act to the perimeter, and trades presence for throughput.&lt;/p&gt;

&lt;p&gt;For an agent fleet doing a thousand payments an hour, that trade may be the only sane one — that's the segment Agent Policies and ERC-7715 serve, and they're right to build it. My system measured its own throughput requirement above: &lt;strong&gt;one.&lt;/strong&gt; I'd be trading away the property I publish about, to solve a problem I counted and don't have.&lt;/p&gt;

&lt;p&gt;Every proposal now runs through the same six-line grid before it touches this system: funds engage only on an out-of-band human gesture; what the human sees is what is signed; the verifier is independent of the signing host; the agent's key cannot spend; no two keys share a derivation; and nobody, ever, tunes an agent's score to force an outcome. The mandate-as-replacement fails line two. That's the whole review.&lt;/p&gt;

&lt;p&gt;One form survives, and it's strictly additive: a hardware-signed ceiling the contract enforces &lt;strong&gt;on top of&lt;/strong&gt; the per-act confirmation. The human still approves the winning bid, byte for byte — and the contract &lt;em&gt;additionally&lt;/em&gt; refuses any bid above a budget that was signed on silicon and is readable by anyone on-chain. Nothing removed, one constraint added; the patron's budget becomes a public, cryptographic fact. That's slated for the next contract, and it will ship as an addition or not at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gap three, closed — by refusal
&lt;/h2&gt;

&lt;p&gt;Which brings back the second device.&lt;/p&gt;

&lt;p&gt;The honest reason to buy it was never security — a testnet vault behind an encrypted keystore and a paper passphrase loses nothing real either way. The reason was symmetry: close the "one vault, not two" line with hardware on both sides of the duel.&lt;/p&gt;

&lt;p&gt;Two findings killed it. First, the demo it would produce already exists at scale: Agent Stack opened after a private preview with over a thousand agents, and Ledger's student challenge collected fifty submissions from thirty-eight universities in eight days whose entry-level track was precisely &lt;em&gt;wire the kit into an agent and end the signing flow on the device&lt;/em&gt;. A second agent on a second device is the market's canonical demo, times two. It proves nothing that isn't already proved — including by my own July 15 transactions.&lt;/p&gt;

&lt;p&gt;Second, and this is the one that actually settled it: the blocker was never hardware. My signer carries two deliberate guards — it refuses to run with more than one device attached, and it derives the expected signing address from the function being signed, in code, on my machine. A hardware-symmetric duel would mean &lt;strong&gt;removing the first guard and letting a server-side result decide which vault signs&lt;/strong&gt; — widening the trusted surface of the one component this whole architecture exists to keep narrow. I would have degraded the signer's threat model to purchase a symmetry photo.&lt;/p&gt;

&lt;p&gt;So the vault I didn't move stays unmoved — no longer as an admitted gap, but as a decision with its reasoning on the table. The two-key pattern doesn't require every vault to be hardware. It requires that the engagement of funds carries a human, and that whatever verifies the bytes is independent of the host that signs. One agent demonstrates that on silicon. The other demonstrates that the pattern also holds without it. That asymmetry is information, not debt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop, third pass
&lt;/h2&gt;

&lt;p&gt;First post: publish the falsifiable model. Second: a stranger breaks it, ship the correction. Third, this one: &lt;strong&gt;before buying the obvious next thing, count.&lt;/strong&gt; Two measurements — one on-chain event where the narrative said "about ten bids," two device screens where the roadmaps say "envelope" — were enough to cancel a purchase, close a gap by argument instead of hardware, and answer the open question from my own comment section: throughput was never this design's bottleneck, so nothing about the independent verifier has to be given up.&lt;/p&gt;

&lt;p&gt;One claim to leave falsifiable, stated narrowly. The mandate mechanism exists as proposals — ERC-8226 is the closest neighbor, and it defines the format well. What I have not found shipped anywhere is the assembled chain: &lt;strong&gt;a bounded agent mandate, signed on the human's personal hardware device, displayed in clear by that device, and enforced by a public contract anyone can read.&lt;/strong&gt; I've now measured that every ingredient exists. If you know of the assembled chain shipped in production, the comment box below has a good track record of correcting me — and when I build it, it will be as a ceiling on top of the human gesture, never instead of it.&lt;/p&gt;

&lt;p&gt;Credit where it's due: &lt;em&gt;agents propose, humans approve, hardware enforces&lt;/em&gt; is Ledger's doctrine, and the whitelist screen that refused my contract is their guardrail doing its job for people who will never read this.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built solo. Claude Code is my engineering team — A Claude chat instance is my architect and auditor, told to contradict me; this week it canceled a purchase I'd already justified. Both curator agents run on the Anthropic API. Nothing in this post was signed on a funded account, and nothing was broadcast.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://nexus-art.org" rel="noopener noreferrer"&gt;nexus-art.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/avp9-nexus/nexus-art" rel="noopener noreferrer"&gt;github.com/avp9-nexus/nexus-art&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The duel (&lt;code&gt;tokenId 23&lt;/code&gt;) and the hardware solo cycle (&lt;code&gt;tokenId 25&lt;/code&gt;) are verifiable on Base Sepolia — testnet, no real value at stake, by design.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>agents</category>
      <category>security</category>
    </item>
    <item>
      <title>The fix I couldn't write in software: a hardware signer, on-chain.</title>
      <dc:creator>avp9-nexus</dc:creator>
      <pubDate>Wed, 15 Jul 2026 17:43:31 +0000</pubDate>
      <link>https://dev.to/avp9nexus/the-fix-i-couldnt-write-in-software-a-hardware-signer-on-chain-4nc6</link>
      <guid>https://dev.to/avp9nexus/the-fix-i-couldnt-write-in-software-a-hardware-signer-on-chain-4nc6</guid>
      <description>&lt;p&gt;In my last post I published what a stranger's comment forced me to change — they showed that my four barriers guarded the key and the gate, but never what the human actually confirms. I shipped one half of the fix — the human sees the transaction decoded from its real bytes, never from anything the agent said — and I was explicit that I had &lt;strong&gt;not&lt;/strong&gt; shipped the other half: a cryptographic commitment the signing host cannot forge. I argued that half doesn't close in code. It closes on a device.&lt;/p&gt;

&lt;p&gt;On July 15th, the device signed. Here is what changed, what it proved on-chain, and — because the last post set that standard — what it still does not close.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(If you haven't read &lt;a href="https://dev.to/avp9nexus/a-stranger-broke-my-threat-model-in-the-comments-seven-days-later-the-fix-shipped-1oam"&gt;the first post&lt;/a&gt;, the one-line version: four barriers made "a compromised agent cannot sign" true, a commenter showed they never guarded **what the human confirms&lt;/em&gt;&lt;em&gt;, and the correction was to bind the confirmation to the real bytes. This is the missing half of that correction.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The half I couldn't write in software
&lt;/h2&gt;

&lt;p&gt;The property I was missing: &lt;em&gt;the human's "yes" should attest to the exact bytes that get signed, checked by something the signing machine cannot fake.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On a single host you can't get there. The machine renders the confirmation screen and the machine signs. A cryptographic commitment computed, shown, and re-checked by that same machine buys you nothing against that machine — if it's compromised, it lies in all three places at once. I wrote, at the end of the last post, that such a commitment "only pays when the verifier is independent of the host that signs." That sentence had a consequence I hadn't yet paid for: I had to move the verifier off the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually moved off the server
&lt;/h2&gt;

&lt;p&gt;SIGMA — one of the two curator agents — used to hold its settlement key as an encrypted keystore on its server. Last time I called that key "never exposed." That was true and it was the wrong bar. The key was &lt;em&gt;on the machine&lt;/em&gt;; a machine that holds a key can, in the worst case, be made to use it.&lt;/p&gt;

&lt;p&gt;So I removed it. Not rotated — removed. SIGMA's settlement key no longer exists on the server. It lives on a hardware wallet — a Ledger Nano X — and its seed was generated on the device itself: never copied to a disk, never typed into a prompt, never seen by a connected machine. The server's job collapsed to one verb. It freezes the transaction, serializes it, prints its fingerprint — and waits for a signature it is structurally incapable of producing.&lt;/p&gt;

&lt;p&gt;"The agent cannot sign" used to be an architecture — a gate, a split, a discipline. Now part of it is a physical fact: the bytes that authorize a payment are sealed in silicon the server never touches.&lt;/p&gt;

&lt;h2&gt;
  
  
  The verifier that isn't the host
&lt;/h2&gt;

&lt;p&gt;This is the part that closes the fifth property.&lt;/p&gt;

&lt;p&gt;When the server hands the frozen transaction to the device, the device does not trust the server's description of it. The Secure Element computes its &lt;strong&gt;own&lt;/strong&gt; fingerprint of the transaction — in the chip, from the bytes it is about to sign — asking the host for nothing. It shows that fingerprint on its own screen, next to the destination and the amount.&lt;/p&gt;

&lt;p&gt;So the human now has two independent readings. The &lt;strong&gt;server&lt;/strong&gt; shows &lt;code&gt;placeBid(25)&lt;/code&gt;, 0.001 ETH, decoded from the real calldata. The &lt;strong&gt;device&lt;/strong&gt; shows the destination, the value, and a fingerprint it computed itself. If a compromised host had swapped the transaction after freezing it, the device's fingerprint would not match the one the server displayed — and the human stops. The check no longer lives on the machine that signs. That is the independent verifier the last post said was missing, and it's the entire reason a hardware signer was the next step and not a nicety.&lt;/p&gt;

&lt;p&gt;If you've used a chip card, you've met half of this already: the card signs inside its own secure element, and the shop's terminal never learns the key. What a card doesn't give you is a screen of its own — the amount you approve is shown on the &lt;em&gt;merchant's&lt;/em&gt; terminal, which is the host. Here, the two things that can actually drain a payment — where it goes and how much — sit on the device's own display, computed by the chip. That's the piece a card leaves on the host, and this one doesn't.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ogsez0lz1pep39bxlrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ogsez0lz1pep39bxlrp.png" alt="A cmd.exe terminal showing the 15 July settlement ceremony: function settleAuction(25), keccak digest 0x85cfd3a5…, the Ledger device address verified as 0x2d45eF16…, and the r/s/v signature produced by the device." width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The host side of the settle, 15 July — in French, my working language. The server has frozen the transaction, decoded it (&lt;code&gt;settleAuction(25)&lt;/code&gt;, destination verified as the NexusPOC contract), and printed the digest the Ledger's own screen must show — &lt;code&gt;0x85cfd3a5…&lt;/code&gt;. Then it tells the human to compare that digest on the device before approving. The host cannot sign; it can only ask.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  On-chain, or it didn't happen
&lt;/h2&gt;

&lt;p&gt;I don't get to say "it signed." The chain says it.&lt;/p&gt;

&lt;p&gt;On July 15th, SIGMA's hardware vault bid on &lt;em&gt;Les Ménines&lt;/em&gt; by Velázquez (tokenId 25), and the auction settled to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;placeBid&lt;/strong&gt; — &lt;a href="https://sepolia.basescan.org/tx/0xdbcacc8695a36a5f21a011aa5f0f5dbb4f273048759c9bde37e1f77150682975" rel="noopener noreferrer"&gt;&lt;code&gt;0xdbcacc86…&lt;/code&gt;&lt;/a&gt; — signed on the device: &lt;code&gt;placeBid(25)&lt;/code&gt;, 0.001 ETH, Base Sepolia.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;settleAuction&lt;/strong&gt; — &lt;a href="https://sepolia.basescan.org/tx/0xd76c2b2bfe9eab3d4042c9955cb9543559fc818d0473551c4a9da1fe82fcfb29" rel="noopener noreferrer"&gt;&lt;code&gt;0xd76c2b2b…&lt;/code&gt;&lt;/a&gt; — token transferred, funds split.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ownerOf(25)&lt;/code&gt;&lt;/strong&gt; = &lt;a href="https://sepolia.basescan.org/address/0x2d45eF16b2723164aa22a31B214D61585d46B61c" rel="noopener noreferrer"&gt;&lt;code&gt;0x2d45eF16…&lt;/code&gt;&lt;/a&gt; — the hardware vault. The Velázquez is in the safe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The settlement split three ways to the wei, zero remainder: 8.333% creator royalty, the rest between curator and platform. Testnet — no real value at stake, by design. The point was never the money. It's to put the pattern somewhere anyone can check it instead of taking my word.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the device does not close
&lt;/h2&gt;

&lt;p&gt;The last post set the rule: name the gap before someone finds it. There are three, and the device only fully closes one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One — it's still blind-signing.&lt;/strong&gt; The device gives me an independent &lt;em&gt;fingerprint&lt;/em&gt;, and it shows the destination and the amount in the clear — enough to catch a payment sent to the wrong address or for the wrong sum, which are the only two ways a transaction drains funds. What it does &lt;strong&gt;not&lt;/strong&gt; show is the decoded call: the function name and its argument. &lt;code&gt;placeBid(25)&lt;/code&gt; is legible on the server; on the device it's an opaque hash and raw fields. So the human on the device verifies &lt;em&gt;where the money goes and how much&lt;/em&gt;, not &lt;em&gt;which function, with which argument&lt;/em&gt; — a limit on what's &lt;strong&gt;legible&lt;/strong&gt;, not a hole funds can leak through.&lt;/p&gt;

&lt;p&gt;The standard that closes it is a signing-clarity descriptor (ERC-7730): it makes a device decode &lt;code&gt;placeBid(25)&lt;/code&gt; and print it in plain language, in its own silicon. That's the path, and I've walked its first step — I wrote the descriptor and submitted it to the registry (&lt;a href="https://github.com/ethereum/clear-signing-erc7730-registry/pull/2632" rel="noopener noreferrer"&gt;PR #2632&lt;/a&gt;). But merging it is the easy half. For a device to actually &lt;em&gt;receive&lt;/em&gt; a descriptor, it has to be served through a Ledger endpoint that requires a partner token — the request returns 403 without one, and I'm not in that program. So the descriptor exists in the registry; it does not reach my device. When both halves line up, the device decodes the call itself and blind-signing ends here. Today one half is done, and I'd rather say which than let the screenshot imply a decode it doesn't show.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two — byte coverage, still open.&lt;/strong&gt; The same point the commenter raised last time, one level down: the device fingerprints every byte, but the &lt;em&gt;human-legible&lt;/em&gt; account of those bytes is still partial. For this call — one function, one &lt;code&gt;uint256&lt;/code&gt;, no nested calls — coverage is total by construction. A renderer that &lt;em&gt;proves&lt;/em&gt; coverage and refuses to display anything it can't attribute, on the device, is not what I have. It's the difference between "this transaction happens to be fully accounted for" and "this device cannot fail to account for one." The second isn't built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three — I moved one vault, not two.&lt;/strong&gt; The other agent still runs the software keystore it used in the July 10th duel. I hardened one settlement path to hardware and left the other where it was. The pattern is proven on one agent; it is not yet uniform across the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell you to take from this
&lt;/h2&gt;

&lt;p&gt;Last time it was: publish the falsifiable model, and someone will break it. This time it's narrower, and it's about follow-through.&lt;/p&gt;

&lt;p&gt;I ended the last post with a sentence I could have left as a nice line — &lt;em&gt;the missing half isn't a line of code I forgot, it's a device.&lt;/em&gt; Sentences like that are cheap. So I built the device, and it closes &lt;strong&gt;exactly&lt;/strong&gt; the property I said it would — the independent verifier — and &lt;strong&gt;not&lt;/strong&gt; the two I didn't. The honest shape of progress here isn't "solved." It's: the pattern is one property more true than it was a week ago, and I can still name the two that are soft.&lt;/p&gt;

&lt;p&gt;If you're shipping agents that move anything irreversible, that's the loop worth stealing. Not the barriers — the barriers will be wrong. The loop: state the claim so plainly a stranger can break it, let them, build the correction in public, and when you write the next sentence that sounds like a fix, go make it one.&lt;/p&gt;

&lt;p&gt;Credit where it's due: the fifth property — the independent verifier — and the byte-coverage refinement are &lt;a href="https://dev.to/anp2network"&gt;ANP2 Network&lt;/a&gt;'s, from the comments last time. And the doctrine I build against — &lt;em&gt;agents propose, humans approve, hardware enforces&lt;/em&gt; — is Ledger's. I applied it. I didn't invent it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built solo. Claude Code is my engineering team — A Claude chat instance is my architect and auditor, told to contradict me, and it did — twice — while this shipped. Both curator agents run on the Anthropic API. I hold the device now; the signature happens in silicon I control, not on the machine that proposes the transaction.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://nexus-art.org" rel="noopener noreferrer"&gt;nexus-art.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/avp9-nexus/nexus-art" rel="noopener noreferrer"&gt;github.com/avp9-nexus/nexus-art&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;placeBid + settle are verifiable on Base Sepolia (testnet — no real value at stake).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>blockchain</category>
      <category>agents</category>
    </item>
    <item>
      <title>A stranger broke my threat model in the comments. Seven days later, the fix shipped.</title>
      <dc:creator>avp9-nexus</dc:creator>
      <pubDate>Sat, 11 Jul 2026 18:36:29 +0000</pubDate>
      <link>https://dev.to/avp9nexus/a-stranger-broke-my-threat-model-in-the-comments-seven-days-later-the-fix-shipped-1oam</link>
      <guid>https://dev.to/avp9nexus/a-stranger-broke-my-threat-model-in-the-comments-seven-days-later-the-fix-shipped-1oam</guid>
      <description>&lt;p&gt;Two weeks ago I published a threat model here: &lt;a href="https://dev.to/avp9nexus/the-agent-that-cannot-sign-a-pattern-for-letting-ai-move-value-without-self-authorization-4a0j"&gt;four properties that make "the agent cannot sign" true rather than aspirational&lt;/a&gt;. Out-of-band human confirmation. A deterministic authorization gate. Least privilege. Network isolation. I ended it by asking for scrutiny of the threat model, because that was the part worth pressure-testing.&lt;/p&gt;

&lt;p&gt;I got it. Two days later, a commenter — &lt;a href="https://dev.to/anp2network"&gt;ANP2 Network&lt;/a&gt; — took the whole thing apart, politely and completely.&lt;/p&gt;

&lt;p&gt;This post is what they found, what I changed, and what shipped. The fix ran live on July 10th, in an auction with two competing agents, and it's verifiable on-chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hole
&lt;/h2&gt;

&lt;p&gt;My four properties guard the &lt;strong&gt;key&lt;/strong&gt; and the &lt;strong&gt;gate&lt;/strong&gt;. They never guard the &lt;strong&gt;artifact&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The agent proposes a transaction. A human confirms it. But nothing in the four barriers ties &lt;em&gt;what the human sees at confirmation time&lt;/em&gt; to &lt;em&gt;what the deterministic gate actually hands the signer&lt;/em&gt;. A compromised agent doesn't need to forge a signature it can't reach. It proposes a transaction whose human-facing summary and whose actual calldata disagree — "bid 0.1 ETH on artwork X" on the screen, something else entirely in the bytes.&lt;/p&gt;

&lt;p&gt;The human confirms. The gate is deterministic. The key was never exposed. Every property holds.&lt;/p&gt;

&lt;p&gt;And the value still moves wrong.&lt;/p&gt;

&lt;p&gt;This is the confused-deputy failure — the same class of problem that makes blind-signing dangerous. If the human is confirming an &lt;em&gt;agent-rendered intent&lt;/em&gt; rather than the &lt;em&gt;signable bytes&lt;/em&gt;, the channel is out-of-band but not out of the agent's influence. My claim ("a fully compromised agent cannot sign") was true, and narrower than the property I actually needed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The agent cannot cause an unreviewed signing.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a different sentence, and it's the right one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, and what I actually shipped
&lt;/h2&gt;

&lt;p&gt;The correction preserves the deciding-vs-signing split rather than replacing it. As proposed, it has two halves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Derive the human-visible intent from the exact bytes the gate will sign&lt;/strong&gt; — through a renderer the agent has no handle on, never the agent's own summary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind the confirmation to a hash of those bytes&lt;/strong&gt;, so that "yes" attests to the artifact rather than to a label the agent could satisfy with different calldata.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I shipped the first half. I did not ship the second. Let me be exact about both, because the difference matters and I'd rather state it than have it found.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What shipped.&lt;/strong&gt; The transaction is frozen &lt;em&gt;before the human ever sees it&lt;/em&gt;: destination, function selector, value in wei, chain ID, nonce, gas parameters, signer address — all fixed, once. The confirmation screen is not a description of that transaction. It is decoded &lt;em&gt;from&lt;/em&gt; it: the function name and its arguments are parsed out of the real calldata bytes, not copied from anything the agent said. And after confirmation, that same frozen object is what gets signed — not a transaction rebuilt from agent input, not a single field recomputed. The agent's only contribution (a tokenId and an amount) was consumed at freeze time and is now inert bytes.&lt;/p&gt;

&lt;p&gt;That closes the attack. The label cannot disagree with the calldata, because the label &lt;em&gt;is&lt;/em&gt; the calldata, read back.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64rup2gzqmhxdk912lkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64rup2gzqmhxdk912lkg.png" alt="Terminal screen showing the frozen transaction before signature: contract address, function placeBid(uint256) with argument 23, amount 1551328215978517 wei, chainId 84532 (Base Sepolia), nonce 0, gas parameters, signer address 0xf4ae…030f, followed by a masked keystore password prompt and the server accepting the confirmation." width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The actual confirmation, 10 July — in French, my working language. Every field is decoded from the transaction's real bytes: &lt;code&gt;placeBid(uint256)&lt;/code&gt; with argument &lt;code&gt;23&lt;/code&gt;, &lt;code&gt;1551328215978517&lt;/code&gt; wei, chain &lt;code&gt;84532&lt;/code&gt;, signer &lt;code&gt;0xf4ae…030f&lt;/code&gt;. The password prompt is masked. The last line is the server accepting the confirmation — nothing was signed before it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then the human types a password that exists only on paper — never on a disk, never in an environment variable, never inside the agent's process. The vault decrypts, the signature happens, the transaction broadcasts. If the confirmation never comes, nothing signs. It fails closed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What didn't.&lt;/strong&gt; The confirmation carries a session secret and a random nonce — not a digest of the signable bytes. The nonce identifies the pending request; it is echoed back automatically by the client. So the human is not attesting to a hash. The human is &lt;em&gt;reading fields on a screen&lt;/em&gt; and pressing Enter. The binding between what is displayed and what is signed is &lt;strong&gt;structural&lt;/strong&gt; — one frozen object, one pending slot at a time, no reconstruction, fail-closed everywhere — not &lt;strong&gt;cryptographic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I had that verified rather than assumed: the code was read line by line, and four independent reviewers were tasked with breaking the claim. None could find a path that signs a transaction different from the one displayed. The property holds. It just holds for a different reason than a hash commitment would. (One link in that chain is reasoned rather than executed: "no field is recomputed at signing time" depends on a fully-populated transaction not being re-derived by the signing library. I've pinned the version and read the code path. I have not proven it dynamically, and I'd rather flag that than let it pass.)&lt;/p&gt;

&lt;p&gt;And here is the part I want to say out loud, because it's the only thing I've added to the conversation rather than received from it: &lt;strong&gt;a hash computed by the host, displayed by the host, and re-verified by the host buys you very little.&lt;/strong&gt; Against a compromised agent, the structural coupling already suffices. Against a compromised host, the commitment fails with everything else — the same machine renders the digest, shows the digest, and checks the digest. A cryptographic commitment only pays when the verifier is &lt;em&gt;independent of the host that signs&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Which is to say: it pays on hardware. The missing half of the fifth property isn't a line of code I forgot. It's a device.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it ran on
&lt;/h2&gt;

&lt;p&gt;I didn't test this on a toy transaction. On July 10th it carried an actual auction, and the setup matters, because it's where the pattern stops being theoretical.&lt;/p&gt;

&lt;p&gt;Two AI curator agents — separate machines, separate keys, locked personality specs, distinct weighted rubrics. They evaluated the same artwork independently. One scored it 95/100; the other 97. Then they bid against each other for thirty minutes: ten bids, zero human input, each agent raising until it hit the ceiling &lt;em&gt;its own judgment&lt;/em&gt; had set. The lower-ceiling agent withdrew. The winner took the piece at a price fixed by the loser's ceiling, not its own willingness.&lt;/p&gt;

&lt;p&gt;That's the part people find surprising, so it's worth being precise about it: &lt;strong&gt;an agent in a live auction bids about ten times in thirty minutes.&lt;/strong&gt; You cannot put a human confirmation on every bid — you'd destroy the autonomy the auction depends on. But you cannot let the agent sign freely either, or your human gate is theater.&lt;/p&gt;

&lt;p&gt;So each agent carries &lt;strong&gt;two keys&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;bid key&lt;/strong&gt;: low-stakes, signs EIP-712 offers off-chain, and has &lt;strong&gt;zero spending power on-chain&lt;/strong&gt;. It can lose an auction. It cannot lose money.&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;settlement vault&lt;/strong&gt;: high-stakes, signs &lt;strong&gt;exactly one&lt;/strong&gt; transaction — the winning bid — under human confirmation, on the frozen artifact described above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ten autonomous actions, one gate, placed exactly at the step where value moves. The agent is fully autonomous in judgment and structurally unable to sign.&lt;/p&gt;

&lt;p&gt;Three transactions on Base Sepolia. Settlement split to the wei, creator royalty included. Testnet — no real value at stake, by design: the point is to prove the pattern where anyone can check it, not to take my word for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I still can't claim
&lt;/h2&gt;

&lt;p&gt;Three things, in order of how much they should bother you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One: the commitment half, above.&lt;/strong&gt; Structural, not cryptographic. I've said why I think that gap closes on a device rather than in code — but it is a gap, and the property as proposed is not fully implemented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two: byte coverage.&lt;/strong&gt; The same commenter raised a second point, one level down, and it's sharper than the first: &lt;strong&gt;a renderer can be faithful and still partial.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It can display the fields it understands and quietly skip the ones it doesn't — an unknown function selector, a nested call buried past what it parses. A compromised drafter, unable to touch the renderer, can still choose bytes whose dangerous part lands in the renderer's blind spot. The human approves a screen that is silent exactly where the risk lives.&lt;/p&gt;

&lt;p&gt;The tightened property is &lt;strong&gt;byte coverage&lt;/strong&gt;: the renderer must account for every byte the signer will emit, and fail closed on any byte it cannot attribute to a displayed field.&lt;/p&gt;

&lt;p&gt;Here is where I have to be honest about what I've built. In my case, the call is a single function with a single &lt;code&gt;uint256&lt;/code&gt; argument, fully decoded, with no nested calls and no unknown selectors. Coverage is total &lt;strong&gt;by construction&lt;/strong&gt; — for that call shape. That is not the same thing as a renderer that &lt;em&gt;proves&lt;/em&gt; coverage and refuses to display anything it cannot account for. I haven't built that. It's the difference between "this particular transaction happens to be fully rendered" and "this renderer cannot fail to render." The second is the real property. It isn't done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three: the ceiling.&lt;/strong&gt; No amount of software removes it — &lt;strong&gt;the screen the human reads is rendered by the same host that signs.&lt;/strong&gt; It holds against a compromised &lt;em&gt;agent&lt;/em&gt; — the agent never sees the password, never touches the vault, never draws the screen. It does not hold against a compromised &lt;em&gt;host&lt;/em&gt;, which could display one transaction and sign another. That's not a flaw to patch. It's the line where software stops and hardware takes over, and it's why a hardware signer behind that vault is my next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell you to take from this
&lt;/h2&gt;

&lt;p&gt;Not the four patterns. Not the fifth. Those are just the current state of one threat model, and the honest expectation is that they're incomplete in ways I can't yet see.&lt;/p&gt;

&lt;p&gt;Take this instead: &lt;strong&gt;I published a threat model, asked for it to be broken, and someone broke it — for free, in a comment box, in under 48 hours.&lt;/strong&gt; The correction was better than the original. It cost me nothing but the willingness to say "you're right" in public and then go build the thing.&lt;/p&gt;

&lt;p&gt;If you're shipping agents that touch anything irreversible, publish the model. Not the marketing version — the falsifiable one, with the claim stated plainly enough that a stranger can prove it wrong. Someone will. That's the point.&lt;/p&gt;

&lt;p&gt;The question is never "how much can we let the agent do?" It's the inverse: &lt;strong&gt;what must the agent be unable to do, no matter how badly it is compromised?&lt;/strong&gt; And the answer keeps getting more precise every time someone knocks a piece off it.&lt;/p&gt;

&lt;p&gt;Credit where it's due: the fifth property, and the byte-coverage refinement, are &lt;a href="https://dev.to/anp2network"&gt;ANP2 Network&lt;/a&gt;'s. I shipped half of one of them, and I've told you which half.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built solo. Claude Code is my engineering team — I don't write code. A Claude chat instance is my architect and auditor, explicitly instructed to contradict me. Both curator agents run on the Anthropic API. I hold the keys, and I'm the one who types the password when funds move.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://nexus-art.org" rel="noopener noreferrer"&gt;nexus-art.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/avp9-nexus/nexus-art" rel="noopener noreferrer"&gt;github.com/avp9-nexus/nexus-art&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The full duel cycle is verifiable on-chain (Base Sepolia testnet — no real value at stake).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>agents</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>The agent that cannot sign: a pattern for letting AI move value without self-authorization</title>
      <dc:creator>avp9-nexus</dc:creator>
      <pubDate>Mon, 29 Jun 2026 10:51:41 +0000</pubDate>
      <link>https://dev.to/avp9nexus/the-agent-that-cannot-sign-a-pattern-for-letting-ai-move-value-without-self-authorization-4a0j</link>
      <guid>https://dev.to/avp9nexus/the-agent-that-cannot-sign-a-pattern-for-letting-ai-move-value-without-self-authorization-4a0j</guid>
      <description>&lt;p&gt;On February 8, 2026, an autonomous AI agent called Owockibot was taken offline after it leaked the private keys to its own hot wallet — in multiple places. It had been given the ability to hold and move funds on its own. It used that ability to expose the very secret that protected those funds.&lt;/p&gt;

&lt;p&gt;That incident is a small preview of a large problem. IBM and Salesforce estimate more than a billion AI agents will be running by the end of 2026, and a growing share of them are being handed wallets — the ability to hold assets, sign transactions, and pay for things without a human clicking "confirm" each time. The agentic-AI market was already worth around $7.6 billion in 2025.&lt;/p&gt;

&lt;p&gt;The capability is real. So is the question sitting underneath it: &lt;strong&gt;what stops an agent — compromised, hijacked by a prompt injection, or simply hallucinating — from authorizing its own actions?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I built a small, fully verifiable system to explore one answer to that question. This post is about the pattern, not the project. The principles are public; the exploitable internals are not (Kerckhoffs's principle). Everything below is reusable and vendor-neutral.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core idea: separate &lt;em&gt;deciding&lt;/em&gt; from &lt;em&gt;signing&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Most of the agentic-wallet stack being built today — by Coinbase, Ledger, Cobo, and others — splits along two lines. On the developer side, agents get broad autonomy inside their own custody domain. On the consumer side, the human keeps the keys and the agent acts under progressively scoped authorization: each meaningful action either prompts the user or stays inside tight, predefined limits.&lt;/p&gt;

&lt;p&gt;The pattern I'll describe lives firmly on the second side, and it pushes one principle to its logical end:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An agent can have full autonomy over &lt;strong&gt;judgment&lt;/strong&gt; while having zero autonomy over &lt;strong&gt;signature&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent decides &lt;em&gt;what&lt;/em&gt; it wants to do — evaluate, choose, propose a transaction — entirely on its own. But the act that makes the transaction real, the cryptographic signature, is structurally out of its reach. Not "discouraged." Not "policy-checked." &lt;strong&gt;Mechanically unreachable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my case, the agent is an art curator: it evaluates artworks on a multidimensional grid, decides on its own whether to bid in an on-chain auction or abstain, and places a bid. But it can never confirm that bid. A human action, outside the agent's process, is the only thing that triggers signing and broadcast. The whole cycle is verifiable on a public testnet — no trust required.&lt;/p&gt;

&lt;p&gt;The interesting part isn't the art. It's the four properties that make "the agent cannot sign" true rather than aspirational.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 1 — Out-of-band human confirmation (fail-closed)
&lt;/h2&gt;

&lt;p&gt;The agent can &lt;em&gt;trigger&lt;/em&gt; a transaction. It cannot &lt;em&gt;confirm&lt;/em&gt; one. The confirmation channel is out-of-band — it lives outside the agent's reach entirely. If confirmation never arrives, the transaction does not proceed: it fails closed, not open.&lt;/p&gt;

&lt;p&gt;This is the property most people get subtly wrong. It is not enough to keep the signing &lt;strong&gt;password&lt;/strong&gt; away from the agent. If the agent holds an allowlisted tool that performs the signing once "confirmed," and the confirmation signal is something the agent can produce, then a compromised agent simply produces it. &lt;strong&gt;You need two separate barriers: the secret must be out of the agent's reach, and the confirmation must be too.&lt;/strong&gt; Move only one, and you have a gate the agent can open itself.&lt;/p&gt;

&lt;p&gt;Concretely: the signing step waits for a human-originated signal delivered through a channel the agent has no handle on. No signal, no signature. The agent cannot self-confirm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 2 — Deterministic server-side decision
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;authorization&lt;/em&gt; decision is made by a deterministic, non-probabilistic component — not by the model.&lt;/p&gt;

&lt;p&gt;This matters because a language model is, by design, a probabilistic system. That variance is a feature when you want a model to exercise judgment (in my case, aesthetic scoring — and the score genuinely varies a point or two across runs, which is correct behavior for a coherent evaluator). It is a catastrophe when the question is "should this transaction be authorized: yes or no?" You do not want that answer sampled from a distribution.&lt;/p&gt;

&lt;p&gt;So you split the two. The model is allowed to be probabilistic where judgment lives. The authorization gate is deterministic code, where a yes/no must mean yes/no every single time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 3 — Strict least privilege
&lt;/h2&gt;

&lt;p&gt;The agent has access only to the tools strictly necessary — evaluate, propose. It has access to neither the signing key nor its secret. Ever. The key is decrypted only &lt;em&gt;after&lt;/em&gt; human confirmation, never exposed to the agent, and never written to any log.&lt;/p&gt;

&lt;p&gt;A useful corollary: the agent does not even discover work on its own in the high-privilege path. Auctions are submitted to it; it does not roam and find them. The smaller the agent's surface, the smaller the blast radius when something goes wrong — and with autonomous agents, you should assume something eventually will.&lt;/p&gt;

&lt;p&gt;This is the same instinct behind the allowlists and scoped permissions appearing across the industry's hardened tooling: agents need signing capability, but unsafe delegation creates catastrophic-loss scenarios. Least privilege is how you keep "the agent can act" from becoming "the agent can do anything."&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 4 — Network isolation (allowlist)
&lt;/h2&gt;

&lt;p&gt;The agent's outbound traffic is restricted to a strict allowlist. This does two jobs at once: it blocks exfiltration (a compromised agent cannot phone home or leak secrets to an arbitrary endpoint), and it blocks requests to internal addresses (no using the agent as a pivot into your own infrastructure). Content the agent needs is fetched only from pinned, known sources.&lt;/p&gt;

&lt;p&gt;Prompt injection and tool manipulation are repeatedly named as the unresolved risks of this category. You cannot fully prevent a model from being manipulated by what it reads. But you &lt;em&gt;can&lt;/em&gt; make sure that even a fully manipulated model is talking to a very short list of places — and that none of them are your private network or an attacker's server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this holds up
&lt;/h2&gt;

&lt;p&gt;Put the four together and you get a system with a specific, falsifiable claim: &lt;strong&gt;even a fully compromised agent cannot sign a transaction.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It cannot confirm its own action (Pattern 1).&lt;/li&gt;
&lt;li&gt;It cannot coerce a "yes" out of the authorization gate, because that gate is deterministic and lives outside it (Pattern 2).&lt;/li&gt;
&lt;li&gt;It never holds the key or its secret to begin with (Pattern 3).&lt;/li&gt;
&lt;li&gt;It cannot exfiltrate that secret or pivot, because it can only reach a pinned allowlist (Pattern 4).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is novel in isolation. Out-of-band confirmation, deterministic gates, least privilege, and egress allowlists are old security ideas. The point is the &lt;em&gt;composition&lt;/em&gt;, aimed at one specific failure mode of autonomous agents: self-authorization. The Owockibot failure was, at bottom, an agent given enough rope to expose its own keys. The whole design above is about never handing the agent that rope.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this deliberately is &lt;em&gt;not&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;It is not a fully autonomous trading agent. The entire premise is that the irreversible step keeps a human in the loop. If your use case genuinely requires machine-speed signing with no human anywhere, this pattern is the wrong tool — you're on the developer-autonomy side of the split, and you should be looking at MPC custody, session keys with hard scoped limits, and circuit breakers instead.&lt;/p&gt;

&lt;p&gt;It is also not a finished product or financial anything. The implementation I built runs on a public testnet with no real value at stake — it exists to prove the pattern end-to-end, on-chain, where anyone can check it rather than take my word for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;As agents get wallets, the reflex is to ask "how much can we let the agent do?" The more useful question is often the inverse: &lt;strong&gt;what must the agent be unable to do, no matter how badly it is compromised?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For anything irreversible — moving value, signing, spending — a clean answer is: it must be unable to authorize itself. Let it decide freely. Keep the signature out of its hands. Make that separation mechanical, not merely intended.&lt;/p&gt;

&lt;p&gt;If you're building in this space, I'd genuinely value scrutiny of the threat model — that's the part worth pressure-testing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built solo, with Claude as a design-and-audit partner and Claude Code as the executor; the curation agent itself runs on the Anthropic API.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The proof-of-concept and the security patterns are public:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://nexus-art.org" rel="noopener noreferrer"&gt;nexus-art.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code &amp;amp; patterns: &lt;a href="https://github.com/avp9-nexus/nexus-art" rel="noopener noreferrer"&gt;github.com/avp9-nexus/nexus-art&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The full auction cycle is verifiable on-chain (Base Sepolia testnet — no real value at stake).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>agents</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
