Internet-Draft SOVP October 2026
Litzki Expires 10 April 2027 [Page]
Workgroup:
Network Working Group
Published:
Intended Status:
Experimental
Expires:
Author:
T. Litzki
Litzki Systems LLC

Sovereign Validation Protocol (SOVP)

Abstract

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.

Status of This Memo

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

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

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

This Internet-Draft will expire on 10 April 2027.

▲

Table of Contents

1. Introduction

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.

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 Section 3).

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 Section 8. The extension is a candidate for removal or revision if that experience shows it is not useful.

2. Conventions and Definitions

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

Layer 0:
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.
Entity:
The party that publishes a SOVP document for a host.
Validating Agent:
A client that verifies a SOVP document locally before committing data to its memory (Mode A).
Sovereign Gateway:
An infrastructure-level gateway that verifies requests on behalf of a protected cluster (Mode B).
Verifier:
A Validating Agent or a Sovereign Gateway.

3. Non-Goals

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 Section 9.5 in Section 9 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.

4. Signature Verification Function: Psi_core

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 Section 5, MUST be processed using the JSON Canonicalization Scheme (JCS) as defined in [RFC8785].

The validation follows the formal function of the Edwards-curve Digital Signature Algorithm, Ed25519, in its pure mode per [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.

Psi_core = Verify(K_pub, sigma, JCS(M))

Where:

5. Technical Specification: The sovp-identity.json Structure

The implementation relies on a signed JSON object retrieved over HTTPS (the "https" scheme of [RFC9110]) from a well-known location on the host, per [RFC8615]. The document is JSON [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 Section 6.1. This object serves as the primary data carrier for the sovereign identity.

Schema (illustrative values; a complete, verifiable document is given in Appendix A):

{
  "@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>"
  }
}
Table 1
Member Requirement Meaning
@context REQUIRED 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.
@type REQUIRED The string "SovereignIdentity".
entity.uid REQUIRED 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.
entity.canonical_url REQUIRED URL of the publishing entity. Its host MUST equal the host from which the document was retrieved (see Section 6.2). A verifier MUST NOT use it to select the DNS name to query.
entity.verification_method REQUIRED The string "Ed25519".
entity.owner OPTIONAL Self-declared display name (see Section 10).
freshness REQUIRED Object with created, expiresAt (both REQUIRED) and nonce (OPTIONAL), covered by the signature (see Section 9.4). Timestamps use the RFC 3339 date-time format [RFC3339] in UTC with a literal Z suffix and no fractional seconds.
integrity_proof.signature REQUIRED Ed25519 signature over JCS(M), encoded as standard Base64 (Section 4 of [RFC4648], with padding) of the 64-octet signature value.
integrity_proof.public_key_ref OPTIONAL Informational pointer to the DNS name of the key. A verifier MUST NOT use it to select the key.
contentAddress OPTIONAL See Section 5.1.
scan OPTIONAL Non-normative vendor extension, outside the signed scope.
instance OPTIONAL Deployment instance binding (see Section 8).

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:

  1. Start from the received (or, when signing, the about-to-be-served) top-level JSON object.
  2. 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.
  3. Canonicalize the remaining object per JCS ([RFC8785]). This canonicalized byte string is JCS(M), the signed scope.
  4. Verify Ed25519(K_pub, sigma, JCS(M)) per [RFC8032] pure mode, where sigma is integrity_proof.signature.

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 Section 5.1 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.

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.

5.1. Content Address Scope

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 Section 5.2 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.

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.

5.2. Deployment Signing Correctness

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.

6. Protocol Execution Sequence

SOVP defines two operational modes:

Table 2
Mode Actor Description
Mode A Validating Agent (Client) The agent verifies the document of a named host locally before committing data to memory.
Mode B Sovereign Gateway (Server) 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.

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.

Execution sequence:

  1. Public key exposure: The entity publishes its Ed25519 public key in a DNS TXT record at _sovp.<host>, the sole source of K_pub (see Section 7).
  2. Artifact retrieval: The verifier retrieves sovp-identity.json, per [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 Section 6.1.
  3. Verification: The verifier applies the ordered steps of Section 6.2.
  4. 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 Section 6.3 in every other case.

6.1. Retrieval and Fallback

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.

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:

  • Standard action: the request is rejected per Section 6.3.
  • Exception: Local allow-lists MAY be defined for legacy systems, though these bypass the Layer 0 integrity guarantee.

6.2. Verification Order and Outcomes

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 Section 8); 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 [RFC5890] without port or trailing dot. A verifier MUST NOT verify a document retrieved from a host given as an IP literal.

  1. Retrieval and input limits: if no document is obtained, the outcome is evidence_missing. If the document violates the limits of Section 9.8, 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 Section 9.4.
  2. (B) Authorization availability: if the verifier has no authorization path (see Section 8.2) for the retrieval host, the outcome is authorization_undetermined.
  3. Host binding: if the host of entity.canonical_url, normalized in the same way, differs from the retrieval host, the outcome is host_mismatch.
  4. Key resolution: the verifier resolves the key set as specified in Section 7. If resolution fails or yields no valid key, the outcome is key_unresolved.
  5. 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.
  6. (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.
  7. Freshness: the verifier applies the checks of Section 9.4. 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.
  8. (B) Instance member: if the document has no instance member, the outcome is instance_missing.
  9. (B) Binding: if the checks of Section 8.1 fail, the outcome is instance_mismatch.
  10. Otherwise the outcome is accepted for a verifier claiming instance binding conformance and verified for any other verifier.
Table 3
Outcome Meaning
verified Signed by the current holder of a key published for the retrieval host, names that host, and is fresh. This is attribution only.
accepted As verified, and in addition the key is authorized through an external path and the document is bound to the expected instance.
evidence_missing No document was obtained.
document_invalid Limits exceeded, not parseable, unsupported version, type or method, or malformed or inconsistent freshness members.
authorization_undetermined No authorization path is available or configured.
host_mismatch The document names a different host than the one it was retrieved from.
key_unresolved The key set could not be resolved or is empty.
invalid_signature Psi_core failed under every key of the set.
anchor_untrusted An authorization path exists and does not authorize the verifying key.
stale The issuance window or the expiry check failed.
instance_missing The document has no instance member.
instance_mismatch The document describes a different instance or descriptor than the one under evaluation.

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 Section 8.2). The fixed order makes each outcome deterministic for a given input, which allows independent implementations to be compared test case by test case (see Appendix A).

6.3. Rejection Status Codes

A Sovereign Gateway that rejects a request at Layer 0 MUST respond with status code 403 (Forbidden) Section 15.5.4 of [RFC9110]. This applies to every outcome other than verified and accepted. Status code 401 is not used: Section 15.5.2 of [RFC9110] 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 [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 [RFC9457] that distinguishes the outcomes; this document defines no problem types.

7. DNS TXT Record Format and Resolution

The public key is published as a TXT resource record at the node name _sovp.<domain> (see Section 11.1), where <domain> is defined below. The record value MUST be formatted as:

v=SOVP1; k=<base64>

k is the 32-byte raw Ed25519 public key, encoded using standard Base64 (not Base64url) as defined in Section 4 of [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.

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.

Multiple character-strings within a single TXT RR: [RFC1035] allows a single TXT RDATA to contain more than one <character-string> (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=<base64> 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.

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 Section 6.2). 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.

8. Deployment Instance Binding (Optional Extension)

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 Section 6.2 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.

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.

The extension does not change the schema major version. instance is not one of the three keys excluded from the signed scope (see Section 5), 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.

8.1. The instance Object

"instance": {
  "id": "0192f5c4-7e1a-7c3b-9d2e-5a4b6c7d8e9f",
  "descriptor_digest": {
    "alg": "sha256",
    "digest": "<lowercase hex SHA-256 digest>"
  }
}
  • 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 [AGENT-MANIFEST] that declares agent_instance_id, instance.id SHOULD equal that value. Where an ARD catalog entry [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 [ARD-INSTANCES], and this document makes no assumption about its outcome.
  • 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.

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.

8.2. Anchor Authorization

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 Section 9.5). 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.

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 [AGENT-MANIFEST]: a key identifier identifies a key and does not authorize one.

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.

8.3. Layer Boundaries

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 Section 5) 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 Section 8.2); the format of such a document is out of scope here.

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.

9. Security Considerations

This section follows the guidelines of [RFC3552].

9.1. Threat Model

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.

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 [RFC6376], which publishes signing keys in TXT records under an underscored label, DANE [RFC6698], which binds keys to TLS endpoints through DNS, and HTTP Message Signatures [RFC9421], which sign individual messages. SOVP signs a standing document and not a message.

9.2. Requester Is Not Authenticated

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 [RFC9421] made with a key that is bound to the host. This document defines no such binding.

9.3. Key Revocation and DNS TTL

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 Section 7), 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.

9.4. Replay Protection

Freshness uses the freshness object (created, nonce, expiresAt), which is part of the Ed25519-signed scope (see Section 5.2). 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:

  1. Both freshness.created and freshness.expiresAt MUST be present and MUST be formatted as UTC timestamps as specified for the freshness member in Section 5 (for example 2026-06-01T12:00:00Z). A document failing this check MUST be rejected.
  2. freshness.expiresAt MUST be strictly later than freshness.created. A document where expiresAt is equal to or earlier than created MUST be rejected.
  3. Issuance window: let now be the verifier's current time. A verifier SHOULD reject a document unless now - W <= created <= 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.
  4. Expiry: if now > 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.

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.

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.

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.

9.5. Attribution

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 Section 3): 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.

9.6. DNS Security (DNSSEC)

Because the trust anchor is a DNS TXT record, protection against DNS spoofing is critical. DNSSEC [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.

9.7. Public Key Source Authority

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.

9.8. Resource Limits for Unverified Documents

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 [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.

9.9. Retrieval Considerations

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.

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 Section 7) and MUST NOT follow a redirect to a URL that does not use the https scheme.

9.10. Privacy Considerations

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.

9.11. Self-Published Trust Anchor and Instance Labels

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 Section 8.2, 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 Section 8.1, and MUST NOT treat a matching instance.id alone as evidence of authenticity.

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.

10. Implementation Status

The information in this section reflects the status of known implementations as of the writing of this document per the requirements in [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.

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:

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 ([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.

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 Section 9.8 (sovp/document_safety.py) and the key set of Section 7 (up to 4 keys, any of which may verify).

Known limitations: nonce-reuse detection (deduplication of freshness.nonce across requests) is not yet implemented in either reference implementation (see Section 9.4). 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 Section 6.2 (step 3) is not implemented in sovp-python.

Deployment instance binding (Section 8), including the authorization and outcome steps of Section 6.2 that are marked (B), is specified in this revision and is not yet implemented in any reference implementation. Appendix A 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.

11. IANA Considerations

11.1. Underscored and Globally Scoped DNS Node Names

Per [RFC8552], IANA is requested to add the following entry to the Section 11.1 registry:

Table 4
RR Type _NODE NAME Reference
TXT _sovp This document

11.2. Well-Known URI: sovp-identity.json

Per [RFC8615], the following entry is to be added to the "Well-Known URIs" registry:

  • URI suffix: sovp-identity.json
  • Change controller: IETF
  • Specification document(s): This document (Section 5)
  • Status: provisional
  • Related information: none

12. Normative References

[RFC3339]
Klyne, G. and C. Newman, "Date and Time on the Internet: Timestamps", RFC 3339, , <https://www.rfc-editor.org/info/rfc3339>.
[RFC5890]
Klensin, J., "Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework", RFC 5890, , <https://www.rfc-editor.org/info/rfc5890>.
[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, , <https://www.rfc-editor.org/info/rfc2119>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, , <https://www.rfc-editor.org/info/rfc8174>.
[RFC1035]
Mockapetris, P., "Domain names - implementation and specification", RFC 1035, , <https://www.rfc-editor.org/info/rfc1035>.
[RFC4648]
Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, , <https://www.rfc-editor.org/info/rfc4648>.
[RFC8032]
Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital Signature Algorithm (EdDSA)", RFC 8032, , <https://www.rfc-editor.org/info/rfc8032>.
[RFC8259]
Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, , <https://www.rfc-editor.org/info/rfc8259>.
[RFC8552]
Crocker, D., "Scoped Interpretation of DNS Resource Records through "Underscored" Naming of Attribute Leaves", BCP 222, RFC 8552, , <https://www.rfc-editor.org/info/rfc8552>.
[RFC8615]
Nottingham, M., "Well-Known Uniform Resource Identifiers (URIs)", RFC 8615, , <https://www.rfc-editor.org/info/rfc8615>.
[RFC8785]
Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, , <https://www.rfc-editor.org/info/rfc8785>.
[RFC9110]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Semantics", RFC 9110, , <https://www.rfc-editor.org/info/rfc9110>.

13. Informative References

[RFC6376]
Crocker, D., Ed., Hansen, T., Ed., and M. Kucherawy, Ed., "DomainKeys Identified Mail (DKIM) Signatures", STD 76, RFC 6376, , <https://www.rfc-editor.org/info/rfc6376>.
[RFC6698]
Hoffman, P. and J. Schlyter, "The DNS-Based Authentication of Named Entities (DANE) Transport Layer Security (TLS) Protocol: TLSA", RFC 6698, , <https://www.rfc-editor.org/info/rfc6698>.
[RFC9421]
Backman, A., Ed., Richer, J., Ed., and M. Sporny, "HTTP Message Signatures", RFC 9421, , <https://www.rfc-editor.org/info/rfc9421>.
[RFC3552]
Rescorla, E. and B. Korver, "Guidelines for Writing RFC Text on Security Considerations", BCP 72, RFC 3552, , <https://www.rfc-editor.org/info/rfc3552>.
[RFC4033]
Arends, R., Austein, R., Larson, M., Massey, D., and S. Rose, "DNS Security Introduction and Requirements", RFC 4033, , <https://www.rfc-editor.org/info/rfc4033>.
[RFC7942]
Sheffer, Y. and A. Farrel, "Improving Awareness of Running Code: The Implementation Status Section", BCP 205, RFC 7942, , <https://www.rfc-editor.org/info/rfc7942>.
[RFC8792]
Watsen, K., Auerswald, E., Farrel, A., and Q. Wu, "Handling Long Lines in Content of Internet-Drafts and RFCs", RFC 8792, , <https://www.rfc-editor.org/info/rfc8792>.
[RFC9457]
Nottingham, M., Wilde, E., and S. Dalal, "Problem Details for HTTP APIs", RFC 9457, , <https://www.rfc-editor.org/info/rfc9457>.
[AGENT-MANIFEST]
agentrust-io, "Agent Manifest Specification, v0.2 (repository commit 2f1b9f5)", , <https://github.com/agentrust-io/agent-manifest/blob/2f1b9f53551b15fdad21e25f3ee1e698d2920fd8/spec/agent-manifest-spec-v0.2.md>.
[ARD]
ards-project, "Agentic Resource Discovery (ARD) Specification, v0.91", , <https://github.com/ards-project/ard-spec/blob/main/spec/ard.md>.
[ARD-INSTANCES]
ards-project, "Spec Proposal: Deployment metadata (ard-spec issue 44)", , <https://github.com/ards-project/ard-spec/issues/44>.

Appendix A. Test Vectors

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 [RFC8792] (a single backslash at the end of a line, with the continuation at the start of the next line).

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=

A.1. Base Vector (Schema v2.0)

Document A.1:

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"
  }
}

JCS(M), the canonicalized signed scope (312 octets), whose SHA-256 digest equals contentAddress.digest above:

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"}}

A.2. Instance-Bound Vector

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:

NOTE: '\' line wrapping per RFC 8792

{"instanceId":"0192f5c4-7e1a-7c3b-9d2e-5a4b6c7d8e9f","url":"http\
s://example.org/agent"}

Document A.2:

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"
  }
}

JCS(M) (482 octets):

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"}}

A.3. Outcome Vectors

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.

Table 5
ID Input Expected outcome
V1 A.2 document accepted
V2 No document obtained evidence_missing
V3 A.1 document (no instance member) instance_missing
V4 A.2 document; authorized set is {K'} anchor_untrusted
V5 A.2 document; no authorization path configured authorization_undetermined
V6 A.2 document with the last character of instance.id changed to 0, signature unchanged invalid_signature
V7 A.2 document; now = 2026-06-01T13:00:00Z stale
V8 A.2 document; now = 2026-06-01T11:58:00Z (created lies more than N seconds in the future) stale
V9 A.2 document; expected instance identifier = 0192f5c4-7e1a-7c3b-9d2e-ffffffffffff instance_mismatch
V10 A.2 document; expected descriptor digest is the SHA-256 digest of the ASCII string "other descriptor" instance_mismatch
V11 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} host_mismatch
V12 A.2 document; DNS publishes {K', K}; authorized set is {K'} anchor_untrusted
V13 A.2 document; DNS publishes {K', K}; authorized set is {K} accepted
V14 A.2 document; DNS publishes no valid v=SOVP1 record key_unresolved
V15 A.2 document text in which the member nonce appears twice in freshness document_invalid
V16 A.2 document text padded with spaces to 65537 octets document_invalid

Author's Address

Thorsten Litzki
Litzki Systems LLC
7901 4th St N, #32272
St. Petersburg, FL 33702
United States of America