| Internet-Draft | Agent Discovery Corroboration | August 2026 |
| Chandra | Expires 28 February 2027 | [Page] |
AI agents are discovered and identified through independent sources, including registries, name services, DID methods, and catalogs. A single source can misrepresent an agent by omission, withholding a record it holds, or by equivocation, serving different answers to different observers. A signature on a served artifact does not, by itself, defend against either behavior. This document specifies a corroboration procedure that classifies one source's claim about one agent observed from one network vantage; reduces claims to comparable views; diffs claims into findings with deterministic attribution; distinguishes legitimate propagation delay from persistent disagreement; and emits a signed Corroboration Record for every sweep, including agreement, that other evidence formats can bind by digest. The procedure is source-, format-, and layer-agnostic; requires neither cooperation from nor modification of any source; and is verifiable from recorded bytes.¶
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 28 February 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
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 (Section 4), the view contract (Section 5), the diff (Section 6), the confirmation discipline (Section 7), the self-description mechanism (Section 8), the Answer Scope (Section 9), and the Corroboration Record (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.¶
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.¶
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).¶
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.¶
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.¶
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.¶
For a source S, vantage V, and agent A, a resolver MUST classify the outcome as exactly one of:¶
present — S served a claim for A, reduced to a view (Section 5).¶
absent — S positively asserts A is unknown. Each resolver defines the positive-absence predicate for its source type; for HTTP sources a 404 MUST be classified absent, and a 200 whose body positively signals not-found SHOULD be classified absent. Non-normative examples of the latter include {"success": false} bodies and error strings containing "not found".¶
error — any other outcome: connection failure, timeout, non-absence error status, unparseable body, or an answer that the resolver cannot map to a view.¶
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.¶
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. For URI-valued fields (e.g., endpoints), implementations MUST apply syntax-based normalization per RFC 3986 [RFC3986] Section 6 (case of scheme and host, default ports, empty path) before comparison. For key-valued fields whose layer defines a did:key representation, the multibase did:key form is the comparison encoding.¶
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).¶
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.¶
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.¶
For each field name appearing in any present view, collect the non-null values keyed by (source, vantage). Comparison is scoped to a cohort: the claims sharing one vantage identifier.¶
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.¶
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.¶
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).¶
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.¶
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.¶
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:¶
suspected — first observation of the disagreement.¶
confirmed — the disagreement was re-observed in a sweep whose observed_at exceeds the first observation by at least the deployment's configured 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.¶
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:¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
{
"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.¶
A verifier MUST reject a bundle unless all of the following hold.¶
The signature verifies against the key subject encodes.¶
label equals the label the bundle was retrieved from, by exact octet equality — 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.¶
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.¶
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.¶
No field appears twice. Overlapping scopes would let an operator select, after the fact, whichever applies.¶
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.¶
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:¶
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.¶
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 14); until a deployment has one, it MUST NOT emit this kind.¶
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.¶
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.2",
"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": "<resolver outcome string>" }
],
"findings": [
{ "kind": "<see Section 12>", "agent_id": "<id>",
"confirmation": "suspected" | "confirmed",
"declared": true | false,
"declaration_version": <integer or null>,
"detail": { ... } }
],
"sweeper": "did:key:z6Mk...",
"signature": "<base64 Ed25519 over JCS of all other members>"
}
¶
Rules:¶
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.¶
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.¶
All count and duration values are exact integers or decimal strings; floating-point values MUST NOT appear in any digest-bearing field.¶
claims MUST include error-status entries (excluded from the diff, preserved for audit).¶
A consumer binding a Corroboration Record from another evidence format SHOULD bind record_id and SHOULD state the verdict it relied upon.
5a. 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.
5b. 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 14).¶
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.¶
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 alongside corroboration/0.2; the added members are the only difference, and their absence is read as declared: false.¶
Discovery — view {endpoint, did}. Resolvers: a by-id HTTP registry resolver (GET {base}/api/agents/{id}, path injectable); a two-hop index resolver (resolve a locator to {registry_url, identifier}, then fetch the record; a missing or empty hop field yields an error claim, a not-found at either hop is absent). 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.¶
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.¶
Kinds seeded by this document: omission, source_equivocation, scope_violation, stale_description, 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, 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.¶
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.¶
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.¶
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).¶
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.¶
This document requests no IANA actions. The finding-kind vocabulary is governed by the namespacing convention of Section 12; this document 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.¶
Section 3.1 (new). Vantage becomes a controlled term: class:value identifiers compared by exact octet equality, constrained by shared denotation, occupiability, non-overlap and bounded cardinality. In -00 a vantage was "a network perspective" with no constraint, which left cross-source comparison within a vantage unsound.¶
Section 6.2. Cross-source field comparison is scoped to a cohort. Where no source declares variation for a field this is a single cohort spanning every claim, i.e. the -00 comparison unchanged.¶
Section 6.3. The withholding of an equivocating source from the cross-source comparison becomes conditional. It is replaced by cohort participation where an Answer Scope applies, never waived.¶
Section 9 (new). The Answer Scope: a source-signed, in-advance declaration of which fields it varies and along which vantage class, with the verification rules that keep it from becoming a blanket exemption. Declaration annotates a finding; it never withdraws one.¶
Section 10. Findings carry declared and declaration_version; record version becomes corroboration/0.2, and a verifier is now required to check the version against an accepted set (corroboration/0.1 remains acceptable).¶
Section 12. Seeds the scope_violation kind.¶
Section 13. Conformance states that Answer Scope support is OPTIONAL and that an implementation without it conforms unchanged; an implementation with it MUST perform the redirection.¶
Section 14. Adds Self-issued declarations and Observer distinguishability.¶
Section 15. Defines region and asn as the initial occupiable vantage classes and notes an IANA registry as a future option.¶
Editorial and precision changes throughout, following review:¶
The abstract and Section 1 no longer state that a signature offers no defense; they state that a signature does not, by itself, defend against omission or equivocation, and that self-certification detects tampering when verified against an expected identity.¶
Section 3.1 identifies its constraint as a regular expression, and gives region and asn externally defined value spaces (ISO 3166-1 alpha-2 [ISO3166] and 32-bit ASNs [RFC6793]). Seeding a class name without a value space left shared denotation unenforceable.¶
Section 6.2 states the cohort rule as four numbered rules rather than prose.¶
Sections 2, 3, 7 and 10 reconcile observed_at. -00 and earlier -01 text required every claim to carry one while the record schema carried a single sweep-level value; the sweep-level value is now normative and the limitation is recorded in Section 14.¶
Section 7 requires exactly one staleness window per deployment and says why per-source windows are not offered: the record carries a single staleness_window_s.¶
Section 9.5 requires that a scope be shown to predate the observation it contradicts before scope_violation may be emitted, and states that a deployment without a witnessed publication history MUST NOT emit the kind. Without it, a narrower scope published after the fact manufactures an accusation.¶
Section 10 states the verdict computation exhaustively, and drops "monetary" from the numeric rule (no monetary fields are defined).¶
Section 12 no longer refers to a "registered layer"; this document defines no layer registry.¶
Section 4 replaces the language-specific "MUST NOT raise" with a requirement to return an error claim.¶
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 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. The companion informative essay is "The Quilt That Checks Itself" (Stellarminds.ai / Project NANDA research).¶