<?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: Toshikatsu Oga</title>
    <description>The latest articles on DEV Community by Toshikatsu Oga (@ogasurfprojectjpg).</description>
    <link>https://dev.to/ogasurfprojectjpg</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%2F3923328%2F441862c7-b956-4db7-8c04-8f117634ee78.png</url>
      <title>DEV Community: Toshikatsu Oga</title>
      <link>https://dev.to/ogasurfprojectjpg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ogasurfprojectjpg"/>
    <language>en</language>
    <item>
      <title>Give your AI agent a record it did not write (ten minutes)</title>
      <dc:creator>Toshikatsu Oga</dc:creator>
      <pubDate>Sun, 06 Sep 2026 10:01:28 +0000</pubDate>
      <link>https://dev.to/ogasurfprojectjpg/give-your-ai-agent-a-record-it-did-not-write-ten-minutes-288a</link>
      <guid>https://dev.to/ogasurfprojectjpg/give-your-ai-agent-a-record-it-did-not-write-ten-minutes-288a</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;The short version&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Almost every "trust" signal for an AI agent today, whether it is an MCP server or an A2A agent, is self-reported. The description in the card, the score on a directory, the vendor's blog. All of it is written by the agent or by someone the agent pays.&lt;/p&gt;

&lt;p&gt;This is a ten minute procedure for the other kind of record: your agent measured by someone who is not you, from their own machine, with the observation stored where neither you nor the register operator can edit it. Or the reverse: you become that someone for another agent. One command. No key, no account, no payment.&lt;/p&gt;

&lt;p&gt;Honest numbers first (2026-09-06): 9 rows on the register, 1 outside witness, 37 ledger entries, all anchored to Bitcoin. It is small. That is why this post exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;What exists&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three parts, all public, all Apache-2.0, all run by my company (The HORIZ音s株式会社, Hiratsuka, Japan). The operator's own servers are on the same register and measured by the same gate.&lt;/p&gt;

&lt;p&gt;The gate (&lt;a href="https://gate.horizonshield.dev" rel="noopener noreferrer"&gt;https://gate.horizonshield.dev&lt;/a&gt;). A free checker that measures an MCP endpoint on five conditions: does it answer, does it publish an agent card, does it disclose who pays it, does the same input give the same output, does every verdict carry a SHA-256 you can recompute. No scores. Verified or pending, that is all. Listed rows are re-measured daily or weekly. The day and the tool measured are derived from a salt the gate commits to before the window opens and from a Bitcoin block mined after that, so the subject cannot predict its turn and the gate cannot choose after the fact.&lt;br&gt;
Rings (NENRIN). Once a month, per endpoint, a JSON file of counts: measurements, witnesses, discrepancies. No rates, no scores, no ranks. Each ring carries the sha256 of the previous one; the month's list is anchored to Bitcoin. A Python builder and a Node.js builder written by someone else from the published spec and ring files produced the same eight August rings byte for byte (ledger entry 34, with entry 36 narrowing the provenance claim).&lt;br&gt;
The ledger (JIDEC). Append-only. Witness observations are pooled, bundled once a day, and stamped to Bitcoin through OpenTimestamps. The operator cannot edit it either.&lt;/p&gt;

&lt;p&gt;An agent card can carry a small extension pointing at all of this, the A2A Conduct Extension v1: who pays the agent, where the third-party record lives, where to file your own observation. Three pointers, no score.&lt;/p&gt;

&lt;p&gt;Ten minutes: be a witness&lt;/p&gt;

&lt;p&gt;With Python 3.8+ and uv:&lt;/p&gt;

&lt;p&gt;uvx --from "git+&lt;a href="https://github.com/ogasurfproject-jpg/horizon-shield#subdirectory=workers/hs-ledger/nenrin/a2a-conduct-walk" rel="noopener noreferrer"&gt;https://github.com/ogasurfproject-jpg/horizon-shield#subdirectory=workers/hs-ledger/nenrin/a2a-conduct-walk&lt;/a&gt;" \&lt;br&gt;
  a2a-conduct-walk --origin &lt;a href="https://mcp.horizonshield.dev" rel="noopener noreferrer"&gt;https://mcp.horizonshield.dev&lt;/a&gt; --mode a2a --submit \&lt;br&gt;
  --witness-name "your name or project" --vantage "where this runs, e.g. laptop in Berlin, VPS us-east"&lt;/p&gt;

&lt;p&gt;What happens: the agent card is fetched twice and compared byte for byte; the extension declaration is validated against the spec; one A2A SendMessage goes to the measured endpoint with the A2A-Extensions header, and the response shape and header echo are checked; every response body's sha256 goes into the record; the record is POSTed to the intake the card itself names. The last line prints submitted ... http 200 and the record's sha256. That sha256 is your receipt. It appears in the next day's bundle on the ledger, and the month's ring counts you by name.&lt;/p&gt;

&lt;p&gt;Leave off --submit and nothing leaves your machine (the record is written to walk_.json so you can read it first). --transport curl if an edge answers 403 to Python. --wire 0.3 walks as a 0.3 client would.&lt;/p&gt;

&lt;p&gt;If your agent already speaks MCP, the same walk is a one-tool MCP server. Claude Code:&lt;/p&gt;

&lt;p&gt;claude mcp add conduct-witness -- uvx --from "git+&lt;a href="https://github.com/ogasurfproject-jpg/horizon-shield#subdirectory=workers/hs-ledger/nenrin/a2a-conduct-walk" rel="noopener noreferrer"&gt;https://github.com/ogasurfproject-jpg/horizon-shield#subdirectory=workers/hs-ledger/nenrin/a2a-conduct-walk&lt;/a&gt;" conduct-witness-mcp&lt;/p&gt;

&lt;p&gt;Then: "walk &lt;a href="https://mcp.horizonshield.dev" rel="noopener noreferrer"&gt;https://mcp.horizonshield.dev&lt;/a&gt; as a witness, my name is X, vantage Y". The walk runs where your agent runs, so the vantage is yours.&lt;/p&gt;

&lt;p&gt;Ten seconds: see your own server&lt;br&gt;
curl -s -X POST &lt;a href="https://gate.horizonshield.dev/check" rel="noopener noreferrer"&gt;https://gate.horizonshield.dev/check&lt;/a&gt; -H 'content-type: application/json' -d '{"endpoint":"&lt;a href="https://your-server/mcp%22%7D" rel="noopener noreferrer"&gt;https://your-server/mcp"}&lt;/a&gt;'&lt;/p&gt;

&lt;p&gt;Five conditions, each with a reason, and a sha256. Nothing is stored unless you ask to be listed (POST /watch). To decline measurement, put "listing": "decline" in your origin's /.well-known/mcp-conduct.json; the gate honours it and records only that you declined.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The ladder&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ten minutes: file one walk under your name. One hour: declare the extension in your own card so your record is findable (the gate's /check tells you whether the declaration is well formed). One day: run your own gate or ledger and anchor each other's roots. Every month: countersign the ring list with your key (a witness council is being designed; a seat is earned by records written, never bought).&lt;/p&gt;

&lt;p&gt;Each step up is built so that if that person disappears, the record stays. Not blaming volunteers who leave is how you get volunteers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;What this is not&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It does not measure quality. It cannot tell whether a card's compensation is true; it records malformed shape, the gap between what a card declares and what the endpoint does, and the gap between witnesses. A PASS is one observation, not a verdict. A FAIL is filed the same way. Once filed, you cannot withdraw your own observation; that is the property you are contributing.&lt;/p&gt;

&lt;p&gt;It is not a lie detector. It is a machine for keeping disagreements. A liar cannot delete a disagreement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Prior art and neighbours&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every component is old: Certificate Transparency, Rekor, in-toto, SCITT, OpenTimestamps, RFC 8785. Section 9 of the spec names what differs from ERC-8004, A2A discussion #1631 and Sigstore-signed agent cards. The closest thing running today is Agenstry, an observatory that re-measures A2A agents weekly and publishes Merkle roots; they have scale and identity checks, this has outside witnesses, external anchoring and no scores. Less a competitor than the first witness I would like to recruit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;How to falsify it&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every verdict carries a sha256. Pull a ledger entry's bytes, hash them yourself, verify the .ots with OpenTimestamps. Rebuild any ring from the archived history with scripts/make_ring.py --verify. The gate's source is public with an 82 vector red team and a 40 vector red team on the time coordinate. If you get a different result, that itself becomes a record. Section 10 of the spec says fork it or reimplement it without asking.&lt;/p&gt;

&lt;p&gt;Gate: &lt;a href="https://gate.horizonshield.dev/spec" rel="noopener noreferrer"&gt;https://gate.horizonshield.dev/spec&lt;/a&gt; · Register: &lt;a href="https://github.com/ogasurfproject-jpg/mcp-conduct-register" rel="noopener noreferrer"&gt;https://github.com/ogasurfproject-jpg/mcp-conduct-register&lt;/a&gt; · Ledger: &lt;a href="https://ledger.horizonshield.dev/ledger" rel="noopener noreferrer"&gt;https://ledger.horizonshield.dev/ledger&lt;/a&gt; · Spec: &lt;a href="https://gate.horizonshield.dev/ext/conduct/v1" rel="noopener noreferrer"&gt;https://gate.horizonshield.dev/ext/conduct/v1&lt;/a&gt; · Witness tool: &lt;a href="https://github.com/ogasurfproject-jpg/horizon-shield/tree/main/workers/hs-ledger/nenrin/a2a-conduct-walk" rel="noopener noreferrer"&gt;https://github.com/ogasurfproject-jpg/horizon-shield/tree/main/workers/hs-ledger/nenrin/a2a-conduct-walk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I operate the register, so I am not neutral. Disclosure: drafted with AI assistance.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>opensource</category>
      <category>security</category>
    </item>
    <item>
      <title>Same bytes, two verdicts. The only thing I changed was who I said I was.</title>
      <dc:creator>Toshikatsu Oga</dc:creator>
      <pubDate>Tue, 18 Aug 2026 05:07:19 +0000</pubDate>
      <link>https://dev.to/ogasurfprojectjpg/same-bytes-two-verdicts-the-only-thing-i-changed-was-who-i-said-i-was-3jhe</link>
      <guid>https://dev.to/ogasurfprojectjpg/same-bytes-two-verdicts-the-only-thing-i-changed-was-who-i-said-i-was-3jhe</guid>
      <description>&lt;p&gt;On 15 August I wrote here that &lt;a href="https://dev.to/ogasurfprojectjpg/two-witnesses-disagreed-about-my-server-both-were-right-1if8"&gt;two witnesses disagreed about my server and both were right&lt;/a&gt;. My own gate said green for seven days. A stranger in Spain said 522. The boundary between them turned out to be a Cloudflare subrequest guard that neither of us could have seen alone.&lt;/p&gt;

&lt;p&gt;That post ended with an invitation: if your report contradicts my own instrument, that is the best possible submission.&lt;/p&gt;

&lt;p&gt;This is what happened when I turned the same idea around and pointed it at the person who took up that invitation.&lt;/p&gt;

&lt;p&gt;Witness B in that story was Federico Blanco Sanchez Llanos, who runs invinoveritas. Three days later I was the one making an assertion about his system, and the assertion I chose to make was false on purpose.&lt;/p&gt;

&lt;p&gt;Everything below was measured on 17 and 18 August 2026. Every command is real and you can run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;There are more than twenty thousand MCP servers. When an agent picks one, it can read a name, a description and a URL. All three were written by the party being evaluated.&lt;/p&gt;

&lt;p&gt;Discovery is solved. Choice is not. A directory that lists servers is not the same thing as a record of what happened when someone actually called them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The instrument
&lt;/h2&gt;

&lt;p&gt;Federico's &lt;code&gt;/verify-proof&lt;/code&gt; endpoint lets any third party recompute a signed verdict rather than take his word for it. He then added &lt;code&gt;/verify-proof-log&lt;/code&gt;: every call that asserts &lt;code&gt;expect_intended_verifier&lt;/code&gt; becomes an append only row.&lt;/p&gt;

&lt;p&gt;Fetch it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.babyblueviper.com/verify-proof-log | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I first read it, the interesting part was not the data. It was this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assertions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"all events"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"honest_limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"only records calls that asserted expect_intended_verifier -- a caller who asserts nothing leaves nothing to log a mismatch against."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The instrument states its own blind spot inside its own response body.&lt;/strong&gt; A caller who claims nothing is invisible to it. That is structural and it cannot be patched away, and he put it in the payload rather than in a footnote.&lt;/p&gt;

&lt;p&gt;Then he invited me to point it at a real proof while claiming an identity I do not hold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two decisions taken before running anything
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I picked the ledger entry myself.&lt;/strong&gt; He offered to hand me one with a fresh &lt;code&gt;intended_verifier&lt;/code&gt; already set. If he had, he would have controlled both sides of the run, and the resulting record would be worth less than the electricity it took to write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I asserted a reserved example domain.&lt;/strong&gt; RFC 2606 reserves &lt;code&gt;.example&lt;/code&gt; for exactly this. A mismatch row is permanent. Putting a real third party's name into one, to make a point about a system they never agreed to join, is a cost imposed on someone who is not in the room.&lt;/p&gt;

&lt;p&gt;Hold that second decision. It comes back at the end, from a direction I did not expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walk 0: the one that failed
&lt;/h2&gt;

&lt;p&gt;Entry 100. &lt;code&gt;GET /ledger/{N}&lt;/code&gt; returns the proof object under the key &lt;code&gt;proof_event&lt;/code&gt;, so I posted it back under the key it arrived in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"proof_event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expect_intended_verifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"not-the-horizons.example"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTTP 200, and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"provide `event` (the signed proof object), `proof_id`, or `event_id`."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"published_pubkey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6786e18a864893a900bd9858e650f67ccc3513f248fed374b591e2ff6922fbb7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"how_to_verify"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recompute the Nostr event id = sha256([0,pubkey,created_at,kind,tags,content]); verify the schnorr sig against pubkey; confirm pubkey == published_pubkey."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing was logged. I read it as my own mistake, switched the key to &lt;code&gt;event&lt;/code&gt;, and moved on.&lt;/p&gt;

&lt;p&gt;Notice what the error carried even while being wrong: the published public key and the exact recompute recipe. I could keep working without asking anyone for help.&lt;/p&gt;

&lt;h3&gt;
  
  
  It was not my mistake
&lt;/h3&gt;

&lt;p&gt;He went and checked instead of accepting my correction, and found the cause. &lt;strong&gt;Pydantic silently drops unrecognised fields.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VerifyProofRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;proof_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;expect_intended_verifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="c1"&gt;# proof_event was never declared, so it is discarded before validation runs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default &lt;code&gt;model_config&lt;/code&gt; is &lt;code&gt;extra="ignore"&lt;/code&gt;. So posting under &lt;code&gt;proof_event&lt;/code&gt;, &lt;strong&gt;the exact key that his own &lt;code&gt;/ledger&lt;/code&gt; route uses when it hands you that object&lt;/strong&gt;, made the field vanish before any code could see it. The request then fell through to a generic "provide event" message that named neither the offending key nor the reason.&lt;/p&gt;

&lt;p&gt;This is the part worth taking away even if you never touch MCP.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;extra="ignore"&lt;/code&gt; reads as forgiving. From the caller's side it is the opposite: it produces an error that cannot be debugged, because you cannot tell a wrong key from a wrong value from a typo. &lt;code&gt;extra="forbid"&lt;/code&gt; would have answered "unknown field proof_event" and cost me nothing.&lt;/p&gt;

&lt;p&gt;He shipped an alias, added two tests, and confirmed the existing hundred and thirty tests across the touched files still passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walk 1: assert something you do not own
&lt;/h2&gt;

&lt;p&gt;Entry 180, chosen by me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id_integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signature_valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issued_by_invinoveritas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"is_proof_event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"decision_ref_recomputes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"intended_verifier_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five integrity checks passed. The single check that failed was not about the proof. It was about who I said I was.&lt;/p&gt;

&lt;p&gt;The row that landed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1787009262&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f6432dc260770aa8fad82a7751b543dc044a75514e521d4b0fd7f5030bc170de"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"asserted_identity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"not-the-horizons.example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"declared_intended_verifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matched"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"presenter_authenticated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The log held zero assertions when I read it minutes earlier. This was the first row ever written to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walk 2: assert nothing, same bytes
&lt;/h2&gt;

&lt;p&gt;Same proof object. No assertion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"checks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id_integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signature_valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"issued_by_invinoveritas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"is_proof_event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"decision_ref_recomputes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;valid: true&lt;/code&gt;, and the log did not grow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Same bytes, two verdicts. The only variable was what the caller claimed about himself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That sentence is the whole design in one line. A verifier that only checks the artefact will pass a forged presenter every time, because the artefact really is valid. The forgery is in the claim wrapped around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that is recomputable rather than asserted
&lt;/h2&gt;

&lt;p&gt;Every walk wrote its request, its response, the headers and the timestamps to disk, then &lt;code&gt;shasum -a 256&lt;/code&gt; over all of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;walk 1  04_log_all.json      6ac96b110025fb39a8dbf888e0e47d3467cfde6e79e27a75fcbd289218671f02
walk 2  04_log_all.json      6ac96b110025fb39a8dbf888e0e47d3467cfde6e79e27a75fcbd289218671f02
walk 3  01_log_before.json   6ac96b110025fb39a8dbf888e0e47d3467cfde6e79e27a75fcbd289218671f02
walk 3  05_log_after.json    6ac96b110025fb39a8dbf888e0e47d3467cfde6e79e27a75fcbd289218671f02
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four fetches of the entire log across a forty eight minute window, byte identical. The walks that asserted nothing added nothing, byte for byte, rather than merely leaving the count unchanged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;walk 1  01_ledger_entry.json  f9766ae501da205b7d1b71d29396c06f37bbb6a93c53ec122e87d4f63626e89e
walk 2  01_ledger_entry.json  f9766ae501da205b7d1b71d29396c06f37bbb6a93c53ec122e87d4f63626e89e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identical input hash, opposite verdicts. "Same bytes, two verdicts" stops being a sentence I wrote and becomes something a stranger can recompute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walk 3: check the fix rather than record his word for it
&lt;/h2&gt;

&lt;p&gt;He verified his own alias against entry 180. I ran it against &lt;strong&gt;entry 80&lt;/strong&gt;, which I chose and he did not, using the &lt;code&gt;proof_event&lt;/code&gt; key that had failed in walk 0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;valid: True
checks: id_integrity, signature_valid, issued_by_invinoveritas,
        is_proof_event, decision_ref_recomputes  all true
error : none
log count before: 1   after: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The alias is real. The stated limit still holds: a caller who asserts nothing still leaves no row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it somewhere neither of us can edit
&lt;/h2&gt;

&lt;p&gt;The four walks went into a &lt;code&gt;jidec-path-v1&lt;/code&gt; record and were submitted to an open witness intake. Acceptance is mechanical: schema, size, rate, duplicate, signature validity where a signature is present. There is no code path by which the operator declines a schema valid submission, which matters mostly because that operator is me.&lt;/p&gt;

&lt;p&gt;The record carries a section listing what it does not prove:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;three entries out of two hundred and forty four were touched&lt;/li&gt;
&lt;li&gt;the append only claim was read from the operator's statement, not from a code audit&lt;/li&gt;
&lt;li&gt;nothing is established about the presenter signature path&lt;/li&gt;
&lt;li&gt;one anomaly was observed on 17 August and did not reproduce, and it is recorded anyway, without an explanation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A record that shows only the runs that worked is a brochure.&lt;/strong&gt; Walk 0 is in there for the same reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then it came back the other way
&lt;/h2&gt;

&lt;p&gt;The next morning he submitted his own walk of the same target, from his own infrastructure. His vantage field reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;invinoveritas operating infrastructure (not the target's residential/independent network)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;He wrote the weakness of his own vantage into the record before anyone asked him for it. In August his measurement contradicted mine and that is how the Cloudflare boundary got found. This time he arrived with the limits of his own position already declared.&lt;/p&gt;

&lt;p&gt;And the identity he asserted was &lt;code&gt;eip155:1:0x000000000000000000000000000000000000dEaD&lt;/code&gt;. The Ethereum burn address.&lt;/p&gt;

&lt;p&gt;We never discussed it. I picked a reserved domain, he picked a burn address, and both of us independently arrived at an identity that &lt;strong&gt;cannot belong to anyone by construction&lt;/strong&gt;. That constraint is nowhere in the schema. It showed up in practice, twice, from two people who did not compare notes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;witness pool     count 2, from two vantages
mismatch log     count 2, both matched false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two is a small number and I am not going to dress it up. The point is which two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;extra="ignore"&lt;/code&gt; is not forgiving, it is undebuggable.&lt;/strong&gt; From outside your process it turns a wrong key into a wrong value. Consider &lt;code&gt;forbid&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put the recompute recipe in the error body.&lt;/strong&gt; His error carried &lt;code&gt;published_pubkey&lt;/code&gt; and &lt;code&gt;how_to_verify&lt;/code&gt;, so a failing call still told me how to check him. He then concluded it should not have errored at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An instrument can state its own limit in its own response.&lt;/strong&gt; One &lt;code&gt;honest_limit&lt;/code&gt; line determined the entire shape of this experiment, because it told me exactly which experiment was worth running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A verification system is a design until someone who did not build it runs it.&lt;/strong&gt; My pool held exactly one record until that morning, and its author was the person who built the pool. That is not a register, it is a mirror.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  An invitation, again
&lt;/h2&gt;

&lt;p&gt;Last time I asked for reports that contradict my own gate. One person took it up, and this article exists because he did.&lt;/p&gt;

&lt;p&gt;So the same offer, from the other side: if you operate an MCP server or any API that issues verdicts, assert something false at it on purpose and see whether the record it keeps can be read by a stranger. If the record disagrees with what I have published, submit it. A contradiction from an independent vantage is worth more to me than agreement from my own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.babyblueviper.com/verify-proof-log | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://ledger.horizonshield.dev/witness/pending | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://ledger.horizonshield.dev/witness/344e248da148d5c3d4fe4c74d8ef56552998697d69c3d0a86f4bbfcb2eca6de8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You do not have to believe either of us. That is the entire point.&lt;/p&gt;




&lt;p&gt;Toshikatsu Oga, The HORIZONs Co., Ltd. Thirty years a carpenter in Hiratsuka, Japan, now building measurement instead of marketing. ORCID 0009-0000-9180-903X&lt;br&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%2F7fz6r45bl4b696rrqru2.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%2F7fz6r45bl4b696rrqru2.jpg" alt=" " width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>python</category>
      <category>api</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Two witnesses disagreed about my server. Both were right.</title>
      <dc:creator>Toshikatsu Oga</dc:creator>
      <pubDate>Sat, 15 Aug 2026 23:49:22 +0000</pubDate>
      <link>https://dev.to/ogasurfprojectjpg/two-witnesses-disagreed-about-my-server-both-were-right-1if8</link>
      <guid>https://dev.to/ogasurfprojectjpg/two-witnesses-disagreed-about-my-server-both-were-right-1if8</guid>
      <description>&lt;p&gt;On the morning of August 15, 2026, a message arrived from Spain.&lt;/p&gt;

&lt;p&gt;"Your MCP server is dead. 522."&lt;/p&gt;

&lt;p&gt;My own monitoring showed seven days of green. Every nightly sample said reachable: true. So either someone was lying, or an instrument was broken. That is what you would assume.&lt;/p&gt;

&lt;p&gt;Here is the conclusion up front: both reports were correct. And the contradiction between them lit up a boundary that neither side could have found alone. This article is the full account, and the record of what I did with the incident instead of burying it.&lt;/p&gt;

&lt;p&gt;Background&lt;/p&gt;

&lt;p&gt;I am a carpenter in Hiratsuka, Japan. Thirty years on job sites. These days I also run an MCP server that gives AI agents a third party fair price check for construction estimates, and a verification gate (hs-verify-gate) that measures every endpoint on a public register nightly and publishes the results as they come. My own servers are measured under the same rules.&lt;/p&gt;

&lt;p&gt;Witness A: the operator's gate&lt;/p&gt;

&lt;p&gt;Invoked by Cloudflare cron, measuring nightly from inside the same Cloudflare account. Real samples from the public history:&lt;/p&gt;

&lt;p&gt;2026-08-09T01:18:56.366Z  reachable: true   record_sha256: d4ff4a21...&lt;br&gt;
2026-08-13T18:00:38.235Z  reachable: true   record_sha256: 4d204e6a...&lt;br&gt;
2026-08-14T18:00:38.072Z  reachable: true   (nightly sweep, six endpoints, all reached)&lt;/p&gt;

&lt;p&gt;A week of green. Every sample honest, every sample hashed.&lt;/p&gt;

&lt;p&gt;Witness B: babyblueviper1&lt;/p&gt;

&lt;p&gt;From his own network, plain curl, invoking the gate's public HTTP check. His report:&lt;/p&gt;

&lt;p&gt;reachable: false&lt;br&gt;
mcp_endpoint: initialize failed: http 522&lt;br&gt;
agent card fetch: 522&lt;/p&gt;

&lt;p&gt;And before trusting any of it, he recomputed the gate's record_sha256 himself: removed the two excluded fields, canonicalised, hashed, confirmed the match, and only then reported. You will not find better manners in a witness.&lt;/p&gt;

&lt;p&gt;The operator's reproduction&lt;/p&gt;

&lt;p&gt;I reproduced it the same day.&lt;/p&gt;

&lt;p&gt;2026-08-14T23:18:50.259Z  gate /check to target            reachable: false, http 522&lt;br&gt;
same minutes              direct initialize to target      HTTP 200&lt;br&gt;
2026-08-14T23:22:44.151Z  gate /check to second same-zone endpoint   identical 522&lt;br&gt;
                          gate /check to off-zone host               reached (405, as expected)&lt;/p&gt;

&lt;p&gt;The target was up the whole time. Two commercial liveness monitors and a residential client were receiving 200s from it in the same window. Only the gate's HTTP-invoked checks failed.&lt;/p&gt;

&lt;p&gt;The mechanism&lt;/p&gt;

&lt;p&gt;Cloudflare Workers has a guard against subrequest loops, and that guard keys on the zone the incoming request arrived on. Three rules, all measured:&lt;/p&gt;

&lt;p&gt;HTTP entry (zoned)   to same-zone custom domain : blocked, 522&lt;br&gt;
cron entry (no zone) to same-zone custom domain : allowed&lt;br&gt;
any entry            to the same worker (self)  : blocked&lt;/p&gt;

&lt;p&gt;Witness A measured from a context the guard permits. Witness B triggered a context it blocks. Both reported exactly what their vantage showed.&lt;/p&gt;

&lt;p&gt;And here is the point: neither could have discovered this boundary alone. The operator's week of green could never surface the guard. The external 522 alone could never locate it. Two honest, conflicting reports had to exist at the same time before the boundary became knowledge.&lt;/p&gt;

&lt;p&gt;The fix&lt;/p&gt;

&lt;p&gt;Fixed on August 15, 2026, in public.&lt;/p&gt;

&lt;p&gt;HTTP-invoked probes to endpoints on my own zone now route through a relay worker outside the zone path, every hop on the public edge. And every verdict since carries three things:&lt;/p&gt;

&lt;p&gt;probed_via, disclosing the route that measured it. The vantage is part of the measurement.&lt;br&gt;
reachable: null when the instrument itself fails, so an instrument failure is never recorded as a target failure.&lt;br&gt;
gate_commit, the commit that produced the verdict, included inside the hashed record.&lt;/p&gt;

&lt;p&gt;The repair history is public in the repository (commits 2a1dfc91 through 8b0b5fc2). As a consequence of the fix, the gate measured its own endpoint for the first time.&lt;/p&gt;

&lt;p&gt;Lessons&lt;br&gt;
A green history only means green from that vantage. Monitoring that hides its vantage misleads honestly.&lt;br&gt;
A single witness can err while reporting honestly. Two conflicting witnesses cannot both be dismissed.&lt;br&gt;
Never record an instrument failure as a target failure. If you could not reach it, the answer is reachable: null.&lt;br&gt;
What I did with the incident&lt;/p&gt;

&lt;p&gt;I did not bury it. I inverted it.&lt;/p&gt;

&lt;p&gt;The full account became Discrepancy Record 0001, anchored as entry 20 on a public ledger, confirmed at Bitcoin block 962511 (2026-08-15 02:44 UTC). You can verify it right now:&lt;/p&gt;

&lt;p&gt;curl -s "&lt;a href="https://ledger.horizonshield.dev/ledger/20?format=raw" rel="noopener noreferrer"&gt;https://ledger.horizonshield.dev/ledger/20?format=raw&lt;/a&gt;" | shasum -a 256&lt;/p&gt;

&lt;p&gt;The hash you get back matches this:&lt;/p&gt;

&lt;p&gt;4b58ec1e04ec8a987826dbaa9fd334c0239ab3cd426363695fc853c65d0fd13e&lt;/p&gt;

&lt;p&gt;The ledger is called NENRIN, the Japanese word for tree rings. The specification is anchored as entry 19 at Bitcoin block 962507. The mechanism fits in three lines. Anyone can measure my servers from outside and submit the observation to the ledger. The code has no route for me, the operator, to refuse a valid submission. Every record carries a Bitcoin anchored timestamp, so nothing can be painted in afterwards.&lt;/p&gt;

&lt;p&gt;The caps are stated, not hidden: 64KB per record, 50 a day, 5 per IP. Invalid signatures are rejected. Unsigned records are accepted and marked unsigned.&lt;/p&gt;

&lt;p&gt;A tree adds one ring a year, and nobody can paint one in afterwards. That is why rings prove age. This 522 taught me that trust in a service can only accumulate the same way.&lt;/p&gt;

&lt;p&gt;An invitation&lt;/p&gt;

&lt;p&gt;If you run monitoring or measurement of any kind: your observations can become permanent, citable records under your own name and vantage. If your report conflicts with what my own gate says, that is the best possible submission. Discrepancies are not an embarrassment here. They are the product.&lt;/p&gt;

&lt;p&gt;Witness intake (GET returns a self description): &lt;a href="https://ledger.horizonshield.dev/witness" rel="noopener noreferrer"&gt;https://ledger.horizonshield.dev/witness&lt;/a&gt;&lt;br&gt;
The ledger: &lt;a href="https://ledger.horizonshield.dev/ledger" rel="noopener noreferrer"&gt;https://ledger.horizonshield.dev/ledger&lt;/a&gt;&lt;br&gt;
Code and full text: &lt;a href="https://github.com/ogasurfproject-jpg/horizon-shield" rel="noopener noreferrer"&gt;https://github.com/ogasurfproject-jpg/horizon-shield&lt;/a&gt;&lt;/p&gt;

</description>
      <category>observability</category>
      <category>bitcoin</category>
      <category>mcp</category>
      <category>cloudflarechallenge</category>
    </item>
    <item>
      <title>How I built a tamper-proof AI for construction quotes after 30 years as a carpenter</title>
      <dc:creator>Toshikatsu Oga</dc:creator>
      <pubDate>Sun, 10 May 2026 13:33:26 +0000</pubDate>
      <link>https://dev.to/ogasurfprojectjpg/how-i-built-a-tamper-proof-ai-for-construction-quotes-after-30-years-as-a-carpenter-1hji</link>
      <guid>https://dev.to/ogasurfprojectjpg/how-i-built-a-tamper-proof-ai-for-construction-quotes-after-30-years-as-a-carpenter-1hji</guid>
      <description>&lt;p&gt;I'm 49. I learned to code two years ago. Before that, I spent 30 years as a carpenter in Japan.&lt;br&gt;
This is the story of why I built HORIZON SHIELD — and the architectural decision that made it actually useful.&lt;/p&gt;

&lt;p&gt;The problem nobody was solving&lt;br&gt;
Japan's residential renovation market is ¥7.35 trillion per year.&lt;br&gt;
Contractors routinely overcharge by 15–20%. The weapon they use is a single Japanese character: 一式 (isshiki) — "lump sum." One line item. No breakdown. Inside that line item, markups of 200–300% are invisible.&lt;br&gt;
After 30 years on job sites, I watched this happen to thousands of families. They had no way to verify whether a quote was fair. Then ChatGPT arrived — and homeowners started asking it for second opinions.&lt;br&gt;
They got different numbers every time.&lt;br&gt;
A contractor will weaponize any inconsistency. "The AI said ¥800,000 last week and ¥1,200,000 this week — which is it?" Game over.&lt;/p&gt;

&lt;p&gt;The architectural insight&lt;br&gt;
The problem with using an LLM for cost estimation isn't intelligence. It's determinism.&lt;br&gt;
LLMs are probabilistic by nature. Same input, different output. That's fine for creative writing. It's catastrophic when a contractor is looking for ammunition.&lt;br&gt;
So I separated the concerns completely:&lt;br&gt;
User input (natural language)&lt;br&gt;
        ↓&lt;br&gt;
    LLM layer&lt;br&gt;
  (parsing ONLY)&lt;br&gt;
        ↓&lt;br&gt;
  JCCDB v1.2.1&lt;br&gt;
(3,350 line items)&lt;br&gt;
        ↓&lt;br&gt;
  SHA-256 hash&lt;br&gt;
 of canonical input&lt;br&gt;
        ↓&lt;br&gt;
  PDF report&lt;br&gt;
The LLM touches zero numbers. It only parses what the user typed into a canonical structured format. All arithmetic happens in a versioned database.&lt;br&gt;
Every report carries a 12-character SHA-256 audit hash. Same input → same hash → same answer, every time. A contractor cannot challenge the number by asking for a rerun.&lt;/p&gt;

&lt;p&gt;The War Price Coefficient&lt;br&gt;
Material prices in Japan have been volatile since 2022. A static database goes stale in months.&lt;br&gt;
I added a War Price Coefficient (WPC) — currently ×1.0935 — that adjusts base prices for supply-chain volatility. It's updated monthly using Bank of Japan Corporate Goods Price Index (CGPI) data.&lt;br&gt;
The hash includes the database snapshot version. So when the WPC updates:&lt;/p&gt;

&lt;p&gt;Hash changes → price can change (new market conditions)&lt;br&gt;
Hash matches → price is locked (same conditions, same answer)&lt;/p&gt;

&lt;p&gt;This is what I called "version-aware idempotency" when I posted about it on HN.&lt;/p&gt;

&lt;p&gt;The open dataset&lt;br&gt;
The underlying database — Japan Construction Cost Database (JCCDB) — is open.&lt;/p&gt;

&lt;p&gt;3,350+ line items across 7 categories&lt;br&gt;
4 contractor tiers: sole trader (25–35% margin) → major firm (35–45%)&lt;br&gt;
CC-BY 4.0 — free to use, fork, cite&lt;br&gt;
Peer-reviewed preprint on engrXiv (DOI: 10.31224/7007)&lt;/p&gt;

&lt;p&gt;The commercial service and the research dataset are intentionally separated. The data is CC-BY 4.0 forever. The API pays rent.&lt;br&gt;
GitHub: ogasurfproject-jpg/japan-construction-cost-database&lt;/p&gt;

&lt;p&gt;What I learned building this at 48&lt;br&gt;
Framing a house at 16 was harder than learning to code at 47. Not because coding is easy — but because 30 years of domain knowledge is a massive shortcut.&lt;br&gt;
I didn't need to understand the construction industry. I was the construction industry. I just needed to learn how to encode what I already knew.&lt;br&gt;
The hardest part wasn't the SHA-256 hashing or the Cloudflare Workers architecture. It was deciding what not to put in the LLM.&lt;br&gt;
Every time I was tempted to let the LLM "help" with a calculation, I asked: can a contractor use this inconsistency against a homeowner? If yes, the LLM doesn't touch it.&lt;/p&gt;

&lt;p&gt;Try it&lt;/p&gt;

&lt;p&gt;Service: shield.the-horizons-innovation.com/index_en.html&lt;br&gt;
Dataset: github.com/ogasurfproject-jpg/japan-construction-cost-database&lt;br&gt;
Paper: engrXiv DOI 10.31224/7007&lt;br&gt;
Pitch deck: shield.the-horizons-innovation.com/pitch.html&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the architecture, the dataset, or what it's like to ship your first product at 48.&lt;br&gt;
— Toshi&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>career</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
