2026-07-15
DNSid Can Prove an Owner. It Cannot Authorize the Agent.
A 40-case DNSid verifier separates DNS origin, entity signature, bilateral issuance, key continuity, fresh status, historical evidence, and application authorization.
DNS-anchored identity evidence
DNSid can help answer who is accountable for an agent. It cannot answer whether that agent may read a mailbox, spend a budget, or invoke a production tool. Treating identity as authorization is the most dangerous shortcut in this draft protocol.
The current DNSid document is an Internet-Draft, not a deployed standard. Its useful idea is narrow: publish a signed record at _dnsid.<agent-fqdn>, connect that record to an entity key and an operational key, require a bilateral issuance event, and consult fresh lifecycle status before trusting the binding. Each piece contributes evidence. None grants application permission.
A deterministic local verifier exercised this boundary with 40 synthetic cases derived from draft -01. Two fully validated cases returned IDENTITY_BOUND, one explicit policy exception returned reduced assurance, one historical case returned evidence only, and 36 stopped. No DNS query, DNSSEC validation, HTTPS request, live key, credential, agent, or authorization operation was performed.
c316db2c0bd06cb31a669e21b2d325bf24573edf2b668f605d8af370e203c6ee.The draft binds accountability, not capability
The DNSid record begins with v=DNSid1 and links an agent FQDN and global identifier to an entity key, an operational key, a status service, a lifecycle log, and a signature. That gives a verifier several claims to check. A valid entity signature says the record content was signed by the key published at the entity-key endpoint. A bilateral issuance event says both entity and operational keys consented to the binding. Status says whether the identity is currently active. DNSSEC, when valid, adds DNS-origin integrity.
Those claims are related, but not interchangeable. RFC 4033 describes DNSSEC as data-origin authentication and integrity for DNS data; it does not provide confidentiality or application authorization. The DNSid draft is equally careful: without DNSSEC, the record signature alone does not authenticate the DNS origin, and it does not prove that the operational identity consented. The issuance event closes the consent gap, not the permission gap.
A verifier should return an identity result that the policy engine can consume. It should never return “authorized” merely because the agent has a stable name and a signed lineage.
Testing: forty mutations met the verifier
The base case used one synthetic TXT record at _dnsid.billing-agent.example.test. It required version DNSid1 first; unique tags for global ID, entity key, operational key, status, lifecycle log, and signature; HTTPS endpoints; validated DNSSEC; a valid entity signature; a matching bilateral issuance event; intact operational-key continuity; a matching TLS name; and a fresh ACTIVE status.
Each negative case changed one boundary. The verifier is ordinary JavaScript with an explicit decision order. It has no network access, no model call, and no fallback that converts missing evidence into trust.
| Changed evidence | Observed result | Reason to stop |
|---|---|---|
| Zero or multiple TXT records | BLOCK_RRSET_AMBIGUITY | The draft requires one unambiguous record, not a best-match parser. |
| Version misplaced, required tag absent, or tag duplicated | BLOCK_VERSION or BLOCK_TAGS | Canonical parsing must finish before signature or endpoint work. |
| DNSSEC failed or was absent without an explicit exception | BLOCK_DNSSEC | Failure is unusable; absence is a distinct reduced-assurance state. |
| Entity signature missing or invalid | BLOCK_SIGNATURE | The record content is not bound to the advertised entity key. |
| Issuance event missing, mismatched, or not countersigned by both keys | BLOCK_ISSUANCE | A record signature alone does not establish bilateral consent. |
| Operational key history broke | BLOCK_KEY_CONTINUITY | A current key cannot silently replace the issued lineage. |
| TLS name differed from the agent FQDN | BLOCK_TLS_IDENTITY | The live endpoint must match the identity being verified. |
| Status was stale or unavailable | HOLD_STATUS | Unknown is not active; freshness is part of the decision. |
| Identity evidence was offered as permission | BLOCK_AUTHORIZATION_CONFLATION | Identity cannot authorize a tool, record, payment, or tenant action. |
This is a policy fixture, not a DNSid interoperability suite. It shows that a proposed verifier can preserve the draft's evidence boundaries. It does not prove live resolver behavior, DNSSEC deployment, JWKS hosting, key custody, lifecycle-service availability, or compatibility with another implementation.
One RRset means one parse result
One RRset, one record, or no claim.
Discovery begins with a TXT query at the fixed owner name. The draft says the RRset must contain exactly one TXT RR and exactly one DNSid record. Zero records do not identify an agent. Multiple records are ambiguous. A parser should not pick the first value, merge tags across records, or prefer the value whose signature happens to verify.
Parse the version first. Reject duplicate tags. Preserve unknown tags in the canonical signature input even when their semantics are not understood. Then require the tags needed by the verifier profile. Endpoint URLs should be HTTPS before any fetch is attempted.
record = parseExactlyOneDnsidRecord(rrset)
require(record.version === "DNSid1")
requireUniqueRequiredTags(record)
requireHttpsEndpoints(record)
verifyDnsOriginAssurance(rrset)
verifyEntitySignature(record)
This order is operationally useful. It prevents an attacker from driving endpoint requests with a malformed record, and it gives logs a stable failure class. A signature error should not be reported as a status outage; a multi-record RRset should not reach either service.
Failure modes: DNSSEC has three outcomes
Three DNSSEC states matter. A validated chain gives origin authentication and integrity for the DNS data. A failed validation makes the record unusable. An unsigned zone is not the same as a failed signature, but it lacks DNS-origin proof. The DNSid draft permits a WebPKI-assisted path in that case: fetch the entity key over HTTPS, verify the record signature, and still perform bilateral issuance checks.
Calling both validated and unsigned paths “verified” erases a material difference. The fixture emits IDENTITY_BOUND_REDUCED_ASSURANCE only when a local policy explicitly permits the unsigned path. Otherwise DNSSEC absence blocks. Production code should retain the assurance class alongside the identity result so a downstream policy can demand DNSSEC for high-risk operations.
The tradeoff is blunt. Requiring DNSSEC reduces deployability where signed zones or validating resolvers are unavailable. Allowing the assisted path improves reach, but shifts more trust to HTTPS, endpoint control, and key publication. Hiding that choice behind one boolean creates false confidence.
Bilateral issuance is the consent proof
After the entity signature verifies, the verifier retrieves the referenced issuance event. The event must agree on the FQDN, global ID, and entity key from the DNS record. Both the entity and operational keys must countersign it. That bilateral step prevents an entity key from naming an operational key that never accepted the relationship, and prevents an operational key from claiming an entity it was never issued under.
Rotation adds another requirement. A newly advertised operational key needs a continuous, verifiable chain from the issued key. The verifier cannot jump directly from the DNS record to the latest key and call it current. Missing rotation evidence produces BLOCK_KEY_CONTINUITY, even when the new key can sign.
TLS then proves possession of an endpoint certificate for the expected FQDN. It does not replace DNSSEC, the entity signature, or bilateral issuance. The checks overlap just enough to be tempting; their failure meanings are different, which is why they remain separate states.
Status has an expiry time
Unknown is not active. That sentence belongs in the verifier contract.
A beautiful signature can describe a revoked identity. Interactive verification therefore consults the status service before a new operation. The result must be ACTIVE and fresh under a locally defined maximum age. SUSPENDED and REVOKED block. Stale, unavailable, or unknown status holds the decision rather than inheriting the last good value forever.
Cache behavior deserves explicit design. A short cache protects the status service from every tool call, but the cache key should include the global identity, relevant key version, status URL, and evidence timestamp. High-risk operations may require a fresher read than low-risk discovery. Service failure should never extend a positive result beyond its declared lifetime.
Troubleshooting: current evidence cannot answer a historical question
History needs retained evidence, not present-day reconstruction.
The draft separates current and historical verification for good reason. Current DNS and status cannot prove who controlled a name or key months ago. Historical questions need the lifecycle log and evidence anchored to the time under review. The fixture returns HISTORICAL_EVIDENCE_ONLY; it does not repurpose today's active status as past-tense proof.
A historical verifier should retain the record, signature inputs, key documents, issuance event, rotation chain, status observation, lifecycle entries, resolver assurance, and timestamps that supported the decision. Re-fetching today's endpoints changes the question. It can show the present state of an identity, but not reconstruct evidence that was never preserved.
The verifier output ends at identity
The final interface should be boring. It returns the normalized identity, assurance class, evidence hashes, key lineage, status timestamp, and verifier version. A separate policy engine receives that result together with the authenticated caller, tenant, requested action, resource, scopes, risk, budget, and approval state.
That result is intentionally incomplete. A consumer asking the verifier to decide whether an agent may perform an action is calling the wrong component. The response should preserve authorization: NOT_EVALUATED even when every identity check is green.
Keep the evidence digest and verifier contract version in the policy input. Otherwise a cached identity result can outlive the status observation or be replayed under a different assurance rule without leaving a useful audit trail.
{
"identityDecision": "IDENTITY_BOUND",
"assurance": "dnssec-validated",
"agentFqdn": "billing-agent.example.test",
"status": { "value": "ACTIVE", "ageSeconds": 20 },
"authorization": "NOT_EVALUATED"
}
Policy owns the consequence
This boundary prevents an accountable agent from becoming an omnipotent one. OAuth audience and scopes still constrain API access. Tool policy still limits invocation. Tenant filters still protect records. Approval still governs consequential actions. DNSid contributes a durable answer to “which entity and operational key stand behind this agent?”—a useful answer, but only one input to authority.
Evidence and the untested boundary
- DNSid Internet-Draft — current
-01record shape, DNSSEC assurance, entity signature, bilateral issuance, operational-key continuity, status, and current-versus-historical verification. - Archived DNSid
-01text — dated 29 June 2026 and used as the fixture's explicit draft boundary. - RFC 4033 — DNSSEC services, including DNS data-origin authentication and integrity, plus its non-goals.
- RFC 7517 — JSON Web Key representation used by JWKS endpoints referenced by the draft.
- Local deterministic verifier — 40 cases, 40 matches, two full identity bindings, one reduced-assurance binding, 36 holds or blocks, one historical-evidence-only result, and 19 decision states. SHA-256:
c316db2c0bd06cb31a669e21b2d325bf24573edf2b668f605d8af370e203c6ee.
The retained experiment stops before the network. It does not demonstrate a DNS resolver, DNSSEC chain, signed zone, JWKS endpoint, private-key custody, mTLS exchange, live issuance event, rotation service, status service, lifecycle log, OAuth server, tool-policy engine, or production agent. A staging rollout should test those dependencies independently and preserve the exact evidence used for every identity decision.