Verify that an AI agent actually belongs to the domain it claims — and that the caller holds its key.
agt://apple.com/siri either proves it is Apple's agent, or it does not. There is no third answer, and you never have to trust us to find out.
npm install bindagtimport { verifyOnChain } from "bindagt";
// No API key. No account. No BindAgt server involved.
const result = await verifyOnChain("agt://bindagt.com/hello");
console.log(result.valid); // true
console.log(result.domain); // "bindagt.com"
console.log(result.domainStatus); // "active"That call reads the public registry directly. Verification is free, open, and permissionless — it works whether or not BindAgt exists tomorrow.
Recorded against mainnet with the domain's real key — no test mode. Case 2 is the one to watch: the attacker holds the genuine identity document, downloaded from the public URL, and is still refused.
Two commands. Nothing to install, no account, no clone. Against mainnet and the live API.
agt://aiaam.xyz/id is a real agent on a real domain. Its identity document is
public — verify_url serves it to anyone, unauthenticated, by design. That
is what keeps verification free. Take it:
DOC=$(curl -s https://api.bindagt.com/v1/verify/0x84ac9884ae27a749e34483dcc01f2a46e8b05c53ba9684cf9362607c66b8f8fb \
| base64 | tr -d '\n' | tr '+/' '-_' | tr -d '=')Those are the genuine bytes, the same ones that agent presents. Now be it:
curl -s -X POST https://api.bindagt.com/v1/demo/guard \
-H "Content-Type: application/json" -H "AGT-Identity: $DOC" \
-d '{"hello":"world"}'{
"accepted": false,
"code": "missing_possession_signature",
"reason": "The document is real, but nothing proves you hold this domain's key. AGT-9303 §7.3 requires a signature over this request.",
"agentId": "agt://aiaam.xyz/id",
"reached": "identity_verified"
}identity_verified is the important word. The verifier agreed the document is
authentic and the domain is in good standing — and refused you anyway.
Now watch the owner make the same call, signed with the domain's key:
curl -s -X POST https://api.bindagt.com/demo-agent/call \
-H "Content-Type: application/json" -d "{\"document\":\"$DOC\"}"{
"signed": true,
"verdict": { "accepted": true, "code": "accepted", "reached": "possession_checked" },
"covered": { "method": "POST", "url": "https://api.bindagt.com/v1/demo/guard", "expiresInSeconds": 60 }
}Same document. Same endpoint. Opposite answer. Holding the document proves nothing — under AGT-9303 §7.3 the caller signs each individual request with the domain's key (RFC 9421), and that signature covers this method, this URL and this body for 60 seconds. Copy it and you are holding someone else's expired proof of someone else's request.
Prefer a browser? aiaam.xyz does the same thing with
buttons, and lets you type any agt:// identifier to see what the registry
actually says about it.
Run the four cases locally, including replay against a different path
git clone https://github.com/bindagt/bindagt && cd bindagt/examples/agent-to-agent
npm install
node demo.mjs1. The owner calls — presentAs("agt://aiaam.xyz/id")
→ ACCEPTED possession proven
2. An attacker replays the REAL document from the public verify_url
→ REJECTED missing_possession_signature
3. An attacker captures a signed request and replays it against /admin
→ REJECTED possession_not_proven
4. Someone calls with no identity
→ REJECTED missing_header
Without the domain's private key the demo prints a TEST MODE banner and signs
with a generated key, stubbing the lookup for that key only; identity and domain
standing still come from mainnet. Cases 2, 3 and 4 do not depend on which key is
used — the attacker has no key at all. Case 1 needs the real key, which is why
the two curl commands above are the honest version of it: those run against the
deployed agent, signing with the actual key aiaam.xyz registered on-chain.
A note on which agent to test with. Use agt://aiaam.xyz/id for anything
involving a signature. agt://bindagt.com/hello is anchored and verifiable, but
its control key is a Passkey (keyType 1), whose private half cannot leave
the authenticator that created it — so it can prove who it is and can never
prove possession. You do not have to take our word for that either:
curl -s "https://api.bindagt.com/v1/lookup?agent=agt://bindagt.com/hello" \
| grep -o 'canSignPossessionProofs":[^,}]*'
# canSignPossessionProofs":falseUntil 29 July 2026, case 2 passed. We found it in our own review, stopped the launch, and built §7.3. If you find the next one, open an issue — we would rather hear it from you than from Hacker News.
Any agent can claim to be Apple's. Any tool can claim to be your bank's. Today there is no universal, vendor-neutral way to check whether an AI agent genuinely belongs to the organisation it says it does.
BindAgt fixes the origin question — and only the origin question. It does not score agents, rank them, or judge their behaviour. It answers two things, cryptographically: does this agent belong to this domain, and does the caller hold its key?
- A domain owner proves control of their domain with a DNS
TXTchallenge (the same model Let's Encrypt uses). - Once verified, the agent's identity is anchored to a public, immutable registry on Ethereum L1.
- Anyone, anywhere, can verify that anchor directly — no account, no API key, no permission.
- When an agent calls out, it signs the request with the domain's key. The receiver checks that signature against the key in the registry.
The trust root is DNS, which the entire internet already relies on. BindAgt inherits that trust rather than inventing a new one.
| Method | What it does | Needs |
|---|---|---|
verifyOnChain() |
Reads the public registry directly. Trustless. | Nothing — talks to a public RPC |
verify() |
Same answer via the BindAgt API (cached, faster at scale). | Nothing to start; a free key raises rate limits |
Both return the same shape:
{
valid: true,
agentId: "agt://bindagt.com/hello",
domain: "bindagt.com",
domainStatus: "active",
agentType: "public",
anchoredAt: "2026-07-22T11:44:47.000Z",
source: "l1"
}import { loadSigningKey } from "bindagt";
import { presentAs } from "@bindagt/openai";
// Your key, decrypted locally from ~/.bindagt/key.enc using
// BINDAGT_KEY_PASSWORD. Synchronous, and it never leaves the process.
const { key, keyType } = loadSigningKey();
// Signs this request with it (RFC 9421, AGT-9303 §7.3).
const headers = await presentAs("agt://acme.com/support-bot", {
signer: key,
keyType,
});import { verifyIdentityGuardrail } from "@bindagt/openai";
// Requires the signature by default. A document on its own is refused.
const out = await verifyIdentityGuardrail(req);You hold the signing key. BindAgt never sees it.
npx bindagt register example.comThe CLI walks you through the DNS challenge and anchors your first agent. The first 100 domains are free.
Each of these is a reasonable choice, and we adopt two of them.
did:web— publish your key at a URL on your own site. Free, no new authority, works today, and the honest baseline. What it cannot do is tell you what the key was yesterday: there is no history, no revocation trail, and if an attacker owns the web server they own the key file too. BindAgt records which key, and since when, somewhere the compromised server does not control.- Sigstore / Rekor — the real architectural neighbour: a mature, tamper-evident transparency log. It is built for signing software artefacts, not for agent identity rooted in DNS with a lifecycle (suspension, expiry, transfer behind a time-lock). We claim no novelty over Rekor.
- A2A (Agent2Agent) — the channel agents talk over. Not a competitor; we speak it.
verifyAgentCard()bridges the two. A2A carries the call, BindAgt says whose key signed it. - SPIFFE — secures workloads inside one organisation. BindAgt verifies identity across organisations with no prior relationship.
- ERC-8004 — mixes identity with reputation scoring. BindAgt refuses reputation by design: origin is a fact, reputation is an opinion.
In one line: others prove the request was not altered; BindAgt proves whose key signed it, and whether that key has ever changed.
- It does not tell you an agent is trustworthy, competent, or safe. Only that it is who it says it is.
- It does not do reputation, ranking or scoring, and it never will.
- Ephemeral delegated agents are specified in AGT-9303 §6 but not implemented — the SDK rejects them, which is the behaviour the spec requires of a receiver that does not support them.
- It binds an identity to a request, not to a message. §7.3 is RFC 9421 — HTTP Message Signatures. A system receiving an HTTP call can verify possession, and so can a chat platform at the moment a bot connects. A person reading an email or a chat message cannot, and we are not going to pretend otherwise: the sender is gone by the time you read it, so there is nobody left to demand a fresh signature from. A signature over the content instead would travel with the content — forward the message and it still verifies — which is the bearer credential this whole project exists to replace. Worth saying plainly: nobody verifies cryptography by hand anyway. You have never inspected a TLS certificate chain; your browser does it and shows you a padlock. The check exists here today; the padlock does not, because no mail or chat client has integrated it yet.
- It cannot protect a domain that never registered. If nobody has registered
amazon.com, a bot presenting an "amazon.com agent passport" is presenting something that does not exist — anyone can establish that in one call, and that dismantles the impostor. But nothing stops the claim from being made in the first place. Andamaz0n.comwith a zero registers perfectly well: you still have to read the domain, exactly as with URLs today. No registry fixes that.
- Mainnet: live.
0x680db4533ef1fdc99bfedd441351d56012e0e7c9 - SDK:
bindagt@0.2.1on npm, with §7.3 possession. - Standard: AGT-9303 v1.7.
- Sepolia is internal QA only and is not part of the public system.
Want to help? Add AGT-9303 verification to a framework you use (LangChain, the OpenAI Agents SDK, CrewAI, …). Open an issue and we will help you scope it.
- The standard:
AGT-9303 - Docs: bindagt.com/docs
BindAgt — open protocol for AI agent identity.
