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.
Our agent's identity document is public. verify_url serves it to anyone, with no authentication, by design — that is what keeps verification free.
Download it right now:
curl https://api.bindagt.com/v1/verify/0xb2069e0c4a378a182a682d33ab7f59fcb38c7f15e299f4d3b196a5898e2fe07aThat is the genuine article — the same bytes our own agent presents. Now try to be us with it.
git clone https://github.com/bindagt/bindagt && cd bindagt/examples/agent-to-agent
node demo.mjsTEST MODE. Signing with a generated key, and the key lookup for it is stubbed.
Identity and domain status still come from mainnet.
1. The owner calls — presentAs("agt://bindagt.com/hello")
→ ACCEPTED agt://bindagt.com/hello · possession proven
2. An attacker replays the REAL document, downloaded 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 key you get the banner above: the demo generates a key so
you can still run it, and identity and domain standing are read from mainnet
either way. The owner runs the same file unchanged with BINDAGT_KEY_PASSWORD
set. Case 2 is the one to watch, and it does not depend on which key is used —
the attacker has no key at all.
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 is valid for 60 seconds against that one request. A copied document, a captured header, a replayed call against a different path — all refused.
Until 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 moves the message, 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 message 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.
- 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.