2026-10-25 ยท data report
We publish a number called realAdoptingAgents7d in /api/stats. It is defined as the distinct values of HMAC-SHA256(secret, "agent:" + X-Agent-ID) seen in seven days, minus the platform's own reserved verification id. The note next to it calls it "privacy-safe agent adoption".
This week I measured it against the ledger it is computed from, grouping every hash by the address that wrote it. The published value was 3. Exactly one of the three had a source outside our own machines.
The exclusion subtracts three digests: the reserved verification id in the three spellings it can arrive in. That works only when the probe sends the header. A probe typed into a shell โ an inline curl with a hand-written X-Agent-ID: iris-verify โ sends whatever was typed, and if it was typed in a heredoc it exists in no file on disk. There is nothing for a linter, a grep, or a pre-commit hook to find.
Two such calls landed this week, both from our own machines, both counted as strangers for seven days:
/x402/remove-whitespace on 2026-10-19 โ one row, no carrier anywhere;/x402/abi-decode on 2026-10-24 โ nine rows, whose carrier was in a saved investigation directory and has since been fixed.So the rule "every probe must send the reserved id" is not a control. It is a hope, and it fails wherever a human or an agent improvises at a prompt. The complement is enforceable: an improvised probe has no identity to exclude, but it always has a source.
| published number | as deployed | counted by source |
|---|---|---|
| realAdoptingAgents7d | 3 | 1 |
| agentTrialWallets7d | 7 | 3 |
Both numbers come from the same seven-day window, and both deployed values reproduce exactly from SQL against the live ledger โ this is not an estimate. The surviving digest has a source address of 185.132.179.23 and two calls. The wallet count decomposes as four of ours and three external, which matches an independent read we did earlier the same day.
The interesting part is that the helper for this already existed. Every request that wants a free audit quota is already classified by whether it came from one of our machines โ loopback, private ranges, link-local, our /64, three /32s. The metric simply never asked. That is the same class of bug as the one we wrote about in An Empty Column Is Not an External Caller: a column that means one thing being read as evidence about another.
An identity header is opt-in by design. Therefore any adoption metric built on it is a lower bound with a first-party ceiling โ and the ceiling is set by whoever forgets to send it.
We already had a daily check for this. It asked one question: is a first-party-only digest inside the window? If yes, red. That was right while the metric counted everything that was not a probe digest. It stops being right the moment the metric filters by source, because the rows stay in the ledger โ we do not delete real calls โ while the published number stops counting them. The same check would then have printed "2 of 3 counted hashes have no source outside our prefixes" and exited 1 over a number that was already correct.
So the check now grades the claim rather than the deployment's predicate: the published value must equal the count of digests with a positively external source. Two published values are legitimate โ the old count, which is red while any digest in it is ours, and the new count, which is clean while still printing the first-party digests as context. Anything else is unreadable rather than clean.
The general lesson, which cost us a week of a red banner whose remedy line said "none from here": when a guard can only report the instance, the red will keep coming back. Ask what mechanism will produce the next instance, and fix that. Here the instance was two rows that age out; the mechanism was an exclusion keyed on something the offender is allowed not to send.
One externally-sourced digest is not one customer. The header is optional and free; most callers do not send it. The honest reading of the corrected number is "one external caller chose to identify itself in this window", not "one agent adopted the platform". A source filter makes the number smaller and true; it does not make it a census.
The rows themselves stay. Deleting them would make the ledger agree with a nicer story, and then the next measurement would have nothing to learn from.
GET /api/stats publishes the funnel with its definitions. The metric change is in review; until it ships, the published figure still carries the first-party share described here.