<?xml version="1.0" encoding="utf-8"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" version="3" category="exp" docName="draft-litzki-sovp-04" ipr="trust200902" submissionType="IETF" xml:lang="en">

  <front>
    <title abbrev="SOVP">Sovereign Validation Protocol (SOVP)</title>
    
    <author fullname="Thorsten Litzki" initials="T." surname="Litzki">
      <organization>Litzki Systems LLC</organization>
      <address>
        <postal>
          <street>7901 4th St N, #32272</street>
          <city>St. Petersburg</city>
          <region>FL</region>
          <code>33702</code>
          <country>USA</country>
        </postal>
        <email>ietf@litzki-systems.com</email>
      </address>
    </author>

    <date year="2026" month="October" day="7"/>

    <area>Security</area>
    <keyword>Validation</keyword>
    <keyword>Cryptography</keyword>
    <keyword>Autonomous Agents</keyword>
    <keyword>Layer 0</keyword>

    <abstract>
      <t>This document specifies the Sovereign Validation Protocol (SOVP), a protocol for checking, before ingestion, that a signed identity document was produced by the party that controls the Ed25519 key published in DNS for a host. A consumer retrieves a JSON document from a well-known location on the host, canonicalizes it, and verifies its signature against the key published in a DNS TXT record. The protocol defines the signed scope, the retrieval and key resolution rules, freshness checks, resource limits for unverified input, and an optional extension that binds a document to a deployment instance. It does not establish legal identity, the accuracy of content, or the configuration of the system that serves the document.</t>
    </abstract>

  </front>

  <middle>
    <section title="Introduction" anchor="introduction">
      <t>Autonomous agents and other automated consumers ingest data from sources they cannot inspect. SOVP lets a source publish a signed identity document together with a DNS-published public key, so that a consumer can check, at the point where data enters its system, that the document was produced by the party that controls the key published under the source's host name.</t>
      <t>This document defines the signed document, its canonicalization and signature check, the rules for retrieving the document and resolving the key, freshness checks, resource limits for unverified input, and an optional extension that binds a document to a deployment instance. It does not define how a consumer learns which host to check for a given request. It does not establish legal identity, content accuracy, or the hardening state of a system (see <xref target="non-goals"/>).</t>
      <t>This document is Experimental. The experiment is to gather implementation and deployment experience with host-anchored identity documents and with the optional instance extension in <xref target="instance-binding"/>. The extension is a candidate for removal or revision if that experience shows it is not useful.</t>
    </section>

    <section title="Conventions and Definitions" anchor="conventions">
      <t>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 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they appear in all capitals, as shown here.</t>
      <dl>
        <dt>Layer 0:</dt>
        <dd>The ingestion boundary, the point at which a consuming system decides whether to accept or reject externally supplied data before application-level processing. It is not an OSI reference model layer or a network position.</dd>
        <dt>Entity:</dt>
        <dd>The party that publishes a SOVP document for a host.</dd>
        <dt>Validating Agent:</dt>
        <dd>A client that verifies a SOVP document locally before committing data to its memory (Mode A).</dd>
        <dt>Sovereign Gateway:</dt>
        <dd>An infrastructure-level gateway that verifies requests on behalf of a protected cluster (Mode B).</dd>
        <dt>Verifier:</dt>
        <dd>A Validating Agent or a Sovereign Gateway.</dd>
      </dl>
    </section>

    <section title="Non-Goals" anchor="non-goals">
      <t>SOVP attributes a signed document to the holder of the key published for a host, and is designed to prove no more than that. It establishes that the document was produced by whoever controlled the private key corresponding to a K_pub published in DNS for the retrieval host (see <xref target="non-repudiation"/> in <xref target="security-considerations"/> for the precise scope, including what it does not establish about legal or real-world identity). SOVP does not validate the semantic accuracy or the truthfulness of the content provided by the entity. It does not establish the security posture, configuration, or hardening of the host that serves the document, and it does not authenticate the sender of a request that names a host. It does not replace downstream fact-checking or qualitative analysis.</t>
    </section>

    <section title="Signature Verification Function: Psi_core" anchor="psi-core">
      <t>The core of the validation is the signature verification function, denoted as Psi_core. It returns a binary result indicating whether the digital signature over the declared entity identity's output is valid. To ensure global interoperability, the identity metadata (M), the signed scope defined in <xref target="tech-spec"/>, MUST be processed using the JSON Canonicalization Scheme (JCS) as defined in <xref target="RFC8785"/>.</t>
      <t>The validation follows the formal function of the Edwards-curve Digital Signature Algorithm, Ed25519, in its pure mode per <xref target="RFC8032"/>, including the range check on the signature component S in Section 5.1.7 of that document. Implementations MUST NOT apply any external hash function to JCS(M) before passing it to Verify; Ed25519 pure mode consumes the canonicalized payload directly.</t>

      <artwork><![CDATA[
Psi_core = Verify(K_pub, sigma, JCS(M))
      ]]></artwork>

      <t>Where:</t>
      <ul>
        <li>K_pub is a public key from the set published in the DNS TXT record for the retrieval host (see <xref target="dns-txt-format"/>). The DNS TXT record is the sole source of K_pub: a verifier MUST NOT take K_pub from an HTTP header field or from the document.</li>
        <li>sigma is the digital signature provided within the integrity_proof.</li>
        <li>JCS(M) is the canonicalized identity metadata, passed directly to Verify with no external pre-hash applied.</li>
        <li>Verify is the deterministic function returning 1 (true) if the signature is valid, or 0 (false) if the integrity is compromised.</li>
      </ul>
    </section>

    <section title="Technical Specification: The sovp-identity.json Structure" anchor="tech-spec">
      <t>The implementation relies on a signed JSON object retrieved over HTTPS (the "https" scheme of <xref target="RFC9110"/>) from a well-known location on the host, per <xref target="RFC8615"/>. The document is JSON <xref target="RFC8259"/>; a verifier need not perform JSON-LD processing. The primary retrieval path is /.well-known/sovp-identity.json; if that path returns HTTP status 404, a Validating Agent or Gateway MUST fall back to /sovp-identity.json at the host's root, as defined normatively in <xref target="fallback-logic"/>. This object serves as the primary data carrier for the sovereign identity.</t>
      <t>Schema (illustrative values; a complete, verifiable document is given in <xref target="test-vectors"/>):</t>

      <sourcecode type="json"><![CDATA[
{
  "@context": "https://litzki-systems.com/protocol/v2.0",
  "@type": "SovereignIdentity",
  "entity": {
    "uid": "urn:sovp:example-entity",
    "canonical_url": "https://example.org",
    "verification_method": "Ed25519"
  },
  "freshness": {
    "created": "2026-06-01T12:00:00Z",
    "nonce": "example-nonce-0001",
    "expiresAt": "2026-12-01T00:00:00Z"
  },
  "integrity_proof": {
    "signature": "<standard Base64 of the 64-octet signature>",
    "public_key_ref": "dns:txt:_sovp.example.org"
  },
  "contentAddress": {
    "alg": "sha256",
    "digest": "<hex SHA-256 digest>"
  }
}
      ]]></sourcecode>

      <table>
        <thead>
          <tr><th>Member</th><th>Requirement</th><th>Meaning</th></tr>
        </thead>
        <tbody>
          <tr><td>@context</td><td>REQUIRED</td><td>Opaque schema identifier that is not dereferenced. For this revision: https://litzki-systems.com/protocol/v2.0. It versions the document schema; the v=SOVP1 token of the DNS record versions the key record format, independently.</td></tr>
          <tr><td>@type</td><td>REQUIRED</td><td>The string "SovereignIdentity".</td></tr>
          <tr><td>entity.uid</td><td>REQUIRED</td><td>Opaque identifier chosen by the signer. This document assigns it no structure and verifiers MUST NOT parse it. The urn:sovp: form used in examples does not denote a registered URN namespace.</td></tr>
          <tr><td>entity.canonical_url</td><td>REQUIRED</td><td>URL of the publishing entity. Its host MUST equal the host from which the document was retrieved (see <xref target="verification-order"/>). A verifier MUST NOT use it to select the DNS name to query.</td></tr>
          <tr><td>entity.verification_method</td><td>REQUIRED</td><td>The string "Ed25519".</td></tr>
          <tr><td>entity.owner</td><td>OPTIONAL</td><td>Self-declared display name (see <xref target="implementation-status"/>).</td></tr>
          <tr><td>freshness</td><td>REQUIRED</td><td>Object with created, expiresAt (both REQUIRED) and nonce (OPTIONAL), covered by the signature (see <xref target="replay-protection"/>). Timestamps use the RFC 3339 date-time format <xref target="RFC3339"/> in UTC with a literal Z suffix and no fractional seconds.</td></tr>
          <tr><td>integrity_proof.signature</td><td>REQUIRED</td><td>Ed25519 signature over JCS(M), encoded as standard Base64 (Section 4 of <xref target="RFC4648"/>, with padding) of the 64-octet signature value.</td></tr>
          <tr><td>integrity_proof.public_key_ref</td><td>OPTIONAL</td><td>Informational pointer to the DNS name of the key. A verifier MUST NOT use it to select the key.</td></tr>
          <tr><td>contentAddress</td><td>OPTIONAL</td><td>See <xref target="content-address-scope"/>.</td></tr>
          <tr><td>scan</td><td>OPTIONAL</td><td>Non-normative vendor extension, outside the signed scope.</td></tr>
          <tr><td>instance</td><td>OPTIONAL</td><td>Deployment instance binding (see <xref target="instance-binding"/>).</td></tr>
        </tbody>
      </table>

      <t>The signed scope is determined by an algorithmic reduction of the received document, not by an open-ended textual exclusion. Implementations (both signer and verifier) MUST compute it as follows:</t>
      <ol>
        <li>Start from the received (or, when signing, the about-to-be-served) top-level JSON object.</li>
        <li>Remove exactly the following top-level keys, if present: integrity_proof, contentAddress, scan. No other top-level key is removed, regardless of its name. In particular, an unrecognized top-level key that is not one of these three names MUST be retained in the object to be canonicalized, not dropped.</li>
        <li>Canonicalize the remaining object per JCS (<xref target="RFC8785"/>). This canonicalized byte string is JCS(M), the signed scope.</li>
        <li>Verify Ed25519(K_pub, sigma, JCS(M)) per <xref target="RFC8032"/> pure mode, where sigma is integrity_proof.signature.</li>
      </ol>
      <t>This is a fixed three-member exclusion (negative) list, not a positive allow-list of permitted fields: { integrity_proof, contentAddress, scan } are the only top-level keys ever removed before canonicalization. integrity_proof is excluded because a signature cannot cover itself; contentAddress is excluded because it is computed after signing, over this same reduced byte range (see <xref target="content-address-scope"/> below); scan is excluded because it is a non-normative vendor extension object carrying scanner output, not part of the identity claim. As of schema v2.0, freshness is part of the signed scope (it is not one of the three excluded keys); under schema v1.4, the equivalent created/nonce/expiresAt fields lived inside integrity_proof and were therefore not cryptographically bound to the signature.</t>
      <t>A verifier that encounters any top-level field not named integrity_proof, contentAddress, or scan MUST include it in the object it canonicalizes, exactly as received, even if the field name is unfamiliar to that verifier. Only the literal three-member exclusion set applies, not a positive list of permitted field names. A signer that adds a new top-level field outside these three names must therefore expect every verifier to include that field in JCS(M), and a verifier that applies the exclusion rule correctly canonicalizes and verifies such a document without knowing the field in advance.</t>

      <section title="Content Address Scope" anchor="content-address-scope">
        <t>contentAddress, when present, is an object with the fields alg and digest. digest is the SHA-256 hash, encoded as lowercase hexadecimal, of exactly the same byte sequence that forms JCS(M), the signed scope defined in <xref target="deployment-signing-correctness"/> below, i.e. it is computed by applying the identical three-key exclusion algorithm (removing integrity_proof, contentAddress, and scan, and nothing else) and the identical JCS canonicalization step, then hashing the result. It is computed after signing and is itself unsigned. Its purpose is to let an external consumer (for example, a catalog or index that references this document by its content hash) pin and verify the exact content of the identity claim without re-running Ed25519 verification; such a consumer recomputes the digest over the received document, using the same exclusion algorithm, and compares it to the referenced value.</t>
        <t>contentAddress is unauthenticated. It is not covered by the Ed25519 signature (in this design it is computed after signing and is not part of the signed scope) and an attacker who can modify or replace the served document can freely remove, alter, or fabricate a contentAddress object without invalidating Psi_core, because contentAddress is one of the three keys stripped before signature verification. contentAddress therefore provides content-identity (consistency/indexing) verification only. It MUST NOT be treated as evidence of authenticity, integrity against tampering, or non-repudiation; those guarantees are established exclusively by a successful Psi_core verification of integrity_proof.signature. A Validating Agent or Sovereign Gateway MUST NOT base any trust decision, including partial or conditional acceptance of a document, on the presence, absence, or matching of contentAddress.</t>
      </section>

      <section title="Deployment Signing Correctness" anchor="deployment-signing-correctness">
        <t>Implementations MUST canonicalize and sign only the fields inside the signed scope defined above; canonicalizing the full document including integrity_proof would make signing self-referential and is invalid. The exclusion set is exactly { integrity_proof, contentAddress, scan }; no other field is ever excluded on the basis of being an "extension" or being unrecognized. Verifiers MUST apply the identical three-key exclusion set before recomputing JCS(M) against the received signature; a verifier that excludes a field beyond this set, or fails to exclude one of these three, will report some or all genuinely valid documents as invalid.</t>
      </section>
    </section>

    <section title="Protocol Execution Sequence" anchor="execution-sequence">
      <t>SOVP defines two operational modes:</t>
      <table>
        <thead>
          <tr><th>Mode</th><th>Actor</th><th>Description</th></tr>
        </thead>
        <tbody>
          <tr><td>Mode A</td><td>Validating Agent (Client)</td><td>The agent verifies the document of a named host locally before committing data to memory.</td></tr>
          <tr><td>Mode B</td><td>Sovereign Gateway (Server)</td><td>An infrastructure-level gateway verifies, on behalf of a protected cluster, the document of the host that a request names or claims to originate from.</td></tr>
        </tbody>
      </table>
      <t>In both modes the verifier evaluates a host and not a requester. SOVP documents are public, so presenting or knowing a document proves nothing about the sender of a request. How a gateway determines the host to check is outside the scope of this document.</t>
      <t>Execution sequence:</t>
      <ol>
        <li>Public key exposure: The entity publishes its Ed25519 public key in a DNS TXT record at _sovp.&lt;host&gt;, the sole source of K_pub (see <xref target="dns-txt-format"/>).</li>
        <li>Artifact retrieval: The verifier retrieves sovp-identity.json, per <xref target="RFC8615"/>, from /.well-known/sovp-identity.json on the host, and falls back to /sovp-identity.json at the host's root only as defined in <xref target="fallback-logic"/>.</li>
        <li>Verification: The verifier applies the ordered steps of <xref target="verification-order"/>.</li>
        <li>Result: A Validating Agent MUST NOT ingest data unless the result is verified or, for a verifier claiming instance binding conformance, accepted. A Sovereign Gateway MUST reject the request as defined in <xref target="rejection-status-codes"/> in every other case.</li>
      </ol>

      <section title="Retrieval and Fallback" anchor="fallback-logic">
        <t>Retrieval path fallback: the verifier falls back from /.well-known/sovp-identity.json to /sovp-identity.json if and only if the well-known path returned HTTP status 404 (Not Found). Any other outcome on the well-known path (a 5xx server error, a connection timeout, a TLS handshake failure, or a connection reset) MUST cause the verifier to fail the verification for that request outright; it MUST NOT fall back to the root path in these cases. This restriction exists because an attacker who can selectively disrupt one retrieval path (for example, by causing 5xx responses or timeouts on /.well-known/ while serving a forged document at the root path) would otherwise be able to force a verifier onto a fallback path of the attacker's choosing.</t>
        <t>If the host does not support SOVP, provides no sovp-identity.json at either retrieval path (the well-known path returns 404 and the root path returns no document), the outcome is evidence_missing. A Sovereign Gateway then acts as follows, as it does for every other outcome except verified and accepted:</t>
        <ul>
          <li>Standard action: the request is rejected per <xref target="rejection-status-codes"/>.</li>
          <li>Exception: Local allow-lists MAY be defined for legacy systems, though these bypass the Layer 0 integrity guarantee.</li>
        </ul>
      </section>

      <section title="Verification Order and Outcomes" anchor="verification-order">
        <t>A verifier applies the following steps in this order and returns the outcome of the first step that fails. Steps marked (B) apply to a verifier that claims instance binding conformance (see <xref target="instance-binding"/>); other verifiers skip them. The retrieval host is the host component of the URL from which the document was retrieved, in lowercase A-label form <xref target="RFC5890"/> without port or trailing dot. A verifier MUST NOT verify a document retrieved from a host given as an IP literal.</t>
        <ol>
          <li>Retrieval and input limits: if no document is obtained, the outcome is evidence_missing. If the document violates the limits of <xref target="resource-limits"/>, is not a JSON object, or declares a @context, @type, or entity.verification_method that the verifier does not support, the outcome is document_invalid. A verifier claiming instance binding conformance supports only the @context value https://litzki-systems.com/protocol/v2.0, the @type "SovereignIdentity", and the verification_method "Ed25519", and a document that declares an earlier schema version MUST NOT yield accepted. Other verifiers MAY support earlier schema versions as described in <xref target="replay-protection"/>.</li>
          <li>(B) Authorization availability: if the verifier has no authorization path (see <xref target="anchor-authorization"/>) for the retrieval host, the outcome is authorization_undetermined.</li>
          <li>Host binding: if the host of entity.canonical_url, normalized in the same way, differs from the retrieval host, the outcome is host_mismatch.</li>
          <li>Key resolution: the verifier resolves the key set as specified in <xref target="dns-txt-format"/>. If resolution fails or yields no valid key, the outcome is key_unresolved.</li>
          <li>Signature: the verifier evaluates Psi_core under each key of the set. If none verifies, the outcome is invalid_signature. The key under which the signature verifies is the verifying key.</li>
          <li>(B) Anchor authorization: if the authorization path does not authorize the verifying key for the pair (retrieval host, entity.uid), the outcome is anchor_untrusted.</li>
          <li>Freshness: the verifier applies the checks of <xref target="replay-protection"/>. A missing, malformed, or inconsistent freshness member (items 1 and 2 there) yields document_invalid. A failure of the issuance window or of the expiry (items 3 and 4 there) yields stale. A verifier claiming instance binding conformance MUST apply the issuance window with a configured W.</li>
          <li>(B) Instance member: if the document has no instance member, the outcome is instance_missing.</li>
          <li>(B) Binding: if the checks of <xref target="instance-object"/> fail, the outcome is instance_mismatch.</li>
          <li>Otherwise the outcome is accepted for a verifier claiming instance binding conformance and verified for any other verifier.</li>
        </ol>
        <table>
          <thead>
            <tr><th>Outcome</th><th>Meaning</th></tr>
          </thead>
          <tbody>
            <tr><td>verified</td><td>Signed by the current holder of a key published for the retrieval host, names that host, and is fresh. This is attribution only.</td></tr>
            <tr><td>accepted</td><td>As verified, and in addition the key is authorized through an external path and the document is bound to the expected instance.</td></tr>
            <tr><td>evidence_missing</td><td>No document was obtained.</td></tr>
            <tr><td>document_invalid</td><td>Limits exceeded, not parseable, unsupported version, type or method, or malformed or inconsistent freshness members.</td></tr>
            <tr><td>authorization_undetermined</td><td>No authorization path is available or configured.</td></tr>
            <tr><td>host_mismatch</td><td>The document names a different host than the one it was retrieved from.</td></tr>
            <tr><td>key_unresolved</td><td>The key set could not be resolved or is empty.</td></tr>
            <tr><td>invalid_signature</td><td>Psi_core failed under every key of the set.</td></tr>
            <tr><td>anchor_untrusted</td><td>An authorization path exists and does not authorize the verifying key.</td></tr>
            <tr><td>stale</td><td>The issuance window or the expiry check failed.</td></tr>
            <tr><td>instance_missing</td><td>The document has no instance member.</td></tr>
            <tr><td>instance_mismatch</td><td>The document describes a different instance or descriptor than the one under evaluation.</td></tr>
          </tbody>
        </table>
        <t>verified means that the document was produced by the current holder of a key published for the retrieval host. It does not mean that the holder is authorized to attest anything about the host or about an instance (see <xref target="anchor-authorization"/>). The fixed order makes each outcome deterministic for a given input, which allows independent implementations to be compared test case by test case (see <xref target="test-vectors"/>).</t>
      </section>

      <section title="Rejection Status Codes" anchor="rejection-status-codes">
        <t>A Sovereign Gateway that rejects a request at Layer 0 MUST respond with status code 403 (Forbidden) <xref target="RFC9110" section="15.5.4"/>. This applies to every outcome other than verified and accepted. Status code 401 is not used: <xref target="RFC9110" section="15.5.2"/> requires a 401 response to carry a WWW-Authenticate challenge, and this document defines no HTTP authentication scheme. Status code 422 is not used either, because <xref target="RFC9110"/> ties it to a well-formed request that is semantically erroneous at the application layer, which does not describe a Layer 0 credential failure. A gateway MAY include a problem details object <xref target="RFC9457"/> that distinguishes the outcomes; this document defines no problem types.</t>
      </section>
    </section>

    <section title="DNS TXT Record Format and Resolution" anchor="dns-txt-format">
      <t>The public key is published as a TXT resource record at the node name _sovp.&lt;domain&gt; (see <xref target="dns-node-names"/>), where &lt;domain&gt; is defined below. The record value MUST be formatted as:</t>
      <artwork><![CDATA[
v=SOVP1; k=<base64>
      ]]></artwork>
      <t>k is the 32-byte raw Ed25519 public key, encoded using standard Base64 (not Base64url) as defined in Section 4 of <xref target="RFC4648"/>, with no line breaks. The literal v=SOVP1 prefix identifies the record as a SOVP version-1 key record and MUST be matched exactly (case-sensitive) by a conformant verifier; the semicolon-separated k= parameter carries the key value.</t>
      <t>Multiple _sovp TXT records: a zone MAY publish more than one _sovp TXT record to support key rotation. A verifier collects the records whose complete value begins with v=SOVP1 and parses each as specified above. A record that cannot be parsed, or whose k value is not the strict Base64 encoding of exactly 32 octets, is ignored. The remaining keys form an unordered set, and the order in which a resolver returns records has no meaning. A verifier MUST consider at least 4 keys and MAY consider more; a zone SHOULD NOT publish more than 4 matching records, because keys beyond a verifier's limit are ignored in an unspecified order. If DNS resolution fails (including NXDOMAIN, SERVFAIL, a timeout, and a DNSSEC validation failure) or the set is empty, verification MUST fail with the outcome key_unresolved.</t>
      <t>Multiple character-strings within a single TXT RR: <xref target="RFC1035"/> allows a single TXT RDATA to contain more than one &lt;character-string&gt; (each limited to 255 octets). A verifier MUST concatenate all character-strings of a record, in order and without separators, before parsing the record. The complete v=SOVP1; k=&lt;base64&gt; record is well under 255 octets (a Base64-encoded 32-byte key is 44 characters), so a signer SHOULD publish it as a single character-string.</t>
      <t>DNS name derivation: the DNS name to query is formed by prepending the label _sovp. to the retrieval host, which is the host component of the URL from which the document was retrieved (see <xref target="verification-order"/>). The resolver's normal answer to the TXT query is used, including the result of following a CNAME at that name. No subdomain tolerance applies: a document retrieved from foo.example.com MUST be verified against _sovp.foo.example.com, and a verifier MUST NOT fall back to querying _sovp.example.com or any other ancestor domain if the exact-match query yields no record. This exact-match rule prevents an attacker who controls a subdomain from having their key implicitly trusted via a parent domain's DNS record, or vice versa.</t>
    </section>

    <section title="Deployment Instance Binding (Optional Extension)" anchor="instance-binding">
      <t>A SOVP document as defined above names a host and a key. It does not state which deployment instance the claim refers to. This section defines an OPTIONAL top-level member, instance, that carries a signed label for one deployment instance, and the additional steps of <xref target="verification-order"/> that a verifier claiming instance binding conformance applies. Documents without an instance member are verified as specified in the preceding sections; a verifier claiming instance binding conformance returns instance_missing for them.</t>
      <t>The label is an assertion by the key holder. It does not prove that the process serving the document is that instance, or that the instance exists or is still running. It is meaningful where a host serves a single instance, or where the verifier selects the host to retrieve from using its own record of the instance.</t>
      <t>The extension does not change the schema major version. instance is not one of the three keys excluded from the signed scope (see <xref target="tech-spec"/>), so it is covered by the Ed25519 signature without any change to the exclusion rule. A verifier that does not implement this section still canonicalizes and verifies such a document correctly, but derives no instance label from it.</t>

      <section title="The instance Object" anchor="instance-object">
        <sourcecode type="json"><![CDATA[
"instance": {
  "id": "0192f5c4-7e1a-7c3b-9d2e-5a4b6c7d8e9f",
  "descriptor_digest": {
    "alg": "sha256",
    "digest": "<lowercase hex SHA-256 digest>"
  }
}
        ]]></sourcecode>
        <ul>
          <li>id (REQUIRED): the identifier of the deployment instance, exactly as assigned by the deployment description in use. Verifiers MUST compare it as an opaque string and MUST NOT parse it or infer structure from it. Where the deployment is governed by an Agent Manifest <xref target="AGENT-MANIFEST"/> that declares agent_instance_id, instance.id SHOULD equal that value. Where an ARD catalog entry <xref target="ARD"/> names the instance, instance.id SHOULD equal that name; a proposal to add an instances array with an instanceId member to ARD is under discussion <xref target="ARD-INSTANCES"/>, and this document makes no assumption about its outcome.</li>
          <li>descriptor_digest (OPTIONAL): {alg, digest} over the deployment descriptor the issuer evaluated. If the descriptor is a JSON value, the digest is computed over its JCS canonicalization; otherwise it is computed over the exact octets of the descriptor as retrieved, unless the descriptor format defines otherwise. This document defines no descriptor format.</li>
        </ul>
        <t>The verifier obtains the expected instance identifier, and the descriptor if it holds one, from its own context, for example from the manifest or deployment record it is evaluating. It MUST NOT take an expected value from the document under test. The outcome is instance_mismatch if instance.id differs from the expected identifier, or if the verifier holds a descriptor and the document either lacks descriptor_digest or carries a digest that differs from the one computed over that descriptor.</t>
      </section>

      <section title="Anchor Authorization" anchor="anchor-authorization">
        <t>A successful Psi_core establishes that the signature was produced by whoever controls the private key matching a K_pub published in DNS for the retrieval host (see <xref target="non-repudiation"/>). That DNS name is normally controlled by the party that operates the deployment under evaluation. Control of a key is therefore not authorization of that key.</t>
        <t>A verifier claiming instance binding conformance MUST resolve the verifying key through an authorization path, and MUST confirm that the path authorizes that key for the pair (retrieval host, entity.uid). The path MUST be external to the SOVP document and MUST NOT be derived from any identifier the attested subject controls, including the DNS record that publishes the key. Examples are a verifier-configured list of trusted issuers, a key pinned by the verifier's operator, and an issuer registry maintained by a party other than the subject. The mechanism is deployment policy, and this document defines no registry. This follows the principle stated in Section 5.3.3 of <xref target="AGENT-MANIFEST"/>: a key identifier identifies a key and does not authorize one.</t>
        <t>Issuer and subject MAY be the same organization. A self-attesting subject reaches accepted only if the verifier's operator has pinned its key through a path of the kind above; otherwise the outcome is authorization_undetermined or anchor_untrusted.</t>
      </section>

      <section title="Layer Boundaries" anchor="layer-boundaries">
        <t>A SOVP identity document, with or without the instance member, is a signed claim about who controls a key for a host and, with this extension, which deployment instance the claim refers to. Its signed scope contains no measurement of the execution environment. Scanner output carried in the scan member lies outside the signed scope (see <xref target="tech-spec"/>) and receives no assurance from Psi_core. Assessment results such as verdicts or scores require a separate signed document, issued under a key that the verifier resolves independently (see <xref target="anchor-authorization"/>); the format of such a document is out of scope here.</t>
        <t>A SOVP document makes no claim about permitted actions, runtime behavior, or the content of an Agent Manifest. A consumer that composes SOVP documents with Agent Manifest or TRACE records MUST treat each as a separate claim, MUST join them only through the declared instance identifier, and MUST NOT infer the validity of one layer from the validity of another.</t>
      </section>
    </section>

    <section title="Security Considerations" anchor="security-considerations">
      <t>This section follows the guidelines of <xref target="RFC3552"/>.</t>
      <section title="Threat Model" anchor="threat-model">
        <t>SOVP considers an attacker who can observe and modify network traffic to a verifier, who can publish content on hosts and DNS zones the attacker controls, and who can copy any public SOVP document. The protocol detects modification of a document after signing, replacement of a document by a party that holds no key published for the retrieval host, and presentation of a document under a host other than the one it names. It does not protect against compromise of the signing key, of the zone that publishes the key, or of the host that serves the document.</t>
        <t>Compared with relying on the TLS session alone, a signature checked against a DNS-published key can be re-verified from a stored copy and does not depend on the TLS terminator or a content delivery network. Related designs are DKIM <xref target="RFC6376"/>, which publishes signing keys in TXT records under an underscored label, DANE <xref target="RFC6698"/>, which binds keys to TLS endpoints through DNS, and HTTP Message Signatures <xref target="RFC9421"/>, which sign individual messages. SOVP signs a standing document and not a message.</t>
      </section>

      <section title="Requester Is Not Authenticated" anchor="requester-not-authenticated">
        <t>A SOVP document is public. A party that copies it can present it, and verification of that document says nothing about who sent a request. A Sovereign Gateway therefore authenticates a host's document and not the sender. Deployments that need to tie a request to a host need another mechanism, for example HTTP Message Signatures <xref target="RFC9421"/> made with a key that is bound to the host. This document defines no such binding.</t>
      </section>

      <section title="Key Revocation and DNS TTL" anchor="key-revocation">
        <t>To minimize the window of vulnerability during a key compromise, SOVP records in DNS SHOULD use a low Time-To-Live (TTL), with a recommended value of 300 seconds. Revocation is achieved by updating or removing the _sovp TXT record. Because a signature is accepted if it verifies under any key in the published set (see <xref target="dns-txt-format"/>), a compromised key remains acceptable to a verifier until its record has expired from that verifier's resolver caches, and a rotation is complete only after the record of the retired key has been removed. A verifier that caches documents or keys extends this window by the cache lifetime and SHOULD NOT cache beyond the DNS TTL of the key record.</t>
      </section>
      
      <section title="Replay Protection" anchor="replay-protection">
        <t>Freshness uses the freshness object (created, nonce, expiresAt), which is part of the Ed25519-signed scope (see <xref target="deployment-signing-correctness"/>). Because freshness is covered by the signature, none of its fields can be altered, including being pushed further into the future, without invalidating Psi_core. A verifier applies the following checks, in order, to every document whose signature has already passed Psi_core verification:</t>
        <ol>
          <li>Both freshness.created and freshness.expiresAt MUST be present and MUST be formatted as UTC timestamps as specified for the freshness member in <xref target="tech-spec"/> (for example 2026-06-01T12:00:00Z). A document failing this check MUST be rejected.</li>
          <li>freshness.expiresAt MUST be strictly later than freshness.created. A document where expiresAt is equal to or earlier than created MUST be rejected.</li>
          <li>Issuance window: let now be the verifier's current time. A verifier SHOULD reject a document unless now - W &lt;= created &lt;= now + N, where W is the maximum accepted age and N is the allowance for clock skew between signer and verifier. The default value of W is 600 seconds; a verifier MAY use a longer W by local policy. N = 60 seconds is RECOMMENDED. A verifier claiming instance binding conformance MUST apply this check.</li>
          <li>Expiry: if now &gt; freshness.expiresAt, the document MUST be rejected as expired. This check has no numeric bound of its own: expiresAt is chosen by the signer and bounds overall document validity, analogous to the notAfter field of a certificate.</li>
        </ol>
        <t>The issuance window and the expiry check serve different purposes. The window bounds how long ago the document was issued and is evaluated against created only. A signer that serves a pre-signed document therefore needs to re-sign it at an interval shorter than W for the document to pass a verifier that uses the default window. This keeps the signing key on a system that is online at that interval, and operators should weigh that exposure; a longer W by local policy trades freshness for key isolation. The expiry check bounds how long a document remains valid at all.</t>
        <t>Replay of a document within W is not prevented: a verifier has no challenge to bind the document to a request, and a copy of a fresh document is as fresh as the original. nonce is intended to support deduplication of high-frequency requests, but a verifier MUST NOT treat the presence of a nonce alone as a deduplication guarantee, because replay detection requires the verifier to remember nonces it has seen, which this document does not specify.</t>
        <t>For documents whose @context declares a schema version earlier than 2.0, the equivalent created and expiresAt fields were carried in integrity_proof, which lies outside the signed scope; any change to them is undetectable by Psi_core. A verifier that supports such documents MAY apply the checks above as a best-effort heuristic and MUST NOT treat the result as cryptographically guaranteed.</t>
      </section>
      
      <section title="Attribution" anchor="non-repudiation">
        <t>SOVP attributes the signed metadata to the party that holds the private key matching a K_pub published for the retrieval host when the verifier resolves it. This is attribution to the current key holder and is not non-repudiation in a legal or historical sense: DNS records can be changed or removed, and a verifier that did not record the key at the time cannot later show who held it. Attribution does not establish the signer's legal or real-world identity (see <xref target="non-goals"/>): SOVP makes no claim about who controls a domain, only that the party that published K_pub produced the signature. SOVP itself defines no reputation system and makes no claim about whether, or by what mechanism, a domain's signing history could be used by a third party to identify or block that domain in the future.</t>
      </section>
      
      <section title="DNS Security (DNSSEC)" anchor="dnssec">
        <t>Because the trust anchor is a DNS TXT record, protection against DNS spoofing is critical. DNSSEC <xref target="RFC4033"/> is RECOMMENDED for the zone that hosts the _sovp record. A verifier SHOULD resolve the record through a validating resolver and SHOULD treat a DNSSEC validation failure as a resolution failure. Without DNSSEC, the integrity of the resolved key depends entirely on the DNS resolution path (cache integrity of intermediate resolvers, absence of on-path spoofing, and the transport used to reach the resolver), a path that SOVP does not protect and cannot monitor. Operators who need a trust anchor stronger than that resolution path need DNSSEC; SOVP offers no alternative mechanism.</t>
      </section>

      <section title="Public Key Source Authority" anchor="key-source-authority">
        <t>A verifier MUST NOT determine K_pub from any source other than the DNS TXT record for the retrieval host. In particular it MUST NOT use an HTTP header field or any value in the document, including integrity_proof.public_key_ref and entity.canonical_url, for that purpose: a verifier that did so would learn only that the document is consistent with itself.</t>
      </section>

      <section title="Resource Limits for Unverified Documents" anchor="resource-limits">
        <t>A verifier parses the document before Psi_core is known, so the document is attacker-controlled input at that point. Before canonicalization, a verifier MUST reject a document, with the outcome document_invalid, that is larger than 65536 octets, that contains JSON values nested deeper than 16 levels (the top-level object is at level 1), or that contains a duplicate member name in any object. The size limit applies to the octets of the document after any HTTP content decoding. Duplicate member names are rejected because JSON parsers differ in which value they keep (see <xref target="RFC8259"/>), so a document could be read differently by a verifier and by a consumer of the same bytes. A verifier MUST NOT canonicalize or otherwise process a document before these checks have passed.</t>
      </section>

      <section title="Retrieval Considerations" anchor="retrieval-considerations">
        <t>Retrieving the document and resolving the key consume a verifier's resources on behalf of whoever caused the request. A verifier SHOULD bound the time spent on each retrieval and each DNS query, and SHOULD limit the rate of retrievals per source. A verifier that derives the host to retrieve from untrusted input MUST NOT retrieve from addresses that are not meant to be publicly reachable (for example loopback, link-local, and private ranges), and MUST check the address after name resolution, to avoid server-side request forgery and DNS rebinding.</t>
        <t>A verifier SHOULD NOT follow HTTP redirects when retrieving the document. A verifier that follows them MUST still derive the DNS name from the host of the original request URL (see <xref target="dns-txt-format"/>) and MUST NOT follow a redirect to a URL that does not use the https scheme.</t>
      </section>

      <section title="Privacy Considerations" anchor="privacy">
        <t>Each retrieval and each DNS query tells the publisher of the document and the operators of the resolvers that a verifier is interested in a host. A gateway that verifies on behalf of many requesters can reveal request patterns this way. This document defines no mitigation.</t>
      </section>

      <section title="Self-Published Trust Anchor and Instance Labels" anchor="self-published-anchor">
        <t>The DNS TXT record that anchors K_pub lies under the control of the entity that serves the document. Without the external authorization step in <xref target="anchor-authorization"/>, a verifier learns that the document and the key come from one party and learns nothing about whether that party may attest for the instance. An attacker who controls a host can sign any instance identifier for it. Verifiers MUST therefore take the expected instance identifier from their own context, as required in <xref target="instance-object"/>, and MUST NOT treat a matching instance.id alone as evidence of authenticity.</t>
        <t>Because instance is inside the signed scope, a party that does not hold the signing key cannot retarget a signed document to a different instance without invalidating Psi_core. This protects against retargeting after the fact and does not protect against an issuer that signs a false claim.</t>
      </section>
    </section>

    <section title="Implementation Status" anchor="implementation-status">
      <t>The information in this section reflects the status of known implementations as of the writing of this document per the requirements in <xref target="RFC7942"/>. The description of implementations in this section is intended to assist the IETF in its decision processes and is not intended to (and MUST NOT be construed to) favor or disfavor any particular implementation. This section will be removed before publication as an RFC.</t>
      <t>The reference implementations named in this section (sovp-python, sovp-engine, and the Cloudflare Worker deployment) are developed and operated by Litzki Systems LLC, the organization publishing this document; they are not independent third-party implementations. The following capabilities are implemented in the reference implementations (sovp-python's sovp/core.py, sovp-engine's lib/sovereign-identity.mjs) as of schema v2.0:</t>
      <ul>
        <li>entity.owner: an optional, non-normative display-name field for the publishing organization, included in the signed scope as part of entity. Despite its name, entity.owner does not assert legal ownership of, or verified control over, the named organization; it is a self-declared display name chosen by whoever controls the private key at signing time, with no independent verification of the underlying legal relationship (see <xref target="non-repudiation"/>).</li>
        <li>freshness object generation: created, nonce, and expiresAt are generated at signing time and included in the signed scope (see <xref target="deployment-signing-correctness"/>).</li>
        <li>Legacy fallback for v1.4 documents: verifiers read freshness.created first and fall back to the unsigned integrity_proof.created only for documents whose @context predates v2.0; this fallback affects timestamp freshness checking only, never signature verification.</li>
        <li>contentAddress generation ({alg, digest}, a SHA-256 digest over the signed byte range): implemented in all three reference implementations (sovp-python's sovp/core.py, sovp-engine's lib/sovereign-identity.mjs, and the Cloudflare Worker) and in production use on both live domains (litzki-systems.com, certavia.org).</li>
      </ul>
      <t>Test coverage: sovp-engine's test/verifyIdentityDocument.test.js covers the production verifyIdentityDocument() path with 12 tests: genuine v2.0 acceptance, per-field tamper rejection (freshness.expiresAt, freshness.created, entity.owner, entity.canonical_url, freshness.nonce, wrong public key), downgrade resistance (freshness stripped, with and without a forged v1.4 @context), upgrade resistance (a genuine v1.4 document with @context and a fabricated freshness object forged to v2.0), and legacy v1.4 fallback correctness (including the known-and-accepted legacy expiresAt-forgery gap). test/canonicalizationDrift.test.js additionally verifies byte-for-byte JCS (<xref target="RFC8785"/>) canonicalization agreement across all three separate implementations maintained by Litzki Systems LLC (sovp-engine's npm canonicalize, sovp-python's jcs package, and the Cloudflare Worker's hand-rolled jcs()) for the real SovereignIdentity document shape, including non-ASCII field values and unsorted source key order.</t>
      <t>The reference implementation sovp-python is published at https://github.com/litzki-systems/sovp-python under the Apache License 2.0; the other implementations are maintained by the same organization. Contact: ietf@litzki-systems.com. sovp-python implements the resource limits of <xref target="resource-limits"/> (sovp/document_safety.py) and the key set of <xref target="dns-txt-format"/> (up to 4 keys, any of which may verify).</t>
      <t>Known limitations: nonce-reuse detection (deduplication of freshness.nonce across requests) is not yet implemented in either reference implementation (see <xref target="replay-protection"/>). In sovp-python, the issuance window is applied only when the caller requests it (check_timestamp), verify_identity does not check expiresAt, and generate_identity_document emits expiresAt only when the caller supplies it, whereas this document requires expiresAt to be present and checked. The host binding of <xref target="verification-order"/> (step 3) is not implemented in sovp-python.</t>
      <t>Deployment instance binding (<xref target="instance-binding"/>), including the authorization and outcome steps of <xref target="verification-order"/> that are marked (B), is specified in this revision and is not yet implemented in any reference implementation. <xref target="test-vectors"/> gives test vectors for each outcome. The vector in A.1 is taken from the sovp-python test suite. The vectors in A.2 and A.3 were generated for this revision with the same published test key and checked with a separate script that follows the verification order of this document, not with a reference implementation. Until a reference implementation passes them, no implementation claims instance binding conformance.</t>
    </section>

    

    <section title="IANA Considerations" anchor="iana-considerations">
      <section title="Underscored and Globally Scoped DNS Node Names" anchor="dns-node-names">
        <t>Per <xref target="RFC8552"/>, IANA is requested to add the following entry to the <xref target="dns-node-names"/> registry:</t>
        <table>
          <thead><tr><th>RR Type</th><th>_NODE NAME</th><th>Reference</th></tr></thead>
          <tbody><tr><td>TXT</td><td>_sovp</td><td>This document</td></tr></tbody>
        </table>
      </section>

      <section title="Well-Known URI: sovp-identity.json" anchor="well-known-uri">
        <t>Per <xref target="RFC8615"/>, the following entry is to be added to the "Well-Known URIs" registry:</t>
        <ul>
          <li>URI suffix: sovp-identity.json</li>
          <li>Change controller: IETF</li>
          <li>Specification document(s): This document (<xref target="tech-spec"/>)</li>
          <li>Status: provisional</li>
          <li>Related information: none</li>
        </ul>
      </section>

    </section>
  </middle>

  <back>
    <references title="Normative References">
      <reference anchor="RFC3339" target="https://www.rfc-editor.org/info/rfc3339"><front><title>Date and Time on the Internet: Timestamps</title><author initials="G." surname="Klyne"><organization/></author><author initials="C." surname="Newman"><organization/></author><date year="2002" month="July"/></front><seriesInfo name="RFC" value="3339"/></reference>
      <reference anchor="RFC5890" target="https://www.rfc-editor.org/info/rfc5890"><front><title>Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework</title><author initials="J." surname="Klensin"><organization/></author><date year="2010" month="August"/></front><seriesInfo name="RFC" value="5890"/></reference>
      <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119"><front><title>Key words for use in RFCs to Indicate Requirement Levels</title><author initials="S." surname="Bradner"><organization/></author><date year="1997" month="March"/></front><seriesInfo name="BCP" value="14"/><seriesInfo name="RFC" value="2119"/></reference>
      <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174"><front><title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title><author initials="B." surname="Leiba"><organization/></author><date year="2017" month="May"/></front><seriesInfo name="BCP" value="14"/><seriesInfo name="RFC" value="8174"/></reference>
      <reference anchor="RFC1035" target="https://www.rfc-editor.org/info/rfc1035"><front><title>Domain names - implementation and specification</title><author initials="P." surname="Mockapetris"><organization/></author><date year="1987" month="November"/></front><seriesInfo name="RFC" value="1035"/></reference>
      <reference anchor="RFC4648" target="https://www.rfc-editor.org/info/rfc4648"><front><title>The Base16, Base32, and Base64 Data Encodings</title><author initials="S." surname="Josefsson"><organization/></author><date year="2006" month="October"/></front><seriesInfo name="RFC" value="4648"/></reference>
      <reference anchor="RFC8032" target="https://www.rfc-editor.org/info/rfc8032"><front><title>Edwards-Curve Digital Signature Algorithm (EdDSA)</title><author initials="S." surname="Josefsson"><organization/></author><author initials="I." surname="Liusvaara"><organization/></author><date year="2017" month="January"/></front><seriesInfo name="RFC" value="8032"/></reference>
      <reference anchor="RFC8259" target="https://www.rfc-editor.org/info/rfc8259"><front><title>The JavaScript Object Notation (JSON) Data Interchange Format</title><author initials="T." surname="Bray" role="editor"><organization/></author><date year="2017" month="December"/></front><seriesInfo name="STD" value="90"/><seriesInfo name="RFC" value="8259"/></reference>
      <reference anchor="RFC8552" target="https://www.rfc-editor.org/info/rfc8552"><front><title>Scoped Interpretation of DNS Resource Records through "Underscored" Naming of Attribute Leaves</title><author initials="D." surname="Crocker"><organization/></author><date year="2019" month="March"/></front><seriesInfo name="BCP" value="222"/><seriesInfo name="RFC" value="8552"/></reference>
      <reference anchor="RFC8615" target="https://www.rfc-editor.org/info/rfc8615"><front><title>Well-Known Uniform Resource Identifiers (URIs)</title><author initials="M." surname="Nottingham"><organization/></author><date year="2019" month="May"/></front><seriesInfo name="RFC" value="8615"/></reference>
      <reference anchor="RFC8785" target="https://www.rfc-editor.org/info/rfc8785"><front><title>JSON Canonicalization Scheme (JCS)</title><author initials="A." surname="Rundgren"><organization/></author><author initials="B." surname="Jordan"><organization/></author><author initials="S." surname="Erdtman"><organization/></author><date year="2020" month="June"/></front><seriesInfo name="RFC" value="8785"/></reference>
      <reference anchor="RFC9110" target="https://www.rfc-editor.org/info/rfc9110"><front><title>HTTP Semantics</title><author initials="R." surname="Fielding" role="editor"><organization/></author><author initials="M." surname="Nottingham" role="editor"><organization/></author><author initials="J." surname="Reschke" role="editor"><organization/></author><date year="2022" month="June"/></front><seriesInfo name="RFC" value="9110"/></reference>
    </references>
    <references title="Informative References">
      <reference anchor="RFC6376" target="https://www.rfc-editor.org/info/rfc6376"><front><title>DomainKeys Identified Mail (DKIM) Signatures</title><author initials="D." surname="Crocker" role="editor"><organization/></author><author initials="T." surname="Hansen" role="editor"><organization/></author><author initials="M." surname="Kucherawy" role="editor"><organization/></author><date year="2011" month="September"/></front><seriesInfo name="STD" value="76"/><seriesInfo name="RFC" value="6376"/></reference>
      <reference anchor="RFC6698" target="https://www.rfc-editor.org/info/rfc6698"><front><title>The DNS-Based Authentication of Named Entities (DANE) Transport Layer Security (TLS) Protocol: TLSA</title><author initials="P." surname="Hoffman"><organization/></author><author initials="J." surname="Schlyter"><organization/></author><date year="2012" month="August"/></front><seriesInfo name="RFC" value="6698"/></reference>
      <reference anchor="RFC9421" target="https://www.rfc-editor.org/info/rfc9421"><front><title>HTTP Message Signatures</title><author initials="A." surname="Backman" role="editor"><organization/></author><author initials="J." surname="Richer" role="editor"><organization/></author><author initials="M." surname="Sporny"><organization/></author><date year="2024" month="February"/></front><seriesInfo name="RFC" value="9421"/></reference>
      <reference anchor="RFC3552" target="https://www.rfc-editor.org/info/rfc3552"><front><title>Guidelines for Writing RFC Text on Security Considerations</title><author initials="E." surname="Rescorla"><organization/></author><author initials="B." surname="Korver"><organization/></author><date year="2003" month="July"/></front><seriesInfo name="BCP" value="72"/><seriesInfo name="RFC" value="3552"/></reference>
      <reference anchor="RFC4033" target="https://www.rfc-editor.org/info/rfc4033"><front><title>DNS Security Introduction and Requirements</title><author initials="R." surname="Arends"><organization/></author><author initials="R." surname="Austein"><organization/></author><author initials="M." surname="Larson"><organization/></author><author initials="D." surname="Massey"><organization/></author><author initials="S." surname="Rose"><organization/></author><date year="2005" month="March"/></front><seriesInfo name="RFC" value="4033"/></reference>
      <reference anchor="RFC7942" target="https://www.rfc-editor.org/info/rfc7942"><front><title>Improving Awareness of Running Code: The Implementation Status Section</title><author initials="Y." surname="Sheffer"><organization/></author><author initials="A." surname="Farrel"><organization/></author><date year="2016" month="July"/></front><seriesInfo name="BCP" value="205"/><seriesInfo name="RFC" value="7942"/></reference>
      <reference anchor="RFC8792" target="https://www.rfc-editor.org/info/rfc8792"><front><title>Handling Long Lines in Content of Internet-Drafts and RFCs</title><author initials="K." surname="Watsen"><organization/></author><author initials="E." surname="Auerswald"><organization/></author><author initials="A." surname="Farrel"><organization/></author><author initials="Q." surname="Wu"><organization/></author><date year="2020" month="June"/></front><seriesInfo name="RFC" value="8792"/></reference>
      <reference anchor="RFC9457" target="https://www.rfc-editor.org/info/rfc9457"><front><title>Problem Details for HTTP APIs</title><author initials="M." surname="Nottingham"><organization/></author><author initials="E." surname="Wilde"><organization/></author><author initials="S." surname="Dalal"><organization/></author><date year="2023" month="July"/></front><seriesInfo name="RFC" value="9457"/></reference>
      <reference anchor="AGENT-MANIFEST" target="https://github.com/agentrust-io/agent-manifest/blob/2f1b9f53551b15fdad21e25f3ee1e698d2920fd8/spec/agent-manifest-spec-v0.2.md">
        <front>
          <title>Agent Manifest Specification, v0.2 (repository commit 2f1b9f5)</title>
          <author><organization>agentrust-io</organization></author>
          <date year="2026"/>
        </front>
      </reference>
      <reference anchor="ARD" target="https://github.com/ards-project/ard-spec/blob/main/spec/ard.md">
        <front>
          <title>Agentic Resource Discovery (ARD) Specification, v0.91</title>
          <author><organization>ards-project</organization></author>
          <date year="2026"/>
        </front>
      </reference>
      <reference anchor="ARD-INSTANCES" target="https://github.com/ards-project/ard-spec/issues/44">
        <front>
          <title>Spec Proposal: Deployment metadata (ard-spec issue 44)</title>
          <author><organization>ards-project</organization></author>
          <date year="2026"/>
        </front>
      </reference>
    </references>

  <section title="Test Vectors" anchor="test-vectors">
    <t>All values in this appendix use the test key below and the host example.org. The test private key is published for testing only and MUST NOT be used for any other purpose. Ed25519 signatures in pure mode are deterministic, so an implementation can reproduce every signature from the seed. Long lines are folded as described in <xref target="RFC8792"/> (a single backslash at the end of a line, with the continuation at the start of the next line).</t>
    <artwork><![CDATA[
Test private key (32-octet seed, standard Base64):
/Tp1tRJ/wlUUHYDsO7C1rR0XAsAg4hHmLC0xQKkdmHQ=

Public key as published in DNS at _sovp.example.org:
v=SOVP1; k=Roc8V4AnfbRjW35aagwwuHJhc7hPQZl7ooncY+HdM+s=
    ]]></artwork>
    <section title="Base Vector (Schema v2.0)" anchor="vector-base">
      <t>Document A.1:</t>
      <artwork><![CDATA[
NOTE: '\' line wrapping per RFC 8792

{
  "@context": "https://litzki-systems.com/protocol/v2.0",
  "@type": "SovereignIdentity",
  "entity": {
    "uid": "urn:sovp:test-entity-2",
    "canonical_url": "https://example.org",
    "verification_method": "Ed25519"
  },
  "freshness": {
    "created": "2026-06-01T12:00:00Z",
    "nonce": "fixed-test-nonce-v2-0001",
    "expiresAt": "2026-12-01T00:00:00Z"
  },
  "contentAddress": {
    "alg": "sha256",
    "digest": "d00c6db5124803dd67495b0551dc0249994fd6f818509724202\
6e28a4d30b460"
  },
  "integrity_proof": {
    "signature": "Th2wr122ikRicemrC5bITXHri5glqNqfkPosCjLBjXLIdO2U\
StZm5j7SmJ0j1xuEYAPOc9WtJW4blsceuCVCBg==",
    "public_key_ref": "dns:txt:_sovp.example.org"
  }
}
      ]]></artwork>
      <t>JCS(M), the canonicalized signed scope (312 octets), whose SHA-256 digest equals contentAddress.digest above:</t>
      <artwork><![CDATA[
NOTE: '\' line wrapping per RFC 8792

{"@context":"https://litzki-systems.com/protocol/v2.0","@type":"\
SovereignIdentity","entity":{"canonical_url":"https://example.or\
g","uid":"urn:sovp:test-entity-2","verification_method":"Ed25519\
"},"freshness":{"created":"2026-06-01T12:00:00Z","expiresAt":"20\
26-12-01T00:00:00Z","nonce":"fixed-test-nonce-v2-0001"}}
      ]]></artwork>
    </section>
    <section title="Instance-Bound Vector" anchor="vector-instance">
      <t>The same key and entity with an instance member. The descriptor is the following JSON text, which is already in JCS form, and descriptor_digest.digest is the SHA-256 digest of its UTF-8 octets:</t>
      <artwork><![CDATA[
NOTE: '\' line wrapping per RFC 8792

{"instanceId":"0192f5c4-7e1a-7c3b-9d2e-5a4b6c7d8e9f","url":"http\
s://example.org/agent"}
      ]]></artwork>
      <t>Document A.2:</t>
      <artwork><![CDATA[
NOTE: '\' line wrapping per RFC 8792

{
  "@context": "https://litzki-systems.com/protocol/v2.0",
  "@type": "SovereignIdentity",
  "entity": {
    "uid": "urn:sovp:test-entity-2",
    "canonical_url": "https://example.org",
    "verification_method": "Ed25519"
  },
  "freshness": {
    "created": "2026-06-01T12:00:00Z",
    "nonce": "fixed-test-nonce-v2-0001",
    "expiresAt": "2026-12-01T00:00:00Z"
  },
  "instance": {
    "id": "0192f5c4-7e1a-7c3b-9d2e-5a4b6c7d8e9f",
    "descriptor_digest": {
      "alg": "sha256",
      "digest": "d9714591fa2a1ef5b07331bd3437d4f5a79f9321b26009293\
6decf8fbab3848d"
    }
  },
  "contentAddress": {
    "alg": "sha256",
    "digest": "7ddd289e0e3dd81df253a9fdbeb18760aa94ae87286c6eca676\
eeb11a7619140"
  },
  "integrity_proof": {
    "signature": "+ey/Rr3G/Qeu+VSsyA8fPI66qSy+V0T6YBxJsoxtcEK77YoH\
wH2cLd6mZnNROnCYMHgoTpakAIBDoC9mbFY/Dg==",
    "public_key_ref": "dns:txt:_sovp.example.org"
  }
}
      ]]></artwork>
      <t>JCS(M) (482 octets):</t>
      <artwork><![CDATA[
NOTE: '\' line wrapping per RFC 8792

{"@context":"https://litzki-systems.com/protocol/v2.0","@type":"\
SovereignIdentity","entity":{"canonical_url":"https://example.or\
g","uid":"urn:sovp:test-entity-2","verification_method":"Ed25519\
"},"freshness":{"created":"2026-06-01T12:00:00Z","expiresAt":"20\
26-12-01T00:00:00Z","nonce":"fixed-test-nonce-v2-0001"},"instanc\
e":{"descriptor_digest":{"alg":"sha256","digest":"d9714591fa2a1e\
f5b07331bd3437d4f5a79f9321b260092936decf8fbab3848d"},"id":"0192f\
5c4-7e1a-7c3b-9d2e-5a4b6c7d8e9f"}}
      ]]></artwork>
    </section>
    <section title="Outcome Vectors" anchor="vector-outcomes">
      <t>Unless a row states otherwise, the verifier context is: retrieval host = example.org; DNS publishes the key of A.1 (K) only; the authorization path authorizes K for (example.org, urn:sovp:test-entity-2); now = 2026-06-01T12:05:00Z; expected instance identifier = 0192f5c4-7e1a-7c3b-9d2e-5a4b6c7d8e9f; expected descriptor digest = d9714591fa2a1ef5b07331bd3437d4f5a79f9321b260092936decf8fbab3848d; the issuance window W is 600 seconds with N = 60 seconds. K' denotes the key RYVkYPO0CymHmHNYfjw3a9baEyWH40fnEzrgY/rMA5M=, which does not correspond to the test key. The verifier claims instance binding conformance.</t>
      <table>
        <thead><tr><th>ID</th><th>Input</th><th>Expected outcome</th></tr></thead>
        <tbody>
          <tr><td>V1</td><td>A.2 document</td><td>accepted</td></tr>
          <tr><td>V2</td><td>No document obtained</td><td>evidence_missing</td></tr>
          <tr><td>V3</td><td>A.1 document (no instance member)</td><td>instance_missing</td></tr>
          <tr><td>V4</td><td>A.2 document; authorized set is {K'}</td><td>anchor_untrusted</td></tr>
          <tr><td>V5</td><td>A.2 document; no authorization path configured</td><td>authorization_undetermined</td></tr>
          <tr><td>V6</td><td>A.2 document with the last character of instance.id changed to 0, signature unchanged</td><td>invalid_signature</td></tr>
          <tr><td>V7</td><td>A.2 document; now = 2026-06-01T13:00:00Z</td><td>stale</td></tr>
          <tr><td>V8</td><td>A.2 document; now = 2026-06-01T11:58:00Z (created lies more than N seconds in the future)</td><td>stale</td></tr>
          <tr><td>V9</td><td>A.2 document; expected instance identifier = 0192f5c4-7e1a-7c3b-9d2e-ffffffffffff</td><td>instance_mismatch</td></tr>
          <tr><td>V10</td><td>A.2 document; expected descriptor digest is the SHA-256 digest of the ASCII string "other descriptor"</td><td>instance_mismatch</td></tr>
          <tr><td>V11</td><td>A.2 document retrieved from host attacker.example, whose DNS publishes K and whose authorized set for (attacker.example, urn:sovp:test-entity-2) is {K}</td><td>host_mismatch</td></tr>
          <tr><td>V12</td><td>A.2 document; DNS publishes {K', K}; authorized set is {K'}</td><td>anchor_untrusted</td></tr>
          <tr><td>V13</td><td>A.2 document; DNS publishes {K', K}; authorized set is {K}</td><td>accepted</td></tr>
          <tr><td>V14</td><td>A.2 document; DNS publishes no valid v=SOVP1 record</td><td>key_unresolved</td></tr>
          <tr><td>V15</td><td>A.2 document text in which the member nonce appears twice in freshness</td><td>document_invalid</td></tr>
          <tr><td>V16</td><td>A.2 document text padded with spaces to 65537 octets</td><td>document_invalid</td></tr>
        </tbody>
      </table>
    </section>
  </section>
</back>
</rfc>