How three copies drifted
The duplication had already become a correctness defect, in three ways, none of which would have produced an error.
- Two routes built the TXT record value from their own local prefix constant instead of calling
verificationRecord(). A change to the record's shape would have left them checking DNS for a value AgenID no longer asks for, matching nothing, forever. /api/verify-dnscarried the disclosure that domain control is evidence and not a level. Its byte-for-byte twin/api/dns/verifydid not, so an honesty rule was enforced on one copy of the same endpoint./api/dns/detectdid spec-literal Domain Connect discovery with no GoDaddy fallback, so two endpoints disagreed about whether the same domain supported one-click setup.
What was deleted, and why one deletion matters more
POST /api/dns/verify was a copy of POST /api/verify-dns. POST /api/dns/detect duplicated provider detection that POST /api/domain/status does better. POST /api/dns/auto-add held a Cloudflare or GoDaddy API credential and wrote to the operator's zone.
The last one was deleted rather than left unconfigured, and that is the substantive decision. A credential-holding write path sitting idle in the tree invites a future session to finish it. AgenID does not hold write access to customers' DNS zones; that credential is exactly what the product argues against. A test now forbids any source file in packages/web from reading a DNS provider's API credential or calling a provider write API, and the whole /api/dns namespace must stay empty.
The new guard then found two more copies that a search had missed, in the UI layer. DomainFlow fell back to a hand-built record value before the first server response arrived, so an operator could have copied and published a stale record and waited forever. Every cell of that record table now comes from the same builder the server uses.
The key-document probe no longer says verified
POST /api/domain/status also reports whether the operator publishes a key document at its .well-known path. It used to report verified whenever the operator's server answered 2xx. Most single-page-app hosts answer every unknown path with 200 text/html and the homepage, and the operator domain of AgenID's own pilot does exactly that, so its homepage was reported as a verified key document, live.
key_discovery.status is now absent, invalid, published or unreachable. published requires a 2xx application/json body, read to at most 64 KiB, that parses as a strict KeysDocument naming the probed domain as controller_domain. invalid carries a reason. Verified is deliberately not a state: the probe knows a domain, not an agent, and never compares keys against the registry. That comparison is the verifier's job. A new suite of 24 tests reproduces the single-page-app response end to end and was shown to fail against the old implementation.
Separately, POST /api/verify-dns stopped echoing caller input when the resolver fails. Its 502 body now returns fixed text plus the error code.
An OpenAPI document that describes every route
The OpenAPI document now covers 13 deployed routes, up from 4. The old rule was never to document a route that does not exist. The new half of the rule exists because omission is a claim too: a contract describing 4 of 13 routes told a security reviewer that two unauthenticated write paths, a credential-forwarding proxy and two outbound DNS amplifiers did not exist.
packages/web/test/dns-surface.test.ts now fails on a route handler with no documented path and on a documented path with no handler. The documentation check additionally asserts that the three deleted routes return 404 in production, because a claim that passes whether a route was deleted or merely undocumented is a claim nobody is checking.
What this does not do, and how to try it
One behavioral difference remains, on purpose: a resolver failure is 502 on /api/verify-dns, which answered a direct question, and pending on /api/domain/status, which will ask again in six seconds. None of these endpoints returns a verification level. Which agent is acting is not the same question as what it is allowed to do. AgenID answers the first. Protocol v1.1.1 defines no signed authorization object, so permission stays with the operator and with the party on the other side.
Fetch https://www.agenid.com/api/v1/openapi.json to see every deployed path. To see the probe from the operator's side, open domain verification and enter a domain you control.