| Internet-Draft | Agent Discovery Corroboration | October 2026 |
| Chandra | Expires 3 May 2027 | [Page] |
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.¶
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.¶
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 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.¶
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.¶
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.¶
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:¶
| 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.¶
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:¶
The scheme and host are lowercased. The path is not. A path MAY address distinct resources by case, so folding it would merge two endpoints.¶
The default port for the scheme is removed; any other port is retained.¶
An empty path becomes /, and dot segments are removed per RFC 3986 Section 5.2.4.¶
Percent-encoding triplets are uppercased, and percent-encoded unreserved characters (RFC 3986 Section 2.3) are decoded. Reserved characters remain encoded: %2F is an encoded solidus, not a path separator, and decoding it would merge two endpoints.¶
The query is compared byte for byte. Its semantics belong to the origin server, and reordering or removing parameters would assert an equivalence this procedure cannot know holds.¶
The fragment is removed. It is not transmitted to the origin server and so cannot distinguish two endpoints as addresses.¶
Hostnames are compared in their A-label (punycode) form, and a trailing dot is removed.¶
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).¶
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 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.¶
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.¶
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.¶
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.¶
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:¶
| 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.¶
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, 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.¶
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 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.¶
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:¶
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.¶
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.¶
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.¶
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.¶
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).¶
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.¶
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, 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.¶
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.¶
Discovery: view {endpoint, did, digest}, with a lease position. Resolvers:¶
a known-subject resolver for a Resolution Registry implementing registry-assisted resolution of a Known Subject [RAR] (POST {base}/resolve with the Subject Identity and its type; one hop; the target is terminal). An empty targets array and a 404 are absent; a target past expiresAt, lacking it, lacking sequence, or marked revoked is error with outcome expired, unverifiable or revoked; a target whose method was not accepted, in particular another registry, which the profile forbids as a terminal target, is error/refused; a Problem Details [RFC9457] code maps onto the outcome vocabulary of Section 4.1. targetUrl is the endpoint, targetDigest the digest, and sequence with authority.issuer the lease position. The resolver does not evaluate Authority Evidence and does not follow a target to another registry;¶
a by-id HTTP registry resolver (GET {base}/api/agents/{id}, path injectable);¶
a two-hop index resolver, retained for sources still exposing that shape (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). Under the known-subject profile a registry returns a terminal target and never another registry, so an index is one source among several and this shape is not used.¶
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.¶
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:¶
Resolution is one source's answer; corroboration is the comparison. A Resolution Registry is a source in the sense of Section 2, and each binding it returns is one claim. A Corroboration Record is the artifact a client, or an auditor acting for many clients, produces from two or more such claims.¶
No forwarding is needed. Corroboration is performed by the party holding the claims. The registries need not communicate, synchronize or route, and the prohibition on inter-registry forwarding is preserved exactly; it is the sweeper that queries each configured registry directly.¶
The sweeper is an auditor over a bounded set. The resolution profile forbids unbounded registry fan-out and requires exact lookup without enumeration. A corroboration sweep satisfies both: its source set is the explicitly configured set of Section 2, it issues one exact resolution request per source per subject, and it enumerates nothing. A deployment MUST NOT use a sweep to discover registries or subjects it was not configured with.¶
Lease sequence is the supersession signal. The binding's sequence within its authority scope is read by Section 6.4, so a registry serving a superseded binding is attributed as stale_binding and never compared against the current one. This is the mechanism by which "two registries disagree" is distinguished from "one registry is behind", which the resolution profile's freshness rules require a resolver to handle and which a naive comparison would report as tampering.¶
The direct path is a source. Where a validated direct DNS-anchored discovery path exists, the resolution profile requires it to remain direct and forbids silent downgrade to a weaker registry result. A corroborator MAY treat the direct path as one source beside the registries: a registry result that diverges from a current direct result is then a finding attributing the registry, which is the detector that downgrade rule otherwise lacks.¶
Authority Evidence is provenance, never verdict. The resolution profile separates registry provenance from subject authority and leaves acceptability of authority evidence to local policy. This document carries the authority issuer as the scope a sequence counts in and MUST NOT let it move the verdict: a corroborator that preferred the claim with the stronger authority would be performing the trust evaluation that both the DAWN charter [DAWN-CHARTER] and the resolution profile exclude. The issuer MAY annotate a finding for the consumer, as declared annotates one (Section 9.4).¶
Answer Scope and target scope. The resolution profile's optional per-target scope (a bounded audience, role or deployment) is the registry-side counterpart of the source-published Answer Scope of Section 9: both declare, in advance, that an answer is intended to vary along a stated dimension. A deployment that corroborates scoped targets SHOULD align the Answer Scope vantage class with the target scope dimension so that the cohort comparison of Section 6.2 compares like with like.¶
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.¶
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.¶
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:¶
| 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.¶
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.¶
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.¶
Section 4.1 (new). Claim outcomes become a seeded vocabulary: timeout, unreachable, refused, unparseable, unverifiable, with the namespacing rule already used for finding kinds. -01 left the member free-form, which would have produced incomparable strings for the same condition across implementations. States that an outcome MUST NOT affect which claims enter the diff.¶
Section 10. The record carries alg, the JOSE/COSE identifier of the signature algorithm, and a verifier MUST derive the algorithm from the key type the sweeper did:key encodes and reject a record whose alg disagrees. -01 named no algorithm at all, so a verifier had to know it out of band and a second algorithm could not be introduced without silently changing what every existing record meant. Record version becomes corroboration/0.3; alg is REQUIRED from it and implied EdDSA before it.¶
Section 15. Records that the claim outcomes are governed by this document.¶
Section 2. Defines Binding: a source's subject-to-target association as a renewable lease with a sequence per authority scope, and distinguishes it from the self-description seq. Aligns the definition of source with DAWN terminology.¶
Section 4.1. Seeds expired and revoked outcomes: a binding the resolution profile forbids treating as current is an error claim, never absent.¶
Section 5. A view MAY carry a lease position; it is not compared, and neither is the authority issuer.¶
Section 6.4 (new). Supersession: within one authority scope a claim below the current sequence is attributed as stale_binding and withheld from field divergence and source_equivocation. Lag is attributed, not reported as tampering. Identity is (source, scope).¶
Section 10. Claim entries carry binding; record version becomes corroboration/0.4; rule 12 requires stale_binding findings to follow from recorded bindings.¶
Section 11. The discovery view gains digest; a known-subject resolver for registry-assisted resolution (POST /resolve) is the first discovery resolver; the two-hop index resolver is retained as legacy.¶
Section 12 (new). States the relationship to registry-assisted resolution of known subjects and the DAWN charter: resolution is one source's answer, corroboration the comparison; no forwarding; bounded fan-out and exact lookup; authority evidence is provenance, never verdict; the direct DNS path as a source; Answer Scope beside target scope.¶
Section 13. Seeds stale_binding and digest.¶
Section 14. Conformance cases for supersession, expiry and digest.¶
Section 15. Adds Fan-out and enumeration and Authority laundering.¶
Em dashes replaced with commas, colons and parentheses throughout. The document is now ASCII-only, which removes the non-ASCII line-length reports from submission checking.¶
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.¶
Section 2 (new definition). A source label is defined: a URI, stable and unique within the deployment, compared by exact octet equality and never normalized. -01 used labels throughout without saying what one is, while Section 9.2 relied on serving at a label to demonstrate control of it. The section also states plainly that this document defines no global source-identity system.¶
Section 5. URI canonicalization is pinned as endpoint-normalization/0.1 rather than deferred to RFC 3986 Section 6, which leaves percent-encoding, dot-segments, empty-versus-root path, fragments and IDNA to the implementer. Two conforming implementations choosing differently produce a false field divergence between honest sources.¶
Section 7 (new subsection). Finding identity is defined per kind, and observed values are excluded from it. -01 left it unspecified, and an implementation keying on values makes a source that rotates its answer restart as suspected forever, never reaching confirmed. The cohort participates, or the same sources disagreeing in two contexts collapse into one finding.¶
Section 9.5. Adds the history_proof envelope so a deployment holding a witnessed publication history can record it, bound by digest to the scope relied upon, without this document standardizing any mechanism.¶
Section 10. The record carries answer_scopes. -01 recorded that a scope sequence was matched but not which rule or which bytes, so a declared finding could not be re-derived from the record, contradicting the property asserted throughout.¶
Section 13. Adds conformance cases for the rules most likely to be implemented differently.¶
Appendix. States that the implementation mapping is informative and that the body governs.¶
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 13 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.¶
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).¶
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.¶