Internet-Draft Agent Discovery Corroboration October 2026
Chandra Expires 3 May 2027 [Page]
Workgroup:
Network Working Group
Internet-Draft:
draft-chandra-agent-registry-corroboration-02
Published:
Intended Status:
Standards Track
Expires:
Author:
S. Chandra
Stellarminds.ai

Multi-Source Corroboration for AI Agent Discovery

Abstract

AI agent discovery depends on sources such as registries, name services, DID methods, and catalogs. A valid signature on an artifact does not by itself show that a source has not withheld a record, or served inconsistent records to different observers. This document specifies a source- and format-agnostic procedure for corroborating independent observations of an agent. It defines claim classification, canonical views, deterministic disagreement findings, multi-vantage equivocation detection, propagation-delay confirmation, and signed Corroboration Records. The procedure produces verifiable evidence for every sweep, including agreement and insufficient-observation results. It composes with registry-assisted resolution of known subjects: a registry's renewable subject-to-target binding carries a sequence, and the procedure attributes a source serving a superseded binding rather than comparing it, so propagation lag is never reported as disagreement.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 3 May 2027.

▲

Table of Contents

1. Introduction

An artifact served by a discovery source can be self-certifying: it can be signed such that a consumer verifies its content offline against an expected identity or key. Self-certification detects tampering with that artifact. It cannot, in principle, prevent two other behaviors available to any source: omission, in which a source withholds an artifact and there is therefore no artifact signature to verify; and equivocation, in which a source serves two inconsistent artifacts to different observers, each individually valid. Both require a structurally different defense: comparison of independent observations, with its own failure modes and evidence.

This document specifies that comparison. It normatively fixes the decision procedure (claim classification in Section 4, the view contract in Section 5, the diff in Section 6, the confirmation discipline in Section 7, the self-description mechanism in Section 8, the Answer Scope in Section 9, and the Corroboration Record in Section 10), and treats the byte-level fetch per source as an injectable implementation detail, because interoperability lives in the comparison and the record, not in any source's wire format.

Not every difference between two sources is misbehavior. A source that is subject to a data-residency obligation may serve a different endpoint by design, and a procedure that reports every such difference as equivocation fires on normal operation. Section 9 admits this without conceding anything: a source may declare, in advance, an axis a third party can independently occupy along which it varies a field. A declaration so made annotates a finding and never withdraws it, the disagreement still stands and the verdict is unchanged, while a source that varies contrary to its own declaration earns a stronger finding than one that never declared at all.

The procedure serves any layer at which a middleman answers questions about an agent: discovery (which endpoint), identity (which key), capability (which functions), and evidence (which records exist). Section 11 gives the two reference layer instantiations.

Discovery is increasingly specified as resolution: a discovering entity that already knows a Subject Identity obtains, from one discovery mechanism it selected, the minimum information needed to continue, together with integrity, authority, freshness and revocation information [RAR] [DAWN-CHARTER]. Resolution is one source's answer. This document is what a verifier does when it holds two of them. The two are complementary and Section 12 states the relationship: resolution defines the shape and lifecycle of a single binding, corroboration defines the comparison across sources and the record of it, and neither evaluates trust.

2. Conventions and Definitions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

Source: Anything that answers "what do you have for agent X?": a registry, a name service, a DID method, a catalog, an evidence store. In DAWN terms [DAWN-TERMINOLOGY] a source is a discovery mechanism, or a registrar's resolution surface, that returns discovery information about a named entity; the agent is the discovered entity and the corroborator is a discovering entity acting as an auditor.

Source label: The identifier under which a source appears in claims, findings and records. A label MUST be a URI [RFC3986], MUST be stable for the lifetime of the source, and MUST be unique within the deployment that produced the record.

Labels are compared by exact octet equality. An implementation MUST NOT normalize a label and MUST NOT infer that two labels denote the same source. The endpoint canonicalization of Section 5 applies to view values, never to labels: normalizing a label would let one source's identity merge with another's, which is the failure a corroborator exists to detect.

This document defines no global source-identity system. A label is meaningful within the deployment that recorded it, and two deployments intending to compare records MUST agree on labels out of band. Where a source is served over HTTP the URI it is served from is the natural label, and the one an operator can demonstrate control of (Section 9.2); where it is not, any stable URI serves. No claim of global uniqueness is made or implied.

Vantage: The observation context under which a source is observed: the conditions the claim was obtained under, not the observing instance. Two sweeps of one source from two hosts in the same context bear the same vantage. One source MAY be observed from multiple vantages; vantage is part of a claim's identity. Identifiers are constrained by Section 3.1.

Answer Scope: A source-signed bundle declaring, in advance, which fields that source varies and along which vantage class (Section 9).

Watch set: The set of canonical agent identifiers a consumer corroborates.

Claim: One source's answer about one agent as observed from one vantage during one sweep, classified present, absent, or error (Section 4). A claim's observation time is the sweep's observed_at (Section 7).

View: The comparable reduction of a present claim: named string fields (Section 5).

Resolver: The per-source adapter mapping a canonical agent identifier to a classified claim (Section 4), applying that source's native verification.

Sweep: One corroboration cycle: every watch-set identifier resolved against every (source, vantage), diffed, and recorded.

Finding: One detected disagreement: {kind, agent_id, confirmation, detail} (Section 6).

Corroboration Record: The signed, content-addressed result of one sweep for one subject (Section 10).

Self-Description: An agent-signed bundle of sequence number, aliases, and service endpoints (Section 8).

Binding: A source's subject-to-target association issued as a renewable lease [RAR]: a sequence that increases, within one authority scope, whenever the target, publisher, ownership state or authority evidence changes, together with validity bounds and revocation information. The authority scope is the issuer that validated the subject for that binding. Sequences are comparable only within one scope. A binding's sequence is a different clock from the self-description seq of Section 8: the first is set by the issuer that bound the subject to a target, the second by the subject.

CORR-DIGEST: The lowercase hexadecimal encoding of SHA-256 applied to the UTF-8 octets of the RFC 8785 [RFC8785] JSON Canonicalization Scheme serialization of a value. All digests in this document use this construction. Values in digest-bearing fields MUST NOT be JSON floating-point numbers.

3. Claim Model

The unit of observation is the tuple (source, vantage, agent) within one sweep, and the sweep carries the observation time (Section 7). Vantage MUST be preserved as a distinct axis and MUST NOT be encoded by treating each vantage as an independent source: intra-source disagreement across vantages attributes a different misbehavior (equivocation by that source) than inter-source disagreement, and collapsing the axes destroys the attribution (Section 6.3).

A sweep with fewer than two present-or-absent claims for a subject has nothing to corroborate; its verdict is INSUFFICIENT (Section 10), and an implementation MUST NOT report agreement in that case.

3.1. Vantage Identifiers

A vantage identifier MUST be either null or a string of the form class ":" value, where class names the context dimension and value names a point on that dimension. Each component MUST match the regular expression [a-z0-9](?:[a-z0-9-]*[a-z0-9])?. Identifiers are compared by exact octet equality; an implementation MUST NOT parse, order, normalize, or infer containment relationships among identifier values.

This document defines two initial occupiable classes. Both take their value space from an external registry, which is what makes shared denotation (below) a fact rather than an agreement between sweepers:

  • region: the value MUST be an ISO 3166-1 alpha-2 code [ISO3166], lowercased. region:de, region:us, and the exceptionally reserved region:eu are well-formed.

  • asn: the value MUST be the string as followed by the decimal representation of a 32-bit Autonomous System Number [RFC6793], with no leading zeros: asn:as64500.

A class outside this set MUST NOT be used unless the deployments comparing observations have agreed on both its meaning and its value space; absent that agreement the requirements below cannot be met and the vantage MUST be null.

Comparison scoped to a vantage (Section 6.2) is sound only if independently operated sweepers assign the same identifier to the same context. Accordingly:

  • Shared denotation. Two claims bearing the same identifier MUST denote the same observation context, whichever source or sweeper produced them. A sweeper MUST NOT mint a private meaning for a class defined elsewhere.

  • Occupiability. A class MUST be one a third party can construct and observe from independently. region and asn are occupiable: a verifier can place itself in that region or on that network and repeat the observation. Attributes of the caller relationship rather than of the observation, caller identity, tenant, subscription tier, authorization level, are NOT occupiable and MUST NOT be encoded as a vantage. This rule is central: a variation on a non-occupiable attribute cannot be independently checked, so a corroborator that credited a declaration of one would be reporting the source's own assertion back as evidence.

  • Non-overlap. Within one sweep the vantages a sweeper uses MUST NOT overlap: no context may be denoted by two identifiers, and no identifier by two contexts. A sweeper that cannot guarantee this MUST use null.

  • Cardinality. A class MUST have a small, enumerable value set. A high-cardinality dimension partitions observations until nothing is compared against anything, degrading corroboration to silence while appearing to function.

The null vantage asserts no context. It is not a comparable identifier and MUST NOT be compared for equality against a non-null one as though it named one. It is correct for a single-vantage sweeper and REQUIRED where the obligations above cannot be met, declining to name a context is always sound, whereas naming one wrongly is not.

4. Classifying One Claim

For a source S, vantage V, and agent A, a resolver MUST classify the outcome as exactly one of:

A resolver MUST return an error claim rather than propagate an unclassified failure to the diff procedure. An error claim MUST be excluded from the diff entirely. This rule is central: a source that failed to answer has asserted nothing and MUST NOT be treated as claiming absence, otherwise every transient fault becomes a false omission finding.

4.1. Outcome

Every claim carries an outcome alongside its status. status states whether the source made a claim; outcome states how that came about.

It matters most for error, which the diff excludes entirely. A timeout, a persistently unreachable host, an explicit refusal, and a body that could not be read are different facts about a source, and a record that collapses them loses the difference: a pattern of refusals from one source is not the same signal as a pattern of timeouts.

Values seeded by this document, bare:

Table 1
Value Meaning
timeout no answer within the deadline
unreachable transport failed
refused the source declined to answer
unparseable answered; the body could not be read
unverifiable answered; the source's own verification failed
expired answered; the binding served is past its validity bound and MUST be treated as stale
revoked answered; the source itself marks the binding revoked

expired and revoked exist because a resolution profile requires that a binding past expiresAt, or revoked, MUST NOT be treated as current [RAR]; a resolver that honours that rule has an answer it may not use, which is an error claim, never an absent one, and the outcome records which.

A present or absent claim MAY use its status as its outcome, which is the least specific honest answer. Bare names are reserved for values seeded in this document and its successors; any party introducing another MUST namespace it with a reverse-DNS or URI prefix, as for finding kinds (Section 13). A consumer MUST treat an unknown outcome as informational and MUST NOT reject a record for carrying one.

An outcome MUST NOT affect which claims enter the diff. status alone decides that. Outcome is provenance, and were it to filter, a source could remove itself from comparison by describing itself.

Only the resolver knows why a resolve ended as it did, so supplying an outcome is the resolver's responsibility. A resolver that does not keeps its status as its outcome.

5. The View Contract

A present claim MUST be reduced to a view: a mapping of field names to string values or null. A null value MUST NOT participate in the diff: an unverifiable or missing field is not a disagreement.

Fields whose values are compared as strings MUST be canonicalized by the layer before entering the view, such that two references to the same resource compare equal and two references to different resources do not.

RFC 3986 [RFC3986] Section 6 names syntax-based normalization but leaves several choices to the implementer, and two conforming implementations that choose differently produce a false field divergence between honest sources. URI-valued fields MUST therefore be canonicalized as follows, which this document identifies as endpoint-normalization/0.1:

A value that is not a URI is compared unchanged. Guessing at a syntax the layer does not recognize is worse than comparing it literally.

A view MAY additionally carry a lease position: the binding's sequence and authority scope (Section 2). The position is not a field; it MUST NOT be compared as a value and MUST NOT appear in values of any finding. It is read by the supersession rule of Section 6.4. The authority issuer likewise MUST NOT be a compared field: two sources validating the same subject through different issuers is the ordinary multi-registry case, and a disagreement between issuers is not a disagreement about the subject.

Where a field carries its own proof (a signed record's DID, a key), the resolver MUST populate it only from a successfully verified artifact whose subject binds to the outer agent identifier; on any verification failure the field is null. A verifier MUST NOT allow an unverified value to produce a divergence finding against a verified one (the no-laundering rule).

6. The Diff

Input: the claims of one sweep for one agent. The diff MUST be pure (deterministic ordering, no I/O, no exceptions) and MUST derive findings as follows. One agent MAY yield multiple simultaneous findings.

6.1. omission

Emit an omission finding iff the agent is present on at least one source and absent on at least one other source. Both sides MUST be positive claims; error claims contribute to neither. Detail: {present_on, missing_from} as sorted lists of source labels.

6.2. Field Divergence

For each field name appearing in any current present view (Section 6.4), collect the non-null values keyed by (source, vantage). Comparison is scoped to a cohort: the claims sharing one vantage identifier.

  1. A source contributes a single value to the comparison for a field only when all non-null values that source produced for that field agree across its observed vantages. Such a source participates in every cohort.

  2. If a source produced differing non-null values for a field across vantages and an applicable Answer Scope exists (Section 9.3), that source contributes its value separately to each cohort identified by the corresponding vantage.

  3. If a source produced differing non-null values for a field across vantages and no applicable Answer Scope exists, that source contributes no value to the cross-source comparison for that field (Section 6.3).

  4. Cohort keys are derived only from the vantages of sources for which an applicable Answer Scope authorizes per-vantage comparison for that field. If no such source exists, the diff MUST evaluate one unscoped cohort containing every eligible claim.

Emit a finding of kind equal to the field name, once per cohort, iff more than one distinct value appears across sources within that cohort. Detail: {field, values} where values maps source labels to values.

6.3. source_equivocation

For each source observed from more than one vantage, if that source's vantages yield differing non-null values for any field, emit a source_equivocation finding attributing that source. Detail: {source, field, values} where values maps vantage labels to values. The finding is emitted whether or not the variation was declared (Section 9); its per-vantage values are carried in the record for audit.

Absent an applicable Answer Scope, a source under source_equivocation for a field contributes no single agreed value to the Section 6.2 comparison for that field: a source that disagrees with itself cannot corroborate. Where an Answer Scope applies (Section 9.3), that withholding is replaced, not waived: the source's per-vantage values enter the cohort comparison of Section 6.2 instead. This rule is central: withholding the source without replacing it would let a source that declares variation serve one vantage a forged value and be recorded AGREE, because nothing would remain to compare that value against, strictly worse than not honouring the declaration at all.

6.4. Supersession

Before Sections 6.2 and 6.3 are evaluated, the present claims are partitioned by authority scope. Claims whose view carries no lease position are current in their own right. Within each scope the highest sequence observed is current; a claim whose sequence is lower is superseded.

For each (source, scope) with a superseded claim, emit a stale_binding finding attributing that source. Detail: {source, scope, sequence, current_sequence, current_on}, where current_on lists the sources at the current sequence. A superseded claim MUST be withheld from Sections 6.2 and 6.3: a source serving yesterday's binding is behind, not disagreeing, and comparing its target against the current one would report propagation as tampering. A source that is current from one vantage and superseded from another is mid-propagation and MUST NOT be charged source_equivocation for that reason alone. A superseded claim remains present for Section 6.1 and remains decisive for the verdict (Section 10): the subject exists on that source.

Equal sequences with differing values are the disagreement supersession exists to isolate, and MUST still yield a field finding.

stale_binding is a finding: the verdict is DIVERGENT. A lagging source within the staleness window is suspected (Section 7); one still behind after it is confirmed, which is the replay and stale-cache condition the resolution profile's security considerations name [RAR]. What a confirmed stale binding is worth is the consumer's judgement, as for every finding. The identity of the finding (Section 7.1) is (source, scope), never the sequence numbers: a source that stays one step behind a moving current is one persistent condition.

7. Observation Time and Confirmation

Sources propagate updates asynchronously; a legitimate change is served inconsistently for a bounded window. Every sweep MUST carry an observed_at timestamp, which is the observation time of every claim in that sweep; claims do not carry individual timestamps (Section 10). Every finding MUST carry a confirmation state:

7.1. Finding Identity

Confirmation asks whether a finding observed now is the same disagreement as one observed earlier. That comparison MUST use a stable identity, defined per finding kind:

Table 2
Kind Identity
omission present_on, missing_from
source_equivocation source, field
scope_violation source, field, declared_class
stale_binding source, scope
a view field name field, the participating source labels, and the cohort
any other kind the whole detail

Observed values MUST NOT participate in the identity. Values change while the same disagreement persists, and keying on them makes a source that rotates its answer produce a stream of unrelated findings, each restarting as suspected and never reaching confirmed. That is the staleness abuse of Section 15, made worse: the pattern does not accumulate under one identity either, so even the count of short-lived divergences from one source is diluted.

The cohort MUST participate wherever the comparison was scoped (Section 6.2). The same sources can disagree differently in two contexts, and without the cohort one of those disagreements masks the other.

A kind this document does not seed MUST key on the whole detail. Its shape is not defined here, so no part of it may be assumed stable, including a values member, which such a kind may use to mean something else entirely.

confirmation and the declaration state of Section 9.4 do not participate either.

7.2. The Staleness Window

A deployment MUST configure exactly one staleness window, expressed in seconds, applied to every finding in every sweep it produces, and MUST record it in each Corroboration Record (Section 10). It is derived from the propagation characteristics of the sources swept (TTLs, sync intervals) and SHOULD be set from the slowest of them, since a window shorter than a source's real propagation time manufactures confirmed findings from ordinary delay. Per-source or per-pair windows are deliberately not provided: the record carries a single staleness_window_s, so a verifier re-deriving confirmation from recorded bytes has exactly one window to apply. A verifier evaluates confirmation deterministically from the recorded timestamps and the window recorded in the Corroboration Record; no external clock is consulted. Consumers SHOULD treat suspected findings as monitoring signals and confirmed findings as evidence.

8. The Self-Description

Corroboration requires knowing each source's name for the same agent (identity correspondence). This document defines an OPTIONAL agent-signed bundle that makes the correspondence verifiable rather than configured:

{
  "version": "self-description/0.1",
  "subject": "did:key:z6Mk...",
  "seq": 7,
  "aliases": { "<source-label-or-namespace>": "<name>", ... },
  "endpoints": [ "<uri>", ... ],
  "signature": "<base64 Ed25519 over JCS of all other members>"
}

Rules:

  1. The signature MUST verify against the key encoded in subject (offline; did:key per its method specification [DIDKEY]). On failure the description contributes nothing and the consumer falls back to caller-supplied correspondence. A fallback MUST NOT be silently overridden by an unverifiable description.

  2. Supersession. seq is a monotonically increasing integer. Among validly signed descriptions for the same subject, the description with the highest seq value is authoritative. A source serving a description whose seq value is lower than that of a description the consumer has already verified for the same subject is serving superseded material; this SHOULD be reported as a finding of kind stale_description attributing that source.

  3. Agent equivocation. Two validly signed descriptions for one subject with equal seq and differing JCS bytes MUST be reported as a finding of kind agent_equivocation. This indicates key compromise or a misbehaving agent and is not attributable to any source.

  4. Distribution. The description SHOULD be obtainable from each participating source and MUST, when so distributed, be corroborated as its own layer under this procedure (view: {seq, aliases_digest, endpoints_digest}), so that omission or replay of the description by a source is an ordinary finding.

  5. Verify-back. A record fetched via an alias asserted in a description MUST bind to the same subject key to contribute a claim; a record that does not bind back contributes no claim and MUST NOT produce a divergence finding (Section 5's no-laundering rule extended to correspondence).

The bundle is deliberately the minimal offline-verifiable profile of DID Core's [DIDCORE] alsoKnownAs and service constructs; implementations integrating richer self-descriptions (e.g., AgentFacts) MAY derive this bundle from them.

9. The Answer Scope

A source may vary its answers for reasons that are not misbehaviour: data-residency obligations, regional infrastructure, jurisdictional routing. A procedure that reports every such variation as equivocation fires on normal operation, and a detector that fires on normal operation is switched off. This document therefore defines an OPTIONAL source-signed bundle by which an operator declares its variation in advance, so that an observed variation can be recorded as one the operator had already described.

An Answer Scope declares an axis, never a mapping. "endpoint varies by region" is checkable: a verifier occupies another region and compares. "EU callers receive X" would be a promise about values, made by the party doing the varying, and a promise adds a label to a finding without adding any means to test it. Declaration without commitment adds vocabulary, not detection.

9.1. The Bundle

{
  "version": "answer-scope/0.1",
  "subject": "did:key:z6Mk...",
  "label": "<the source label this scope covers>",
  "seq": <integer>,
  "scopes": [
    { "field": "<view field>", "vantage_class": "<class>" }
  ],
  "signature": "<base64 Ed25519 over JCS of all other members>"
}

seq is a monotonically increasing integer that the operator increments whenever it reissues the bundle; freshness is supersession by seq, so no external clock is required to order two bundles. subject identifies the operator's verification key, distinct from the agent key of Section 8: an agent self-description attests an agent about itself, while an Answer Scope attests an operator about its own registry.

9.2. Verification

A verifier MUST reject a bundle unless all of the following hold.

  1. The signature verifies against the key subject encodes.

  2. label equals the label the bundle was retrieved from, by exact octet equality, with no normalization of scheme, case, port, or trailing path. This rule is central: a valid signature establishes that the holder of the subject key authored the bundle; it does not, by itself, establish control of any particular source label. Serving the bundle at the label is what supplies that evidence. Inferring equivalence between spellings would let one naming convention acquire authority over another.

  3. Every vantage_class is occupiable (Section 3.1). A verifier MUST maintain an explicit set of classes it accepts and MUST reject any other; widening that set is a deliberate act.

  4. Every field and vantage_class matches the token grammar of Section 3.1. Wildcards and catch-alls MUST be rejected at the token level, irrespective of the accepted class set, the permissive form of a declared-scope mechanism is what becomes its attack surface.

  5. No field appears twice. Overlapping scopes would let an operator select, after the fact, whichever applies.

  6. scopes carries no more entries than the verifier's configured maximum.

An empty scopes list is well-formed and meaningful: the operator positively asserts that it does not vary. This is distinct from publishing nothing, which is ambiguous between "does not vary" and "does not implement this". A source that does not vary SHOULD publish an empty scope rather than remain silent.

9.3. Application

An Answer Scope applies to a (source, field) for one sweep iff a verified scope from that source names that field, and every vantage at which that source produced a non-null value for that field lies on the declared vantage_class. A declaration on one axis says nothing about variation observed on another.

Where it applies, exactly two things change, and an implementation MUST change nothing else:

  1. The source_equivocation finding of Section 6.3 is annotated declared (Section 10), carrying the seq that was matched.

  2. The withholding of Section 6.3 is replaced by cohort participation in Section 6.2.

9.4. Annotation, Not Absolution

A declaration annotates a finding; it MUST NOT withdraw one. The finding is emitted, and the verdict of Section 10 is computed exactly as though no declaration existed. A corroborator reports what it observed and what the source had said it would do; whether an annotated divergence is acceptable is the consumer's judgement, and fusing that judgement into the procedure would make it wrong for every deployment whose policy differs.

An implementation MUST record, in every annotated finding, which seq of the declaration was matched. Without it, a source retroactively legitimises past divergences by publishing a broader scope today.

9.5. scope_violation

A source that varies a field contrary to an Answer Scope it previously published, most plainly one whose scope asserted no variation, is not merely exhibiting an undeclared divergence. It has published a constraint and violated it. A corroborator MUST emit a scope_violation finding attributing that source. Detail: {source, field, declared_class, observed, declaration_version}.

The temporal rule is not optional. A corroborator MUST NOT emit scope_violation unless it can establish that the scope it evaluated was published before the observation it contradicts. Section 9.4 prevents a source retroactively legitimizing a past divergence by publishing a broader scope; the same reasoning applies in the opposite direction, and with a different victim. Without the temporal rule, a narrower scope published after the fact, by the source under duress, or by anyone who can influence what a sweeper fetches, manufactures an accusation about an observation that did not violate anything at the time it was made. Establishing publication order requires a witnessed history of what a source published when, which this document places out of scope (Section 15); until a deployment has one, it MUST NOT emit this kind.

A deployment that does have one records its evidence in the finding's detail under history_proof, so that the finding is independently auditable without this document standardizing any particular mechanism:

"history_proof": {
  "type": "<namespaced mechanism identifier>",
  "statement_digest": "<CORR-DIGEST of the Answer Scope relied upon>",
  "proof": { ... mechanism-defined ... }
}

statement_digest MUST equal the scope_digest of the corresponding entry in answer_scopes (Section 10). That binding is what makes the envelope evidence rather than an assertion: a proof about some other scope is evidence about something else, however valid it is in its own terms.

type MUST be namespaced with a reverse-DNS or URI prefix. This document seeds no mechanisms, so every value belongs to a third party and a bare name would claim a reservation that does not exist.

A verifier that does not recognize type MUST treat the finding as though no history proof were present, and therefore MUST NOT rely on the scope_violation. Checking the envelope is not checking the proof.

This is the strongest accusation the procedure makes, and it is available only because the declaration precedes the observation. An implementation that cannot demonstrate that ordering has not earned the accusation.

10. The Corroboration Record

An implementation MUST emit a Corroboration Record for every (sweep, subject) pair, whatever the verdict, AGREE, DIVERGENT, or INSUFFICIENT. A corroboration trail recording only disagreements cannot prove its checks ran; the agreement record is the positive attestation downstream consumers bind.

{
  "version": "corroboration/0.4",
  "alg": "EdDSA",
  "record_id": "<CORR-DIGEST of the object minus these two fields>",
  "subject": { "agent_id": "<id>", "did": "<did:key or null>" },
  "observed_at": "<RFC 3339 UTC, Z suffix>",
  "staleness_window_s": <integer>,
  "verdict": "AGREE" | "DIVERGENT" | "INSUFFICIENT",
  "claims": [
    { "source": "<label>", "vantage": "<label or null>",
      "status": "present" | "absent" | "error",
      "view": { "<field>": "<value or null>", ... } | null,
      "outcome": "<see Section 4.1>",
      "binding": { "sequence": <integer>,
                   "scope": "<issuer or null>" } }
  ],
  "findings": [
    { "kind": "<see Section 13>", "agent_id": "<id>",
      "confirmation": "suspected" | "confirmed",
      "declared": true | false,
      "declaration_version": <integer or null>,
      "detail": { ... } }
  ],
  "answer_scopes": [
    { "label": "<source label>", "subject": "did:key:z6Mk...",
      "seq": <integer>, "scope_digest": "<CORR-DIGEST>",
      "scopes": [ { "field": "<name>", "vantage_class": "<class>" } ] }
  ],
  "sweeper": "did:key:z6Mk...",
  "signature": "<base64 Ed25519 over JCS of all other members>"
}

Rules:

  1. record_id MUST equal the CORR-DIGEST of the record minus record_id and signature. A verifier MUST recompute and compare. observed_at is an RFC 3339 [RFC3339] UTC timestamp with a Z suffix.

  2. alg names the signature algorithm, using its JOSE/COSE identifier. A verifier MUST derive the algorithm from the key type the sweeper did:key encodes and MUST reject a record whose alg disagrees with it. This rule is central: a did:key states its own key type, so the algorithm was always derivable; naming it makes the obligation explicit, but a verifier that read alg and trusted it would gain a downgrade vector rather than agility, since a record could then claim one algorithm while the signature was made with another. The key is authoritative; alg states the obligation rather than deciding it. alg is REQUIRED from corroboration/0.3; a record at an earlier version carries no such member and EdDSA is implied, and one that does carry it MUST still agree. Introducing a second algorithm is a new key type and its corresponding identifier, and requires no other change to this procedure.

  3. The signature MUST verify against sweeper. Self-attestation by the sweeping party is the base tier; a record MAY additionally be registered with an append-only transparency service (e.g., a SCITT Transparency Service [SCITT]), in which case a consumer MUST NOT report the anchored tier without verifying an inclusion proof against a log key it trusts.

  4. All count and duration values are exact integers or decimal strings; floating-point values MUST NOT appear in any digest-bearing field.

  5. claims MUST include error-status entries (excluded from the diff, preserved for audit).

  6. A consumer binding a Corroboration Record from another evidence format SHOULD bind record_id and SHOULD state the verdict it relied upon.

  7. The verdict MUST be computed as follows, and only as follows. The verdict is INSUFFICIENT if and only if the record contains fewer than two present-or-absent claims. Otherwise the verdict is DIVERGENT if and only if findings is non-empty, and AGREE otherwise. A declared annotation on a finding (Section 9.4) does not affect this computation.

  8. observed_at is the observation time of every claim in the record. Claims do not carry individual timestamps; a deployment whose sweeps span a period long enough for that to matter SHOULD shorten its sweeps rather than reinterpret this field (Section 15).

  9. answer_scopes MUST list every Answer Scope the sweep relied upon, and MUST be present and empty when it relied on none. "No scope applied" and "this implementation does not record scopes" are different facts, and a verifier that cannot distinguish them cannot know whether a record is complete. Each entry MUST carry scope_digest, the CORR-DIGEST of the verified bundle, and SHOULD carry scopes. This rule is central: declared with a sequence number states that a scope was matched without stating which rule was matched or which bytes the sequence refers to, so a verifier holding only the record could not reproduce the decision. The digest binds the decision to specific bytes; carrying scopes as well spares a verifier that does not already hold the bundle from a fetch, which is what offline verification exists to avoid. A finding with declared: true is re-derived by locating the entry whose label equals the finding's source and whose seq equals its declaration_version.

  10. declared MUST be present on every finding, defaulting to false; declaration_version MUST carry the matched seq when declared is true and MUST be null otherwise. Neither member participates in any dedup or first-observation key a consumer maintains, they describe what the source had published, not which disagreement was observed, and a source publishing or revising a declaration MUST NOT change the identity of a finding already being tracked.

  11. A verifier MUST check version against the set of record versions it accepts and MUST reject any other. A verifier consuming records issued before this revision MUST accept corroboration/0.1, corroboration/0.2 and corroboration/0.3 alongside corroboration/0.4; for those versions the absence of declared is read as false, the absence of alg as EdDSA, and the absence of binding as no lease position.

  12. A claim entry whose view carries a lease position (Section 5) MUST carry binding with that position's sequence and scope; an entry whose view carries none MUST omit the member. A stale_binding finding names the sequences it compared; the claim entries are where a verifier re-derives that comparison from recorded bytes, and a verifier MUST reject a record whose stale_binding findings do not follow from its binding members under Section 6.4.

11. Reference Layer Instantiations

Discovery: view {endpoint, did, digest}, with a lease position. Resolvers:

The did field is populated only per Section 5's verified-field rule, from a signed record verified offline against its embedded did:key, with subject binding to the outer id. The digest field is compared only where both sides carry one; two sources agreeing on an endpoint and disagreeing on its digest have resolved to one host serving two documents, which is substitution at the host rather than at the source, a condition the endpoint comparison cannot express.

Identity: view {key}, the agent's Ed25519 verification key, represented in did:key multibase form (the encoding shared by did:key identifiers and Ed25519VerificationKey2020 [EDSIG2020] publicKeyMultibase, so keys compare as strings). Resolvers: did:key (offline; the self-certifying root), did:web (fetched from the method-specified document location; 404 is absent), and a Universal Resolver adapter. A method serving a different key than the root is a key divergence.

12. Relationship to Registry-Assisted Resolution of Known Subjects

Registry-assisted resolution [RAR] specifies how a discovering entity that already knows a Subject Identity, and has no usable DNS discovery anchor for it, obtains from one client-selected Resolution Registry the minimum information needed to continue discovery, together with registry provenance, subject-specific authority evidence, and a renewable binding carrying sequence, validity bounds and revocation information. It is deliberately narrow: no search, no ranking, no global root, no registry-to-registry forwarding, no trust evaluation. It also leaves open how a client that holds bindings from several registries notices that they disagree, and names a transparency mechanism for conflicting or malicious bindings as an open question.

This document is that mechanism, and the two compose without either widening its scope:

Nothing here changes what either document requires of a single registry or a single resolution. Corroboration adds obligations only to the party that chooses to hold several answers and say so.

13. Finding Kinds and Extensibility

Kinds seeded by this document: omission, source_equivocation, scope_violation, stale_description, stale_binding, agent_equivocation, and one kind per view field name of a layer defined by this document or by an extension specification (this document seeds endpoint, did, digest, key). Bare (un-prefixed) kind names are reserved for values seeded in this document and its successors; any party introducing a new kind MUST namespace it with a reverse-DNS or URI prefix. A consumer MUST treat unknown kinds as informational and MUST NOT reject a record for carrying one.

14. Conformance

A conforming implementation MUST reproduce, for the shared corpus of fixed per-claim inputs, the exact finding set of Section 6 and the exact verdicts of Section 10, including: agreement yields no findings and an AGREE record; present-beside-absent yields omission with correct attribution; distinct field values yield that field's kind; an error claim is excluded and creates no omission; a positive soft 404 is absent; vantage disagreement within one source yields source_equivocation and, absent an applicable Answer Scope, withholds that source from the cross-source field comparison; where an Answer Scope applies, that finding is annotated declared and the source's per-vantage values enter the cohort comparison instead; one subject may carry multiple simultaneous findings; and every sweep yields a record whose record_id recomputes. The corpus is view-agnostic and reused by every layer.

The corpus MUST include the following cases, which exercise the rules most likely to be implemented differently:

Table 3
Case Expected
one present, one error INSUFFICIENT; no omission
one present, one absent, one error DIVERGENT with omission; the error claim preserved
one source, two vantages, differing value, no scope source_equivocation; that source withheld from the cross-source comparison
the same, with an applicable region scope source_equivocation annotated declared; per-vantage cohort comparison proceeds
declared regional variation, values differing within one cohort the field divergence is still emitted; verdict remains DIVERGENT
a scope published after the observation no scope_violation, whatever the scope says
equal seq, differing self-description bytes agent_equivocation
endpoints differing only in host case, default port, empty path, dot segments, unreserved percent-encoding, or fragment no field divergence
endpoints differing in an encoded reserved character, path case, or query order field divergence
one verified and one unverifiable proof-carrying field the unverifiable value is null and produces no divergence
a scope whose label differs from the label it was served at only in case or a trailing solidus the scope is rejected
the same disagreement re-observed with changed values the same finding identity; confirmation advances
the same sources disagreeing in two cohorts two findings, distinct identities
two sources, same scope, sequences 13 and 14, differing endpoints stale_binding attributing the source at 13; no endpoint finding
two sources, same scope, equal sequences, differing endpoints endpoint field divergence
two sources, different scopes, unrelated sequences, equal values no finding
a source current from one vantage and superseded from another stale_binding; no source_equivocation
a superseded source beside an absent source omission lists the superseded source as present_on
a binding past expiresAt error with outcome expired; no omission
equal endpoints, differing digests digest field divergence

Answer Scope support is OPTIONAL. An implementation that does not support it conforms: with no declaration supplied, Section 6.2 yields a single cohort spanning every claim, and the procedure is identical to one with no notion of declaration. An implementation that does support it MUST NOT report AGREE for a sweep in which a declared-varying source's cohort disagrees, the redirection of Section 6.3 is not optional for an implementation that honors declarations at all, because honouring the declaration without it is strictly less safe than ignoring the declaration.

15. Security Considerations

Independence. Corroboration among k sources is worth exactly the independence among them. Sources sharing an upstream feed, an operator, or a material incentive may not provide independent corroboration, and can reproduce the same misinformation. This procedure records which sources agreed, enabling diversity-weighted consumption; it cannot manufacture diversity. Deployments SHOULD select sources with distinct operational and data lineage and SHOULD disclose known dependencies.

Residual equivocation. Cross-vantage comparison detects per-network-path splits. It cannot detect a source lying identically to all of one consumer's vantages while answering other consumers differently; that residual requires witnessed, append-only transparency over served answers ([RFC6962], [RFC9162], [CONIKS]), out of scope here. The Corroboration Record is forward-compatible with such logs (a witnessed history of records).

Sweep-level observation time. A record carries one observed_at for every claim in it, so a sweep that takes appreciable wall-clock time records claims as simultaneous when they were not. Confirmation (Section 7) is unaffected as long as the staleness window exceeds sweep duration, which it will for any realistic configuration. It does mean a record cannot be used to establish the relative ordering of two sources' answers within one sweep, and consumers MUST NOT read it as though it could.

Watch-set bootstrap. A consumer cannot detect omission of an agent it never watched; a watch set enumerated from a single source inherits that source's omissions. Watch-set construction SHOULD draw on multiple sources.

Sweeper honesty. A record attests the sweep's recorded observations, not the sweeper's honesty; a dishonest sweeper can sign a fiction. Anchoring bounds timing and prevents silent substitution; consumers with high assurance requirements SHOULD corroborate the corroborator, multiple independent sweepers over the same watch set, which this procedure supports without modification (sweep records are themselves views to diff).

Fan-out and enumeration. A sweep issues one request per configured source per watched subject. A sweeper configured with many sources, or watching many subjects, is indistinguishable from a scraper to the registries it queries, and the resolution profile treats exact-lookup endpoints as denial-of-service and enumeration targets. A deployment SHOULD rate-limit its own sweeps, SHOULD prefer a watch set the subjects consented to or the sweeper operates for, and MUST NOT derive subjects or sources from a registry's responses. Query fan-out reveals which subjects a sweeper watches to every registry it asks; a deployment with a privacy obligation SHOULD treat the watch set as sensitive.

Authority laundering. A corroborator that accepted the registry's own statement of a target's authority as a reason to prefer that target would let a registry win a comparison by asserting authority. Authority evidence is never evaluated by this procedure and never moves the verdict (Section 12); where a consumer evaluates it, the consumer does so against the recorded claims after the fact.

Staleness abuse. An attacker aware of the staleness window can rotate lies faster than confirmation. suspected findings therefore MUST be preserved in records even when never confirmed; a pattern of unconfirmed, short-lived divergences from one source is itself signal, and its evaluation is consumer policy.

Self-description replay and squatting. Addressed structurally in Section 8 (supersession, corroborated distribution, verify-back); implementations MUST NOT skip the verify-back rule, which is the sole defense against alias squatting manufacturing false divergences.

Self-issued declarations. In every deployed mechanism this procedure draws on, the party authorizing variation is not the party exhibiting it: a parent zone, a parent CA, the subject, or a standards body sets the tolerance. An Answer Scope inverts that, the source declares its own. A declaration is therefore not evidence of legitimacy and MUST NOT be treated as any; it is evidence only of what the source committed to in advance, which is precisely what makes Section 9.5 possible. Occupiability (Section 3.1) is the structural defense: a declaration on an axis a verifier can occupy is falsifiable by observation, while one on an axis it cannot is unfalsifiable self-attestation, which is why this document forbids the latter outright rather than discouraging it. Deployments SHOULD treat a source that declares broadly as less corroborative, not as more compliant.

Observer distinguishability. A source able to identify the corroborator (by network origin, credential, query shape, or timing) can serve the corroborator its declared answer and other consumers something else, and the procedure records the declared case. Occupiability bounds this but does not eliminate it: it guarantees a verifier can occupy the declared context, not that it is indistinguishable from an ordinary caller while there. Deployments SHOULD sweep from origins not attributable to the sweeping party, and consumers SHOULD weight a declaration lower where the source could plausibly identify the observer. This residual is the one described under Residual equivocation and has the same eventual answer: witnessed, append-only logs of served answers.

Privacy. A watch set reveals the sweeping party's interests to every queried source; records reveal them to every consumer. Deployments handling sensitive watch sets SHOULD consider query padding and record-level access control; both are out of scope.

16. IANA Considerations

This document requests no IANA actions. The finding-kind vocabulary is governed by the namespacing convention of Section 13; this document defines the claim outcomes of Section 4.1, the endpoint canonicalization profile endpoint-normalization/0.1 of Section 5, and defines region and asn as the initial occupiable vantage classes, with the value spaces given in Section 3.1, and a future revision MAY establish an IANA registry for further classes should the set need to grow outside this document and its successors; the record and self-description version strings are governed by this document and its successors. A future revision MAY register a media type for the Corroboration Record (candidate: application/corroboration-record+json) once transport contexts warrant it.

17. References

17.1. Normative References

[DIDCORE]
W3C, "Decentralized Identifiers (DIDs) v1.0", , <https://www.w3.org/TR/did-core/>.
[DIDKEY]
W3C Credentials Community Group, "The did:key Method", n.d., <https://w3c-ccg.github.io/did-method-key/>.
[EDSIG2020]
Sporny, M., "Ed25519Signature2020", n.d., <https://w3c-ccg.github.io/lds-ed25519-2020/>.
[ISO3166]
International Organization for Standardization, "Codes for the representation of names of countries and their subdivisions, Part 1: Country codes", ISO 3166-1:2020, .
[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
[RFC3339]
Klyne, G. and C. Newman, "Date and Time on the Internet: Timestamps", RFC 3339, DOI 10.17487/RFC3339, , <https://www.rfc-editor.org/rfc/rfc3339>.
[RFC3986]
Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, DOI 10.17487/RFC3986, , <https://www.rfc-editor.org/rfc/rfc3986>.
[RFC6793]
Vohra, Q. and E. Chen, "BGP Support for Four-Octet Autonomous System (AS) Number Space", RFC 6793, DOI 10.17487/RFC6793, , <https://www.rfc-editor.org/rfc/rfc6793>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.
[RFC8785]
Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, DOI 10.17487/RFC8785, , <https://www.rfc-editor.org/rfc/rfc8785>.

17.2. Informative References

[ANSV2]
Narajala, V. and M. Courtney, "Agent Name Service v2 (ANS): A Domain-Anchored Trust Layer for Autonomous AI Agent Identity", , <https://datatracker.ietf.org/doc/draft-narajala-courtney-ansv2/>.
[CAPSULE]
Mih, S., "An Agent Action Capsule Profile for SCITT", , <https://datatracker.ietf.org/doc/draft-mih-scitt-agent-action-capsule/>.
[CONIKS]
Melara, M., Blankstein, A., Bonneau, J., Felten, E., and M. Freedman, "CONIKS: Bringing Key Transparency to End Users", USENIX Security 2015, , <https://www.usenix.org/conference/usenixsecurity15/technical-sessions/presentation/melara>.
[DAWN-CHARTER]
IETF, "Discovery of Agents With Names (DAWN) Charter", Charter charter-ietf-dawn-01, , <https://datatracker.ietf.org/doc/charter-ietf-dawn/>.
[DAWN-REQS]
King, D. and A. Farrel, "Requirements for the Discovery of Agents With Names (DAWN)", Work in Progress, Internet-Draft, draft-king-dawn-requirements-02, , <https://datatracker.ietf.org/doc/draft-king-dawn-requirements/>.
[DAWN-TERMINOLOGY]
Farrel, A., Yao, K., Schott, R., and N. Williams, "Terminology for the Discovery of Agents With Names (DAWN)", Work in Progress, Internet-Draft, draft-farrel-dawn-terminology-06, , <https://datatracker.ietf.org/doc/draft-farrel-dawn-terminology/>.
[MPIC]
CA/Browser Forum, "Multi-Perspective Issuance Corroboration (Baseline Requirements amendment)", n.d., <https://cabforum.org/>.
[RAR]
Raskar, R., Sharaf, S., and P. Chari, "Registry-Assisted Resolution for Known Subjects Without DNS Discovery Anchors", Work in Progress, Internet-Draft, draft-raskar-agentic-web-federated-resolution-02, , <https://datatracker.ietf.org/doc/draft-raskar-agentic-web-federated-resolution/>.
[RFC6962]
Laurie, B., Langley, A., and E. Kasper, "Certificate Transparency", RFC 6962, DOI 10.17487/RFC6962, , <https://www.rfc-editor.org/rfc/rfc6962>.
[RFC9162]
Laurie, B., Messeri, E., and R. Stradling, "Certificate Transparency Version 2.0", RFC 9162, DOI 10.17487/RFC9162, , <https://www.rfc-editor.org/rfc/rfc9162>.
[RFC9457]
Nottingham, M., Wilde, E., and S. Dalal, "Problem Details for HTTP APIs", RFC 9457, DOI 10.17487/RFC9457, , <https://www.rfc-editor.org/info/rfc9457>.
[SCITT]
Birkholz, H., "An Architecture for Trustworthy and Transparent Digital Supply Chains (SCITT)", n.d., <https://datatracker.ietf.org/doc/draft-ietf-scitt-architecture/>.

Appendix A. Changes from -01

Appendix B. Changes from -00

Editorial and precision changes throughout, following review:

Appendix C. Mapping to the Reference Implementation

This appendix is informative. Where it differs from the body of this document, the normative requirements in the body govern; the reference implementation does not define behaviour.

Every normative element of this document is implemented in the reference implementation, split across two packages: the source-agnostic sm-resolver kernel (the claim model of Section 3, the view contract of Section 5, the diff of Section 6, and the confirmation discipline of Section 7, producing an unsigned sweep result with the verdict of Section 10) and sm-divergence built on it (the reference discovery and identity layers of Section 11, the agent self-description of Section 8, and the signing that seals a sweep result into the Corroboration Record of Section 10). The relationship stated in Section 12 is implemented as a thin resolver: the kernel never learns the resolution profile, and the profile never learns the comparison. The present/absent/error classification (Section 4), the view contract (Section 5), and the diff (Section 6) are the kernel's stable core; the vantage axis and source_equivocation (Sections 3, 6.3), observation time and confirmation (Section 7), the self-description schema and hardening (Section 8), and the Corroboration Record emitted on every verdict (Section 10) are all present and covered by the conformance corpus. The Answer Scope of Section 9 is split the same way: the kernel accepts an already-verified declaration as inert data and performs the cohort comparison of Section 6.2, while fetching, verifying and matching the bundle belong to the layer above, the kernel holds no keys and performs no I/O.

The reference implementation is available as open-source software (MIT License) at https://github.com/Sharathvc23/sm-divergence and https://github.com/Sharathvc23/sm-resolver, and is published on PyPI as sm-divergence and sm-resolver. This revision corresponds to sm-resolver 0.7.0 and sm-divergence 0.12.0. The supersession rule of Section 6.4 lives in the kernel (Binding, stale_binding); the known-subject resolver of Section 11, the digest field and the binding claim-entry member live in sm-divergence. The companion informative essay is "The Quilt That Checks Itself" (Stellarminds.ai / Project NANDA research).

Acknowledgements

Section 12 of this revision, and the supersession rule of Section 6.4 that it rests on, exist because of the model set out by Ramesh Raskar, Samuel Sharaf and Pradyumna Chari in "Registry-Assisted Resolution for Known Subjects Without DNS Discovery Anchors" [RAR]: a registry that resolves one known subject to one terminal target, with provenance separated from authority and a binding issued as a renewable lease. That separation, and the open question their draft leaves about conflicting bindings across registries, are what this document composes with. The author also thanks the Project NANDA community, whose quilt-of-registries architecture is the setting this procedure was built for, and the IETF DAWN community, whose charter, terminology and requirements work frame the boundary this document is careful to stay inside.

Author's Address

Sharath Chandra
Stellarminds.ai
Richmond, California
United States of America