cd /news/ai-agents/open-source-reference-implementation… Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-164170] src=github.com β†— pub= topic=ai-agents verified=true sentiment=↑ positive

Open-source reference implementation of the Agent Name Service (ANS)

The Agent Name Service project published `ans`, an open-source reference implementation of a registry and transparency log that assigns AI agents versioned DNS-style names such as `ans://v1.0.0.my-agent.example.com` and backs each registration with an append-only Merkle log, SCITT COSE_Sign1 receipts, and private-CA identity certificates for mTLS between agents. The release ships four binaries β€” `ans-ra` (Registration Authority, port 18080), `ans-tl` (Transparency Log, port 18081), `ans-verify` (offline CLI verifier) and `ans-dns` (dev-only authoritative DNS server, port 15353) β€” and requires Go 1.26+, openssl, curl and jq to build via `make build`. The project states the wire shape (event envelope, SCITT COSE receipts, sumdb-note checkpoints and `/root-keys` format) is a public contract that offline verifiers and TL clients depend on byte-for-byte.

read7 min views2 publishedNov 4, 2026
Open-source reference implementation of the Agent Name Service (ANS)
Image: Michielbdejong (auto-discovered)

ans is an open-source implementation of the Agent Name Service (ANS), a registry + transparency log for discovering and verifying AI agents by name. Every registered agent gets:

  • A versioned DNS-style name: ans://v1.0.0.my-agent.example.com
  • A publicly-auditable event history (append-only Merkle log)
  • SCITT COSE_Sign1 receipts proving the agent's state at a point in time
  • Identity certificates signed by a private CA for mTLS between agents
  • Optional BYOC server certificates + pinned TLSA records

The wire shape β€” event envelope, SCITT COSE receipts, sumdb-note checkpoints, /root-keys format β€” is the public contract. Offline verifiers and TL clients depend on it byte-for-byte.

Binary Port Role
ans-ra 18080 Registration Authority β€” accepts registrations, issues identity certs, tracks lifecycle
ans-tl 18081 Transparency Log β€” durable Merkle-tree append log, serves badges + receipts
ans-verify β€” Offline CLI verifier for SCITT COSE receipts and status tokens
ans-dns 15353 Dev-only authoritative DNS server with install /clear subcommands for local end-to-end verify-dns testing

Both daemons (ans-ra, ans-tl) serve Swagger UI at /docs β€” browse http://localhost:18080/docs (RA) and http://localhost:18081/docs (TL) after make build && scripts/demo/start.sh. The raw OpenAPI bytes live at /docs/openapi.yaml on each host.

To call protected endpoints from Swagger UI's "Try it out" button: click Authorize in the top-right and paste the static API key. The quickstart defaults are:

  • RA: ans-dev-key-change-me (fromconfig/ra-local.yaml )
  • TL: tl-internal-key (fromconfig/tl-local.yaml )

The RA writes signed events; the TL verifies, ingests, and publishes them. An operator can run just the TL (for read-only verification) or both together (full registry).

For a native Ubuntu host with systemd, Caddy, OIDC, and Let’s Encrypt, see the RA/TL deployment guide.

git clone https://github.com/agentnameservice/ans
cd ans
make build                        # builds bin/ans-ra, bin/ans-tl, bin/ans-verify, bin/ans-dns
scripts/demo/start.sh             # starts both daemons against ./data/demo
scripts/demo/run-lifecycle.sh     # registers an agent, verifies the receipt end-to-end

Expected output (last few lines):

━━━ 13. Offline verify (bin/ans-verify) ━━━
  βœ“ Fetched 2 verification key(s) from /root-keys
  βœ“ 1602 bytes (Content-Type: application/scitt-receipt+cose)
  βœ“ VERIFIED (kid <8-char-hex> matched key directly)

Tear down with scripts/demo/stop.sh or scripts/demo/stop.sh --clean to wipe the data directory.

The default DNS verifier in config/ra-local.yaml is noop, which accepts any DNS state. To exercise the real lookup verifier end-to-end without touching public DNS, run the bundled authoritative server:

bin/ans-dns serve --addr 127.0.0.1:15353

bin/ans-dns install --api-key <api-key> http://localhost:18080 <agentId>


bin/ans-dns clear <agentId>

ans-dns is dev-only. Production deployments use the lookup verifier against the operator's existing authoritative DNS.

make docker-build

docker compose up --build

Compose persists ra-data, ra-keys, and tl-data volumes so restarts are cheap; docker compose down -v wipes them. ans-verify and ans-dns are local-development binaries and ship from make build only β€” they are not in the compose stack.

Hexagonal / ports-and-adapters. The domain model (internal/domain) has zero framework or storage dependencies. Services depend on port interfaces (internal/port) which adapters in internal/adapter/ implement against real drivers.

cmd/ans-ra/main.go ───────┐
                          β”œβ”€β–Ί internal/ra/service ─► port.AgentStore     ─► internal/adapter/store/sqlite
cmd/ans-tl/main.go ────────                          port.KeyManager     ─► internal/adapter/keymanager
                          β”‚                          port.DNSVerifier    ─► internal/adapter/dns
                          └─► internal/tl/service ─► port.RenewalStore   ─► …

cmd/ans-verify/main.go ─► internal/tl/receipt   (Verify, ExtractKID, ComputeLeafHash)
cmd/ans-dns/main.go    ─► miekg/dns authoritative server + RA-driven record install/clear

See spec/api-spec-v2.yaml + spec/api-spec-tl-v2.yaml for the authoritative OpenAPI contracts.

Event envelope (signed on the RA side):

{
  "payload": {
    "logId": "<uuid>",
    "producer": {
      "event": { "ansId": "...", "ansName": "ans://...", "eventType": "AGENT_REGISTRATION", ... },
      "keyId": "ans-ra-signer",
      "signature": "<detached JWS over JCS(event)>"
    }
  },
  "schemaVersion": "V1",
  "signature": "<TL attestation JWS>",
  "status": "ACTIVE"
}

Receipts (SCITT COSE_Sign1, RFC 8152 tag 18):

  • Protected header: {1:-7 (ES256), 4:<kid 4-byte SPKI hash>, 395:1 (RFC 9162), 15:{1:<issuer>, 6:<iat>}}
  • Unprotected header: {396:{-1:treeSize, -2:leafIndex, -3:path[][], -4:rootHash}}
  • Payload: the JCS-canonical event bytes (the same bytes the leaf hash is computed over)
  • Signature: ES256 over the RFC 8152 Β§4.4 Sig_structure

Leaf hash: RFC 6962 β€” SHA-256(0x00 || payload).

Root-keys endpoint (GET /root-keys) emits the sumdb-note verification-key format (one line per key):

<origin>+<keyhash-hex>+<base64(0x02 || SPKI-DER)>

where keyhash-hex is the first 4 bytes of SHA-256(SPKI-DER) as zero-padded big-endian hex. Verifiers match this against the kid in the receipt's protected header for O(1) key lookup.

The TL signs every outbound artifact (checkpoints, attestations, receipts, status tokens) with a single ECDSA P-256 key. /root-keys advertises exactly one line.

Any third party can verify an agent's state cryptographically without trusting the TL operator beyond the advertised verifier keys:

ans-verify -url https://tl.example.com -agent <agentId>

ans-verify -pubkey ./trusted-tl.pub -agent <agentId> \
  -url file://./receipt.cbor

The verifier:

  1. Fetches /root-keys (or reads a pinned PEM).
  2. Fetches the receipt CBOR.
  3. Parses COSE_Sign1, extracts the inclusion proof from label 396.
  4. Maps the receipt's 4-byte kid β†’ verifier key in O(1).
  5. Walks the Merkle path from SHA-256(0x00 || payload) torootHash ; rejects any mismatch.
  6. ES256-verifies the signature over Sig_structure.
  7. Cross-checks that the receipt's computed leaf hash matches the badge's merkleProof.leafHash (same leaf in the same tree).
  8. For every metadata hash the leaf attests, fetches the descriptor at the URL the attested SVCB row carries (key65400 ) and compares its SHA-256 with the attested value. A mismatch fails verification; an unreachable descriptor is reported only.-check-metadata=false skips this step.

Both daemons read YAML via koanf. The shipped quickstart configs are under config/:

  • config/ra-local.yaml β€” local-filesystem dev config (SQLite, self-CA, noop DNS)
  • config/tl-local.yaml β€” same for the TL
  • config/ra-docker.yaml /config/tl-docker.yaml β€” compose-friendly paths + log format

Every YAML key is overridable via env vars using the _ β†’ . dotted form (e.g. ANS_RA_SERVER__PORT=18090).

Two providers ship today:

  • Static API key (quickstart): accepts bothAuthorization: Bearer <key> (ans-native form) andAuthorization: sso-key <apiKey>:<apiSecret> (the form generated SDK clients use). The static key is treated as admin, so it can hit/internal/v1/producer-keys .
  • OIDC : standard bearer-token validation against an issuer URL. Configure admin groups withauth.oidc.admin-groups[] .

Every GET under /v1/agents/, /v1/log/, and the tlog-tiles paths (/checkpoint, /root-keys, /tile/*) is anonymous when auth.public-read: true (the default for the TL quickstart config) β€” verifiers never need credentials.

Every external dependency lives behind a port interface in internal/port/. Defaults target local development; you can swap any of them at the port boundary without touching the service layer.

Concern Default adapter Common production swap
Auth Static API key + OIDC OIDC against your IdP
Identity cert issuance File-backed self-signed CA Cloud KMS-backed CA
Server cert issuance Self-signed CA + BYOC PEM ACME / public CA / BYOC PEM
DNS verification noop andlookup (real miekg/dns queries with DNSSEC AD-bit propagation) lookup against your authoritative DNS
Signing keys File-based ECDSA P-256 Cloud KMS adapter
Storage SQLite Postgres / managed RDBMS

Cloud-KMS, Postgres, and managed-DNS adapters are contribution-shaped β€” see CONTRIBUTING.md.

make test         # unit tests
make test-cover   # with coverage, enforces internal β‰₯90%, domain 100%, crypto β‰₯95%
make test-race    # race detector
make check        # fmt + vet + lint + test-cover (pre-commit gate)

Test coverage targets: 100% on internal/domain and internal/crypto (with annotated SAFETY exceptions for unreachable defensive branches), β‰₯90% overall across internal/ (enforced by make test-cover). Adapter packages under internal/adapter/ are the primary remaining gap β€” contributions welcome, see CONTRIBUTING.md.

Contributing guide: CONTRIBUTING.md. Project conventions for code generation tools: CLAUDE.md.

Shipped: full V1 + V2 RA surface (register, verify-acme, verify-dns, revoke, list, detail, identity/server cert GET + POST, CSR status, renewal lifecycle with BYOC + CSR paths, renewal expiry sweep) β€’ TL ingest, badge, audit, SCITT COSE receipts (tag 18, ES256), SCITT status tokens (1h TTL, terminal-state aware), root-keys (sumdb-note format), JSON + raw checkpoint, paginated checkpoint history, schema lookup, Merkle tiles β€’ Producer-key admin CRUD (/internal/v1/producer-keys) β€’ ans-verify CLI that fetches + verifies both receipts and status tokens β€’ ans-dns dev DNS server with install / clear for verify-dns end-to-end testing.

Follow-up opportunities (community): cloud-KMS / Postgres / managed-DNS adapters, real ACME DNS-01 verifier wiring, additional schema versions.

MIT License. See LICENSE.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @agent name service 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/open-source-referenc…] indexed:0 read:7min 2026-11-04 Β· β€”