Skip to content

Verify a domain

The core verification surface of the SDKs. Verification needs no identity or key of its own. It does require trust for the peer’s lifecycle log: first configure a log registry for its lr= method. The examples below use that registry. A manager constructed with an identity, as shown in the SDK overview, verifies the same way.

// Verify-only: no identity or key provider required.
idm, err := dnsid.NewVerifier(dnsid.WithLogRegistry(registry))
if err != nil {
log.Fatal(err)
}

Verify a peer’s domain directly. This resolves the _dnsid record, validates the JWKS at ku=, checks the record signature, and queries the status endpoint.

vd, err := idm.VerifyDomain(ctx, "agent.example")
if err != nil {
log.Fatalf("invalid: %v", err)
}
fmt.Println(vd.Domain(), vd.Record().GovernanceID, vd.Status().State)

Verification proves a domain is who it says it is. It does not decide whether you want to talk to it. The verification section of the config takes an optional allowlist of accountable entities. Each entry names a governance identifier (the gi= value in the peer’s record) and, optionally, RFC 7638 thumbprints the peer’s current record-signing key must match.

verifier, err := dnsid.NewIdentityManager(dnsid.Config{
Verification: dnsid.VerificationConfig{
TrustedEntities: []dnsid.TrustedEntity{
{GovernanceID: "acme.example"},
},
},
}, nil, dnsid.WithLogRegistry(registry))

Matching is exact on the normalized governance ID, with no wildcard or suffix matching. Leaving the list unset makes no acceptance decision; an empty list denies every counterparty. Acceptance runs on every call, including cache hits, and a denial surfaces as a permanent verification error with the counterparty-not-accepted code. Protocol evidence is cached before the acceptance check, and denials are never cached.

Per-language details: Go IdentityManager · TypeScript · Python IdentityManager.