Skip to content

Transparency log

An agent’s identity history consists of signed lifecycle events: ISSUANCE, KEY_ROTATION, REVOCATION, RETIREMENT, MIGRATION, and DELEGATION. The identity record’s lr= field identifies the log and stream that carry those events; a C2SP reference has the form c2sp-tlog:<scope>:<log-prefix>#<stream-id>. The SDKs currently ship the c2sp-tlog binding, which verifies C2SP tiled transparency logs using a trusted log policy, witnessed checkpoints, Merkle proofs, and signed lifecycle events.

For draft-01 identities, VerifyDomain checks that the logged ISSUANCE binds the record’s entity key and operational key with both parties’ signatures. It also checks the chain of logged key rotations to the current operational key. Verification needs a trusted reader for the record’s lr= method and a fresh ACTIVE response from su=. Missing log trust or invalid evidence fails verification.

An operation-time non-revocation check additionally requires complete history through a fresh checkpoint. The fl=logchk record flag asks relying parties to perform this check for high-value or irreversible operations. Your application decides which operations need it, including any operations covered by local policy when the flag is absent. See Enforce logchk.

SettingUse it forHow to configure it
DNSid-managedIdentities on DNSid’s managed development and production logsExplicitly select the SDK’s reviewed trust catalog in code or set logTrust.managed in a deployment file.
Trusted profileA deployment with an independently supplied C2SP trust profileSupply the profile in code, a deployment file, or DNSID_LOG_TRUST_PROFILE_FILE. The profile binds one exact log scope and prefix to its policy and stream-bundle signer keys.
Trusted policyDNSid Local or a deployment that distributes a C2SP tlog-policy document or its HTTPS URLSupply trusted policy bytes, DNSID_LOG_POLICY_FILE, or DNSID_LOG_POLICY_URL. The SDK uses its bounded complete-log scanner unless separately configured with trusted bundle signer keys.

Trust is selected by your application or deployment configuration. Obtain profile and policy locations through a channel you trust; the identity record’s lr= and files advertised by a log server do not supply trust roots. Loaded logTrust accepts exactly one of managed, profile, policyDocument, or policyUrl. An explicitly injected logRegistry takes precedence over loaded trust.

The managed factory accepts exact canonical public references to https://log.dev.dnsid.ai and https://log.dnsid.ai using trust material embedded in the SDK release. Select this trust setting explicitly; other scopes and log prefixes fail closed.

import (
dnsid "github.com/dnsid-ai/dnsid-go"
"github.com/dnsid-ai/dnsid-go/log/c2sptlog"
)
registry, err := c2sptlog.NewDnsidManagedVerificationRegistry(ctx, c2sptlog.DnsidManagedVerificationConfig{})
if err != nil { return err }
verifier, err := dnsid.NewVerifier(dnsid.WithLogRegistry(registry))
if err != nil { return err }
vd, err := verifier.VerifyDomain(ctx, "agent.example") // use a DNSid-enabled domain on a supported log
if err != nil { return err }

These are verification-only managers; they require no local identity or signing key. Replace the example domain with an identity whose signed lr= points to one of the supported logs.

Follow the local quickstart to publish the agent’s record and issuance event. dnsid local env exports DNSID_LOG_POLICY_URL along with the local DNS server, CA bundle, and private-host settings. Run the SDK under dnsid local run or load that environment into your shell. The environment constructors then configure the log reader and verification transport together:

import (
dnsid "github.com/dnsid-ai/dnsid-go"
"github.com/dnsid-ai/dnsid-go/config"
)
verifier, err := config.IdentityManagerFromEnvironment(ctx, nil, dnsid.Config{}, config.Dependencies{})
if err != nil { return err }
vd, err := verifier.VerifyDomain(ctx, "alice.test") // use the domain assigned by DNSid Local
if err != nil { return err }

For deployments distributing trust separately, the same environment constructors accept one of DNSID_LOG_TRUST_PROFILE_FILE, DNSID_LOG_POLICY_FILE, or DNSID_LOG_POLICY_URL. A deployment file can instead contain {"logTrust":{"managed":true}}, {"logTrust":{"profile":{...}}}, or {"logTrust":{"policyUrl":"https://policy.example/dnsid-policy"}}. Supply trusted profile bytes or policy bytes directly through the generic C2SP verification factories when you need code-level options. Pass the same DNS/CA/private-host transport settings to the generic factory and the identity manager for private or test environments. Managed trust has no environment-variable selector.

Freshness, completeness, and rollback protection

Section titled “Freshness, completeness, and rollback protection”

The C2SP reader checks the log signature and witness quorum on a checkpoint, proves event inclusion under its Merkle root, checks append-only consistency with previously trusted checkpoints, and verifies lifecycle signatures and stream order. An individual inclusion proof establishes that an event was logged. A current non-revocation check requires a complete stream view through a fresh accepted checkpoint.

Managed trust prefers signed per-domain stream bundles. A trusted profile with bundle signer keys enables the same bundle-first path. The SDK can use a bounded full-log scan for specified bundle availability failures or missing consistency evidence. Invalid, expired, or conflicting bundle evidence fails verification. The generic factories also provide requireStreamBundle-style options when bundle availability must be enforced. Raw scans can be expensive on large logs; reuse the registry and checkpoint store, and set limits appropriate to your deployment.

The managed factory and environment/deployment-file trust constructors use a 10-minute checkpoint freshness policy for operation-time non-revocation. When constructing a generic registry directly, set its checkpoint maximum age (CheckpointMaxAge in Go, checkpointMaxAge in TypeScript, checkpoint_freshness_ms in Python). Without one, non-revocation checks fail closed. Historical binding and key-continuity checks can use older authenticated checkpoints.

Perform this check immediately before an operation your application classifies as high value or irreversible. In the examples, highValueOperation / high_value_operation and localPolicyRequiresLogCheck / local_policy_requires_log_check are application policy decisions. Local policy can require the check even when fl=logchk is absent. The check retrieves fresh, complete log evidence and fails if the stream is incomplete, stale, or revoked. Reverify the domain when your operation also needs a fresh su= status response; log evidence does not refresh protocol status.

vd, err := verifier.VerifyDomain(ctx, domain)
if err != nil { return err }
if highValueOperation && (vd.RequiresLogCheck() || localPolicyRequiresLogCheck) {
evidence, err := vd.VerifyLogEvidence(ctx, time.Time{}) // current time
if err != nil { return err }
_ = evidence // checkpoint, completeness, and freshness boundary
}

The returned evidence records the verified history, completeness, checkpoint, and freshness boundary. An unavailable log, untrusted policy, failed witness quorum, incomplete history, or stale checkpoint raises a verification error; see Verification results.

To inspect an identity’s history, load its proof-checked events and compute a snapshot at a chosen time. Snapshots represent historical logged state; use a fresh su= response for current protocol status.

domainLog, err := verifier.LoadDomainLog(ctx, vd)
if err != nil { return err }
snapshot, err := domainLog.SnapshotAt(time.Now())
if err != nil { return err }
fmt.Println(snapshot.HistoricalState, snapshot.ActiveKeyThumbprint)

All three SDKs also support offline verification of signed dnsid-c2sp-stream-bundle@v1 bundles with independently supplied policy bytes and bundle verifier keys. See the language references for VerifyStreamBundle, verifyC2spStreamBundle, and verify_c2sp_stream_bundle.

Go lifecycle log and Go C2SP tlog · TypeScript C2SP tlog · Python transparency log.