Extension proposal: DANE-anchored identity for Agent Cards (DNSSEC TLSA binding for the signing key)
- Dominant language
- Shell
- Stars
- 25.7k
- Forks
- 2.6k
- Avg merge
- 3d 6h
- Merged PRs (30d)
- 16
Description
## Abstract
This proposes an A2A extension that lets an Agent Card prove its signing key belongs to the agent's domain by anchoring that key in DNS, as a DNSSEC-signed DANE-EE TLSA record. Verification runs from the DNS root, with no central identity issuer and no prior key exchange. The agent keeps its own self-sovereign key. DANE only answers the question the current design leaves open: does this key provably belong to this domain.
I run [Whisper Security](https://whisper.online) and operate [AS219419](https://as219419.net), and we have this deployed in production DNS today. There is a live endpoint at the bottom of this issue that you can verify in about thirty seconds with `dig` alone. I am new to the A2A community, so please steer me if a fresh issue is not the right venue or if this should take a different shape; I would rather learn your process than assume it.
## Motivation
Section 8.4 lets an Agent Card carry a JWS. The protected header can include `kid` and, optionally, `jku`, a URL to a JWKS fetched over HTTPS. That establishes that the key came from some HTTPS endpoint, and that a WebPKI CA vouched for the TLS certificate on that endpoint. It does not establish that the key belongs to the domain named in the card. A verifier still has to trust whoever controls that endpoint, or whichever CA signed it. The spec is honest about this: it leans on channel security and a client-side trusted key store rather than a domain-level key binding.
The current debate splits into two camps and both hit the same wall. A centralized issuer (getagentid.dev is the clearest example) can vouch for an agent, but then everyone has to trust that issuer, and you have reintroduced a CA. A self-sovereign Ed25519 key (see #1829) is clean and needs no issuer, but a bare public key cannot tell you which domain it speaks for. Both are stuck on "who verifies the verifier."
There is a third anchor that this thread has mostly not considered: DNSSEC. It is already a global, delegated, root-anchored trust hierarchy, and every domain owner already sits inside it.
## Proposed mechanism
Publish the agent's signing key as a DANE-EE TLSA record under a DNSSEC-signed name:
- The record is usage 3, selector 1, matching type 1 (`3 1 1` in RFC 7218 mnemonics: DANE-EE / SPKI / SHA-256). It carries the SHA-256 of the key's SubjectPublicKeyInfo.
- It lives under a DNSSEC-signed name derived from the agent's endpoint, so a validating resolver returns it with the AD bit set.
- Verification is: DNSSEC-validate the TLSA, then confirm the Agent Card's JWS signing key has the SPKI hash the record commits to. Trust chains to the IANA root KSK, not to a WebPKI CA and not to an agent-identity issuer.
DANE base is RFC 6698, operational guidance in RFC 7671. Revocation is removing or replacing the TLSA record, and a live-DNS validator sees the change within the record's TTL. To be precise: that is DNS-timed revocation, not offline or stapled revocation, and I would not claim otherwise.
## How it fits the extension model
This is a data-only extension. The agent declares the extension URI in `capabilities.extensions[]` and carries the anchoring details in `metadata`. It changes no core types and adds no enum values, which keeps it inside the documented extension limitations. It composes with 8.4 rather than replacing it: the JWS stays, DANE just makes the signing key's domain binding checkable from the root. It has no dependency on a specific key algorithm, so it sits fine alongside the Ed25519 direction rather than competing with it.
## Verify the mechanics yourself
We anchor per endpoint (one DNSSEC-signed name and TLSA per address). A live example, sixty-second TTL, so re-verify before relying on it:
`dig +dnssec TLSA _443._tcp.ae3b051ff3bf7f478.tdc38e7c55bad3306a92b830f9bb1e4f9.agents.whisper.online`
You should get a `3 1 1 ...` record returned with the AD flag set, an AAAA of `2a04:2a01:b69a:6717:e3b0:51ff:3bf7:f478`, and an active RDAP record for the address. No account, no private CA, no prior exchange.
## Honest state of it
Two things I will not overstate. First, I have written this up as an Internet-Draft, `draft-ranjbar-dane-anchored-identity`, which I am bringing to the IETF DANCE working group. It is not on the datatracker yet, so I am not linking it here; I will share it once it is public. Second, we keep key history in a tamper-evident, signed log, but the independent witness that would make that history externally checkable is still being landed, so I am describing it as tamper-evident and signed, nothing more.
## What I am asking
Interest from a maintainer in sponsoring this as an `experimental-ext`. I am happy to publish the specification under a stable identifier and provide a reference implementation, which I read as the graduation requirements. This also looks relevant to the credential-discovery work in #1990, and I would rather align with it than route around it.
Kaveh
[Whisper Security](https://whisper.security)
Contributor guide
Assessment
This issue has not been assessed yet.