/// Litzki Systems LLC — Draft Product Spec

SovereignAttestation — Format & Verification Procedure

1. What this is (and is not)

Two documents exist for a domain that has gone through SOVP validation:

DocumentLocationSigned byClaim
sovp-identity.json subject domain's own /.well-known/ subject domain's own key (_sovp.<subject-domain>, possibly custody-managed) “I am this domain” — self-asserted identity
sovp-attestation.json subject domain's own /.well-known/ Litzki Systems LLC issuer key (_sovp-attestation.litzki-systems.com) “Litzki certifies this verdict about this domain” — third-party attestation

These are independent trust claims from independent signers, verified against independent DNS-anchored keys. A verifier who cares about the verdict trusts the attestation, signed by the issuer. The identity document's own scan block (if present) is explicitly out-of-scope for trust decisions per draft-litzki-sovp-03 §5 — it is not, and must not be read as, a substitute for this attestation.

2. Document structure

{
  "@context": "https://litzki-systems.com/protocol/sovp-attestation-v1",
  "@type": "SovereignAttestation",
  "subject": {
    "domain": "example.com",
    "canonical_url": "https://example.com"
  },
  "attestation": {
    "verdict": "CERTIFIED",
    "sovereignScore": 87.4,
    "contentQuality": 91,
    "aiReadiness": 88,
    "infrastructureScore": 79,
    "authorityScore": 62,
    "coherenceScore": 70,
    "parameterCount": 268,
    "vault_payload_digest": "<sha256 hex, optional>",
    "scanned_at": "2026-07-22T10:00:00Z",
    "valid_until": "2026-10-20T10:00:00.000Z",
    "spec_url": "https://litzki-systems.com/protocol/sovp-attestation-v1"
  },
  "issuer": {
    "uid": "urn:sovp:litzki-systems-llc",
    "canonical_url": "https://litzki-systems.com"
  },
  "integrity_proof": {
    "signature": "<base64>",
    "created": "2026-07-22T10:00:03.000Z",
    "public_key_ref": "dns:txt:_sovp-attestation.litzki-systems.com",
    "nonce": "<uuid4>"
  }
}

spec_url points at this specification, not at the IETF identity draft — the attestation is a Litzki product artifact, and pointing at the draft would misrepresent it as an IETF-specified object.

attestation.vault_payload_digest is optional: a SHA-256 hex digest of the JCS-canonicalized Sovereign Vault record this attestation was issued for. Because it sits inside the signed scope, it binds the vault record's grading threshold and scores to this signature — the vault record can no longer be edited after issuance without invalidating the attestation. Attestations issued before this field existed, and verifiers that don't check it, remain valid.

3. Signed scope

The Ed25519 signature covers every top-level field except integrity_proof — @context, @type, subject, attestation, issuer. This differs from the SovereignIdentity document, where vendor extensions (like scan) sit outside the signed scope; here, the verdict and scores are the entire point of the document, so they are inside the signed scope by construction.

4. Canonicalization

Identical rule to draft-litzki-sovp-03 §4: the payload (all signed fields) MUST be serialized using an RFC 8785-compliant JSON Canonicalization Scheme (JCS) implementation before signing or verifying. Non-compliant canonicalizers (ad hoc JSON.stringify, handwritten sorters that don't recurse into arrays, etc.) MUST NOT be used — they silently break cross-implementation verification even when the signature math is otherwise correct. Reference implementation here uses the canonicalize npm package (RFC 8785 conformant); a Python verifier should use the jcs PyPI package for the same reason.

5. Signature algorithm

Ed25519 in pure mode (RFC 8032 §5.1) over JCS(payload) directly — no external pre-hash is applied before the sign/verify call (Ed25519 already applies SHA-512 internally). This is directly verifiable with standard tools, e.g.:

openssl pkeyutl -verify -rawin \
  -pubin -inkey issuer_pubkey.pem \
  -sigfile signature.bin \
  -in canonical_payload.json

where canonical_payload.json is the JCS-canonicalized payload bytes and signature.bin is the base64-decoded integrity_proof.signature.

6. Key location (trust anchor)

The issuer's Ed25519 public key is published as a DNS TXT record:

_sovp-attestation.litzki-systems.com  IN TXT  "v=SOVP1; k=<base64-raw-32-byte-key>"

Format identical to draft-litzki-sovp-03 §10.1. This label is deliberately distinct from _sovp.litzki-systems.com (the SovereignIdentity key for litzki-systems.com itself) and from any _sovp.<subject-domain> label (the subject's own identity key). A verifier resolves the issuer key from DNS independently — never trusts a key embedded in the document itself, and never trusts a key found under the subject domain's own _sovp label for this purpose.

Recommended TTL: operationally dependent, not a fixed value. Short (300 seconds, per draft-litzki-sovp-03 §8.1) while the issuer key rotates frequently; longer (3600 seconds or more) once the issuer key is stable — a longer TTL reduces resolver load and lookup latency for verifiers with no revocation-speed cost as long as no rotation is imminent. Lower the TTL back to 300 seconds ahead of a planned key rotation, so the old key falls out of caches quickly once the new one is published. Same revocation mechanism either way (update/remove the TXT record).

7. Expiry semantics

attestation.valid_until is either an ISO-8601 timestamp or the literal string "permanent".

Verification produces two independent results, not one collapsed boolean:

  • signature_valid (bool) — purely cryptographic. Never affected by dates. A tampered document is signature_valid: false regardless of whether valid_until has passed.
  • status ("valid" | "expired") — purely a comparison of valid_until against the current time. Independent of signature_valid.

A genuine attestation past its valid_until is {signature_valid: true, status: "expired"} — it was never forged, it lapsed. Consumers MUST NOT report an expired-but-genuine attestation as “invalid” or “tampered”; those are different failure modes with different remediation (re-scan vs. security incident).

8. Verification procedure (informal)

  1. Fetch https://<subject-domain>/.well-known/sovp-attestation.json.
  2. Resolve _sovp-attestation.litzki-systems.com TXT record; parse v=SOVP1; k=<base64>.
  3. Strip integrity_proof from the document; JCS-canonicalize the rest.
  4. Ed25519-verify integrity_proof.signature against the canonical bytes using the DNS-resolved public key.
  5. Compare attestation.valid_until against current time.
  6. Report signature_valid and status separately (see §7).

Reference implementation: verify-attestation.mjs (offline reference verifier, Node.js + canonicalize).