Expand description
Provenance attestation — turning “we have a trace” into “we have evidence”
(SPEC.md §6.5, ADR 0010).
A Provenance link carries a digest. A digest is tamper-evident only
to a party that already trusts whoever recorded it: it proves the bytes
did not change since someone wrote that number down, and says nothing
about who wrote it or whether they were entitled to. For a host reading its
own cache that is enough. For the auditor asking “prove this citation is
what the provider actually served,” it is not — the digest and the frame it
describes were produced by the same unauthenticated party, so a provider
that fabricates a frame simply fabricates a matching digest.
A signature closes that gap, and it is the only thing that does. This module defines the three constructions that make a frame’s provenance verifiable offline, by a third party, with no network and no trust in the host that stored it:
- A provenance chain hash (
provenance_chain_head) — a hash chain over a frame’s orderedProvenancelinks, folded source-first, so no link can be inserted, removed, reordered, or edited without changing the head. - A frame commitment (
frame_commitment) — the chain head bound to the frame’s fullFrameIdidentity. - A Merkle root (
merkle_root) over a whole result set, withInclusionProofs, so one frame can be proven a member of a signed answer without disclosing its siblings.
ProvenanceAttestation is the detached Ed25519 signature over (1)–(3).
It reaches a host on the frames envelope, in
ContextQueryResult::frame_attestations
and
ContextQueryResult::result_attestation
— beside the frames, never inside one (SPEC.md §6.5.5, F6/F11).
§Why the frame identity is inside the signed preimage
Signing a bare chain head would be a forgery primitive, not a defense. Two
frames citing the same source share a chain head, so a signature over the
head alone can be lifted from an innocuous frame and stapled onto a
fabricated one: the signature verifies, the evidence is invented. The signed
preimage therefore commits to (provider_id, frame_id, content_digest) —
the whole FrameId triple — and the chain head. A signature binds to one
frame served by one provider, or it binds to nothing.
§Why the encoding is length-prefixed rather than canonical JSON
The lifecycle profile’s record_hash canonicalizes with RFC 8785 (JCS),
which is the right choice there: a record’s hash covers a whole open-ended
JSON document. A provenance chain is a fixed list of six optional strings,
and for that shape JCS is a liability — it makes every implementation depend
on a conforming JSON canonicalizer, whose number formatting and Unicode
escaping rules are exactly where cross-language implementations silently
disagree.
This module encodes the typed fields directly, each length-prefixed
(SPEC.md §6.5.1). Length prefixing is not decoration:
naive concatenation is ambiguous, and a chain with uri: "ab", range: "c"
would otherwise hash identically to one with uri: "a", range: "bc" — a
collision an adversary chooses, not one they have to find. A four-byte
big-endian length in front of every field makes the encoding injective, and
any language can produce it from the typed fields with no library at all.
§Cryptography is optional; the preimage rule is not
Hashing and signature verification live behind the off-by-default
attestation feature, so contextgraph-types keeps its “zero dependencies
beyond serde” promise for the pure wire consumer. ProvenanceAttestation
itself is a wire type and always compiles — a host must be able to parse,
relay, and store an attestation it has not been built to check, exactly as it
relays a frame kind it does not recognize.
The protocol defines the preimage; it does not define your signing
backend. frame_commitment and merkle_root are public so a provider
holding keys in an HSM, a KMS, or a hardware token signs the bytes itself
and never hands this crate a secret. sign_frame_attestation exists for
providers and tests that are content to sign in-process.
Structs§
- Frame
Attestation - What a result set says about one frame’s attestation — the wire carrier that
keeps a
ProvenanceAttestationbeside the frame it covers (SPEC.md§6.5.5, F11). - Inclusion
Proof - A proof that one frame commitment is a leaf of a signed
merkle_root(SPEC.md§6.5.3). - Inclusion
Step - One step of a Merkle
InclusionProof: the sibling hash, and which side it sits on. - Provenance
Attestation - A detached attestation binding one frame’s provenance to a signing identity
(
SPEC.md§6.5).
Enums§
- Attestation
Verdict - The outcome of checking a
ProvenanceAttestation(SPEC.md§6.5.4).
Constants§
- ALGORITHM_
ED25519 - The signature algorithm this revision defines.
algorithmis a string, not an enum, precisely so a post-quantum successor is an additive change rather than a new major family — seeProvenanceAttestation::algorithm. - MAX_
INCLUSION_ PATH_ STEPS - The longest inclusion path this crate will walk (
SPEC.md§6.5.3).
Functions§
- digest_
string - Render 32 raw bytes as this protocol’s
sha256:<hex>digest string. - encode_
provenance_ link - The canonical encoding of one provenance link (
SPEC.md§6.5.1). - frame_
commitment - The commitment binding one frame’s identity to its provenance chain
(
SPEC.md§6.5.2) — the preimage a single-frame attestation signs. - inclusion_
proof - Build an
InclusionProofforleaf_indexwithincommitments.Noneif the index is out of range. - merkle_
root - The Merkle root over a set of frame commitments (
SPEC.md§6.5.3). - provenance_
chain_ head - The head of a frame’s provenance hash chain (
SPEC.md§6.5.2). - public_
key_ for - The public key matching a signing seed, as raw bytes — the form
verify_frame_attestationaccepts. - result_
set_ commitments - Every frame of a result set paired with its commitment, in the
protocol’s canonical
FrameIdorder (SPEC.md§6.3, §6.5.3). - result_
set_ root - The Merkle root a provider signs to attest a whole answer
(
SPEC.md§6.5.3, F12). - root_
from_ proof - Recompute a Merkle root from a leaf commitment and its proof.
- sign_
commitment - Sign an arbitrary commitment (a frame commitment or a Merkle root).
- sign_
frame_ attestation - Sign a frame’s commitment in-process, for providers content to hold key material in memory.
- verify_
commitment - Verify a detached attestation over an already-computed commitment — a
merkle_rootfor a result set, or aframe_commitment. - verify_
frame_ attestation - Verify a detached attestation over a single frame (
SPEC.md§6.5.4). - verify_
frame_ inclusion - Verify that a frame was a leaf of a signed result-set root
(
SPEC.md§6.5.3, F13).