| Internet-Draft | Crovia Seal v1 | May 2026 |
| En Nakhai | Expires 5 November 2026 | [Page] |
This document specifies the Crovia Seal v1, a compact, tamper-evident JSON receipt that may be attached to any output produced by an AI generator (large language model, image model, audio model, or composite system) to record its provenance in a cryptographically verifiable form. A Crovia Seal binds an issuer's identity to the SHA-256 digests of an input/output pair, the identity and parameters of the generator, an emission timestamp, and a per-issuer hash chain, under an Ed25519 signature computed over a strict canonicalization (CSC-1) of the receipt with explicit domain separation. Optional fields permit transparency-log inclusion proofs and witness co-signatures. The Seal is designed for offline verification and for inclusion in third-party transparency logs and standards-based revocation infrastructure.¶
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 5 November 2026.¶
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 2 November 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.¶
Outputs produced by automated generative systems (large language models, diffusion image models, speech-synthesis pipelines, or composite agents) are difficult to attribute reliably after they leave the generating system. Today, an output may be quoted, edited, copy-pasted, ingested into a search index, surfaced in a court filing, or attributed to the wrong vendor with no available chain of custody. Both vendors and downstream consumers have an operational interest in an attestation that fixes, at the moment of generation, the bytes that were produced and the parameters under which they were produced.¶
This document specifies a single such attestation: the Crovia Seal. A Seal is a JSON object whose bytes are signed by an issuer's Ed25519 private key after a deterministic canonicalization (Section 3) and with explicit cryptographic domain separation (Section 3.3). A Seal carries the SHA-256 digest of the input that was provided to the generator, the SHA-256 digest of the output, the generator identity and generation parameters, an issuance timestamp, and a per-issuer append-only hash chain. Optional fields carry inclusion proofs from public transparency logs and additional co-signatures ("witnesses").¶
A Seal does not attest to the truthfulness, lawfulness, originality, or safety of the output. It is a receipt, not a verdict. This deliberate narrowness is essential to the protocol's value: the Seal can be re-verified by any party possessing only the Seal, the issuer's public key, and the original output bytes, without dependence on any judgement that cannot be cryptographically checked.¶
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.¶
The following terms are used throughout this document:¶
The following are explicitly out of scope for this specification. An attestation that silently included any of them would be misunderstood by users:¶
JSON [RFC8259] is syntactically flexible: whitespace, key ordering, number formatting, and string-escape choices can vary while producing the same logical value. A signature over a JSON text requires an unambiguous serialization.¶
CSC-1 is a strict subset of the JSON Canonicalization Scheme
(JCS) [RFC8785]. It adopts the deterministic
ordering and escaping rules of JCS but forbids floating-point
numbers in signed payloads, sidestepping the edge cases of
ECMA-262 number serialization. This restriction does not
constrain use cases: continuous parameters such as
temperature MUST be encoded as strings when carried
inside the signed payload (Section 3.6).¶
A CSC-1-serialized JSON value is a UTF-8 byte sequence produced as follows:¶
null → null¶
true / false → true / false¶
+ sign.¶
\", \\, \b, \f,
\n, \r, \t, and
\u00XX for U+0000..U+001F. All other code points
MUST be emitted literally.¶
[ followed by canonicalized elements
separated by , followed by ]. No interior
whitespace.¶
{ followed by "key":value
pairs separated by , followed by }. Keys
MUST be sorted ascending by their UTF-16 code-unit sequence
(equivalent to JavaScript
Array.prototype.sort on strings). No interior
whitespace.¶
-0 MUST cause
serialization to fail with NonCanonicalNumber.¶
DuplicateKey.¶
NonStringKey.¶
Given a Seal S, the signing payload P(S) is computed as:¶
P(S) = DOMAIN || 0x0A || CSC1(S \ {signature, witnesses})
¶
where:¶
DOMAIN is the ASCII string "CROVIA-SEAL-v1" (14 bytes).¶
0x0A is a single newline byte, acting as an unambiguous separator.¶
S \ {signature, witnesses} denotes the Seal with
the signature and witnesses top-level
fields removed (they are computed over the payload, not part
of it).¶
CSC1(...) is the UTF-8 serialization per Section 2.2.¶
The DOMAIN prefix ensures that a signature over
P(S) cannot be replayed as a valid signature in any
other protocol that does not use the same prefix.
Implementations MUST NOT omit the prefix. Verifiers MUST
reject any Seal whose signature was produced without the
prefix.¶
A conformant Seal is a JSON object with exactly the top-level fields listed below. Unknown top-level fields MUST cause verification to fail (the "fail closed" principle).¶
| Field | Required | Type | Section |
|---|---|---|---|
seal_version
|
MUST | string | 4.2 |
seal_id
|
MUST | string | 4.3 |
issuer
|
MUST | object | 4.4 |
subject
|
MUST | object | 4.5 |
generator
|
MUST | object | 4.6 |
timestamp
|
MUST | object | 4.7 |
chain
|
MUST | object | 4.8 |
checks
|
OPTIONAL | object | 4.9 |
anchor
|
OPTIONAL | object | 4.10 |
signature
|
MUST | object | 4.11 |
witnesses
|
OPTIONAL | array | 6 |
The literal string "crovia.seal.v1". Any other
value MUST cause verification to fail.¶
A string matching the regular expression
^cs_[0-9]{4}_[A-Z2-7]{26}$: prefix cs_,
4-digit issuance year, underscore, 26 RFC 4648 base32
characters [RFC4648] (alphabet A-Z, 2-7, no
padding) encoding 16 random bytes (128 bits). The random
bytes MUST be produced by a cryptographically secure source.¶
{
"id": string, ; urn:crovia:seal-issuer:<name>
"pubkey": { "alg": "ed25519", "key_hex": string }
}
¶
key_hex is 64 lowercase hexadecimal characters (32
raw bytes, Ed25519 public key per [RFC8032]).
Other algorithms are reserved for
Section 6.¶
{
"input_hash": "sha256:" + 64 lowercase hex chars,
"output_hash": "sha256:" + 64 lowercase hex chars,
"input_len": integer, ; byte length of input
"output_len": integer, ; byte length of output
"modality": string ; one of: text/code/image/audio/multimodal
}
¶
The Seal does NOT carry the content itself. The hashes commit to the content; verifiers who possess the content can re-hash and compare.¶
{
"id": string, ; e.g. "openai/gpt-4o"
"version": string | null, ; e.g. "2024-08-06"
"weights_hash": string | null, ; if available
"params": object ; key -> string map of gen-params
}
¶
All parameter values MUST be strings in the signed payload (per
the floating-point restriction in
Section 2). Numeric values like
temperature=0.7 MUST be encoded as
"0.7".¶
{
"emitted_at": string, ; RFC 3339 UTC, ms precision
; e.g. "2026-04-15T12:34:56.789Z"
"nonce": string ; 26 RFC 4648 base32 chars (16 random bytes)
}
¶
emitted_at follows [RFC3339].
Issuers MUST use UTC. The nonce guarantees that two
Seals issued in the same millisecond are still distinct under
P(S).¶
{
"prev_seal_hash": "sha256:" + 64 hex chars | null,
"sequence": integer (>= 0)
}
¶
prev_seal_hash is the SHA-256 over the canonical
payload P(S_prev) of the immediately preceding Seal
from the same issuer, or null for the genesis Seal
(sequence == 0). Verifiers that track issuer chains
MUST detect:¶
chain.sequence but different
prev_seal_hash. This is non-repudiable evidence of
issuer misbehavior or key compromise.¶
Free-form object carrying analytical claims such as memorization checks, safety probes, toxicity scores. The schema for specific check types is defined in separate check specifications; the Seal itself imposes no constraints on the content beyond CSC-1 compatibility.¶
Example:¶
{
"memorization": {
"db_version": "crovia-memdb-2026-04-15",
"method": "ngram-lsh-v1",
"matches": 0,
"max_conf": "0.03"
}
}
¶
Each check produced by an issuer is signed along with the rest of the Seal; its validity as evidence depends on the method's own robustness, which is out of scope for this specification.¶
{
"log_url": string,
"merkle_root": "sha256:" + 64 hex chars,
"merkle_proof": [ "sha256:" + hex, ... ],
"log_index": integer (>= 0),
"root_signed_at": string (RFC 3339 UTC)
}
¶
The anchor commits the Seal to a public transparency log.
The log operator's signature over merkle_root is not
part of this Seal and MUST be fetched separately from
log_url.¶
{
"alg": "ed25519",
"canon": "csc-1",
"domain": "CROVIA-SEAL-v1",
"payload_hash_alg": "sha256",
"sig_hex": string ; 128 hex chars (64 raw bytes)
}
¶
The signature is computed as
Ed25519_sign(privkey, P(S)) where P(S) is
defined in Section 2.3. Implementations
MUST NOT hash the payload before signing; Ed25519 internally
handles the hash (SHA-512). The payload_hash_alg
field is informational, indicating the algorithm used to
derive prev_seal_hash and other SHA-256 digests in
the Seal.¶
"witnesses": [
{
"id": string,
"pubkey": { "alg": "ed25519", "key_hex": string },
"sig_hex": string
},
...
]
¶
A witness signs the same canonical payload P(S) as the issuer. Witness signatures are OPTIONAL and additive: a Seal with no witnesses is valid; a Seal with invalid witness signatures is invalid overall (fail-closed).¶
Typical witnesses: consortium co-signers (civil-society organizations, academic institutions, regulatory observers). Witnesses endorse the Seal without endorsing its content.¶
A conformant transparency log accepts Seals and periodically publishes a signed Merkle root over the Seals it has received. The log API design is outside the scope of this document; an implementation profile compatible with [RFC9162]-style logs is anticipated in a companion document.¶
Future versions of this specification MAY permit alternative
signature algorithms (e.g., Dilithium, Falcon) by extending the
signature.alg vocabulary. A Seal MAY additionally
carry a pq_signature top-level field with an
independent post-quantum signature over the same payload
P(S). Verifiers that support only Ed25519 MUST ignore
pq_signature and rely on signature.
Verifiers MAY require both signatures to validate (strict mode).¶
Prevented by domain separation
(Section 2.3). A signature on
P(S) cannot be reinterpreted as a valid signature on
a payload of any other protocol that does not use the exact
same 14-byte CROVIA-SEAL-v1 prefix followed by a
newline.¶
A Seal is a historical record; "replay" of a Seal is a
semantic issue, not a cryptographic one. The
seal_id, timestamp, and
chain.sequence fields make each Seal unique.
Verifiers that track seen seal IDs can detect duplication
attempts.¶
Eliminated by CSC-1 (Section 2). Any tool that reorders keys, adds whitespace, or re-escapes strings produces a different byte sequence and hence an invalid signature.¶
The signature covers every field except signature
and witnesses, which are themselves cryptographic.
Adding, modifying, or deleting any field invalidates the
signature.¶
If an issuer key is compromised, the attacker can issue valid Seals until the compromise is detected and the key is revoked in the transparency log's trust root. The issuer hash chain MAY reveal the compromise if the attacker issues a forking Seal.¶
seal_version, signature.alg,
signature.canon, and signature.domain are
all inside the signed payload. An attacker cannot negotiate
a weaker algorithm without producing a wholly new signature.¶
CSC-1 forbids floats in the signed payload precisely to avoid the numeric edge cases of JCS. Strings carrying numeric values MUST use a documented format (see Section 3.6).¶
SHA-256 is used for all digests (content commitments, chain
links). Migration to SHA-3 or BLAKE3 is anticipated in a
future minor version; the payload_hash_alg field
signals the choice.¶
All random values (seal_id suffix,
timestamp.nonce) MUST be produced by a
cryptographically secure source (e.g.,
os.urandom, the secrets module in Python,
crypto.randomBytes in Node.js).¶
The Seal commits to input/output hashes, never content.
Content-bearing fields such as generator.params are
in the clear; issuers MUST NOT place sensitive user data in
these fields.¶
IANA is requested to register the following media type per [RFC6838]:¶
IANA is requested to register the URN namespace
urn:crovia:seal-issuer: per [RFC8141]
for the identification of Crovia Seal issuers by stable URN.
The complete formal namespace registration template will be
provided in a companion document.¶
A reference implementation in Python and TypeScript, including a public sealing service, an in-browser verifier, and a set of normative test vectors, is available at https://croviatrust.com/registry/seal/. Test vectors are linked from https://croviatrust.com/registry/seal/spec/.¶
draft-crovia-seal-00 (May 2026):¶
The Crovia Seal has been informed by extensive prior work in cryptographic transparency ([RFC9162], [SIGSTORE]) and media-provenance attestation ([C2PA]). The authors thank the broader community working on AI provenance and tamper-evidence for the intellectual foundations on which this work rests.¶