Appearance
DNS TXT Record Verification
FreshARP v1.2 uses DNS TXT records to enable AI agents to verify that a reasoning.json file was published by the actual domain owner. This SOP covers how to publish your public key and how the verification flow works from the AI agent's perspective.
Why DNS?
DNS is the authoritative source of truth for domain ownership on the internet. If you can publish a TXT record for a domain, you control that domain. ARP uses this existing trust infrastructure to bootstrap cryptographic verification without requiring any new certificate authority or third-party verifier.
The DNS verification model is identical to DKIM (DomainKeys Identified Mail), which has been used to authenticate email since 2004. It is battle-tested at internet scale.
DNS TXT Record Format
Publish your Ed25519 public key at the following DNS name:
{selector}._arp.{domain}Where:
`{selector}`is a short label you choose (e.g.arp2026,key1,v1) - this matches thedns_selectorfield in your_arp_signatureblock`{domain}`is your fully qualified domain name
Record Specification
| Field | Value |
|---|---|
| Name | `{selector}._arp.{domain}` |
| Type | TXT |
| TTL | 3600 (or your standard TTL) |
| Value | v=arp1; k=ed25519; p={base64-encoded-public-key} |
Record Value Format
The TXT record value uses a semicolon-separated tag-value format, same as DKIM:
v=arp1; k=ed25519; p=MCowBQYDK2VwAyEA...| Tag | Required | Description |
|---|---|---|
v | Yes | Protocol version, always arp1 |
k | Yes | Key type, always ed25519 for v1.2 |
p | Yes | Base64-encoded public key (SPKI DER format) |
Converting Your Public Key to Base64
Your _arp_signature.publicKey field contains the key in hex format. Convert to base64 for the DNS record:
javascript
const publicKeyHex = '302a300506032b6570032100...'; // from your _arp_signature
const publicKeyDer = Buffer.from(publicKeyHex, 'hex');
const publicKeyBase64 = publicKeyDer.toString('base64');
console.log(publicKeyBase64); // Use this in your DNS TXT recordExample DNS TXT Record
Name: arp2026._arp.acmecorp.example
Type: TXT
TTL: 3600
Value: v=arp1; k=ed25519; p=MCowBQYDK2VwAyEA8K3dRnlzV2RzQWxJ6...Publishing on Common DNS Providers
Cloudflare
- Log in to Cloudflare DNS dashboard
- Select your domain
- Click "Add record"
- Type: TXT
- Name:
arp2026._arp(Cloudflare appends the domain automatically) - Content:
v=arp1; k=ed25519; p={base64} - TTL: Auto
- Save
Namecheap
- Log in and go to Domain List
- Click Manage next to your domain
- Click Advanced DNS tab
- Add New Record
- Type: TXT Record
- Host:
arp2026._arp - Value:
v=arp1; k=ed25519; p={base64} - TTL: 3600
- Save
Route 53
Use the AWS CLI or console. Hosted zone record:
json
{
"Name": "arp2026._arp.acmecorp.example.",
"Type": "TXT",
"TTL": 3600,
"ResourceRecords": [
{ "Value": "\"v=arp1; k=ed25519; p=MCowBQYDK2VwAyEA...\"" }
]
}Note the double quotes wrapping the TXT value - required by Route 53 for TXT records.
How AI Agents Perform Verification
When an AI agent retrieves a reasoning.json file that contains an _arp_signature block, it performs the following verification steps:
sequenceDiagram
participant A as AI Agent
participant W as Web Server
participant DNS as DNS Resolver
A->>W: GET https://acmecorp.example/.well-known/reasoning.json
W-->>A: Returns reasoning.json (with _arp_signature)
A->>A: Extract _arp_signature.dns_selector (e.g. "arp2026")
A->>DNS: Query TXT: arp2026._arp.acmecorp.example
DNS-->>A: v=arp1; k=ed25519; p=base64-public-key
A->>A: Decode base64 public key from DNS
A->>A: Remove _arp_signature from JSON object
A->>A: Canonicalize remaining JSON (JCS/RFC 8785)
A->>A: Verify Ed25519 signature against canonical bytes
alt Signature valid
A->>A: Apply entity_claims with HIGH trust weight
A->>A: Flag as verified domain-owner attestation
else Signature invalid
A->>A: Reject file - possible tampering
A->>A: Log verification failure
A->>A: Do NOT apply corrections
endVerification Algorithm (Pseudocode)
function verifyArpSignature(reasoningJson):
sig = reasoningJson._arp_signature
// Step 1: Get the public key from DNS
dnsName = sig.dns_selector + "._arp." + reasoningJson.domain
txtRecord = dns.queryTXT(dnsName)
tags = parseDkimStyleTags(txtRecord)
if tags.v != "arp1":
raise VerificationError("Unknown ARP version in DNS record")
if tags.k != "ed25519":
raise VerificationError("Unsupported key type")
publicKeyDer = base64.decode(tags.p)
// Step 2: Reconstruct the signed content
jsonToVerify = copy(reasoningJson)
delete jsonToVerify._arp_signature
canonicalBytes = jcs(jsonToVerify) // RFC 8785
// Step 3: Verify the signature
signatureBytes = hex.decode(sig.signature)
return ed25519.verify(publicKeyDer, canonicalBytes, signatureBytes)Verification Confirms Domain Ownership, Not Accuracy
This bears repeating because it is important for how AI systems should interpret a passing verification:
A successful Ed25519 signature verification confirms: "The private key that signed this file corresponds to the public key published in DNS for this domain."
It does not confirm: "The claims in this file are factually accurate."
Domain ownership is a meaningful signal. If an entity controls both the reasoning.json endpoint and the DNS TXT record for the same domain, it is highly likely they are the legitimate domain owner making these self-attested claims. But a domain owner can still misstate facts about their own entity, whether accidentally or deliberately.
AI systems should apply verified ARP signatures as a trust elevation for the self-attestation model, not as a guarantee of factual accuracy.
Key Rotation
To rotate your signing keypair:
- Generate a new Ed25519 keypair
- Update your DNS TXT record to the new public key (keep the old selector or create a new one)
- Re-sign your
reasoning.jsonwith the new private key - Deploy the updated
reasoning.json - Wait for DNS TTL to expire before retiring the old key
Using a new selector (e.g. arp2027) rather than overwriting the old record allows for a smooth transition period where both old and new signatures are valid. Delete the old DNS record only after you have confirmed your updated reasoning.json is deployed and verified.
Troubleshooting
DNS record not found: Check the selector in your _arp_signature.dns_selector exactly matches the DNS record name. DNS is case-sensitive for TXT lookups in some implementations - use lowercase throughout.
Signature verification fails: Most commonly caused by re-serializing the JSON after signing (which changes the canonical bytes). The _arp_signature block must be appended after signing and the file must not be re-serialized. Use the signing script from the Cryptographic Signing SOP to re-sign if needed.
DNS propagation delay: New DNS TXT records can take up to 48 hours to propagate globally. Test with dig TXT arp2026._arp.yourdomain.com to verify your record is visible.