quotation-fidelity v0.1
A signed assertion that a quoted span appears verbatim in a committed source, checkable against a 32-byte root by a reader who holds none of that source.
Predicate type: https://getprobity.dev/predicate/v1/quotation-fidelity
Status: draft. The namespace https://in-toto.io/attestation/quotation/v0.1 is unclaimed and is
the intended destination; this document is written to be portable to it.
What it asserts
That a quoted span appears verbatim at a stated position in a committed source, checkable against a 32-byte root by a reader holding none of that source, and how the source was obtained.
Why this exists
Three quotations were fabricated in three documents in one day, and every one was caught by a reader rather than by anything we own. The shared mistake was quoting from a summary of a source rather than from the source: faithful enough to feel quotable, lossy enough that the words are no longer the author's, and nothing about the sentence looks wrong on the page.
A gate now catches that locally. It cannot travel. A reader who wants to check a quotation needs the capture corpus, which is 5.9 GB, gitignored and mirrored off-repository — so the check is available to us and to nobody else. This predicate is the travelling form.
What was already built, and what was missing
Two vocabularies exist and neither has teeth, which is exactly the gap:
- W3C PROV defined
prov:Quotationandprov:wasQuotedFromin 2013 — "some taken Entity from an earlier, larger Entity" — with no digest, no signature and no location. - W3C Web Annotation defines
TextQuoteSelector(exact/prefix/suffix) andTextPositionSelector(start/end) — the location, with no cryptographic binding.
PROV supplies the relation. Web Annotation supplies the location. The binding that would make either checkable is what nobody built. This predicate is only that binding; the vocabulary is reused rather than reinvented.
C2PA already solved the hard part and aimed it at pixels: c2pa.hash.bmff does Merkle-over-chunks
with an init hash and a hash array, so a verifier can check one chunk without holding the file. For
text its specification mandates a whole-asset hash and states that partial verification is "not
supported by design." The machinery transfers to canonical text segments at the cost of a
translation, not a new cryptographic idea.
Shape
1. The source is committed as a Merkle tree over canonical text segments
Not as a digest of the whole file. A whole-file digest forces a verifier to hold the whole file, which is the thing that makes the local gate untravellable.
The tree is RFC 9162 (the Certificate Transparency tree), chosen because it is the only candidate
that is short-proof, short-state and already registered: RFC 9942 registers it as
RFC9162_SHA256 with a COSE-carryable inclusion proof, so the commitment is natively expressible in
SCITT (RFC 9943) without inventing a structure.
At ~1 KB segments over a 5.9 GB corpus (n ≈ 6×10⁶) an inclusion proof is ~23 sibling hashes ≈ 736 bytes, verifier state is 32 bytes, and the check is ~23 SHA-256 compressions.
Alternatives were considered and lose structurally, not incidentally. Redactable and sanitisable signatures prove membership but not position, and have no COSE algorithm identifier. KZG and vector commitments give 48-byte proofs but need a trusted setup and pairings to save 700 bytes nobody needs. SD-JWT is flat, so a verifier would hold six million digests. BBS+ buys unlinkability, which a public citation corpus actively does not want.
2. Canonical segmentation is normative, and it is the more durable claim
Two implementations that segment differently produce different roots for the same bytes, so the segmentation profile is part of the predicate rather than an implementation detail. Whoever writes this profile defines what "verbatim" means downstream — a more durable contribution than the predicate itself.
The normalisation is the one this project's gate already learned the hard way, and each rule cost a
real defect: NFKC; typographic quotes, dashes and non-breaking spaces folded; soft hyphens removed;
a hyphen before a line break preserved (removing it broke a verbatim quotation of a compound word
that happened to wrap); \n and \t inside JSON string captures unescaped (a document quoted
inside a JSON capture otherwise fails at 96%); leading blockquote and list markers stripped per line,
because specifications put their normative callouts in blockquotes and that is precisely the text
people quote; whitespace collapsed; lowercased.
An offset map back to raw bytes is required, not optional. Without it an implementation can locate a quotation and still not say where it lives in the artifact being attested to.
3. Acquisition is required, and a predicate that cannot state it is not signable
This is the field that keeps the artifact honest, and it exists because the first design of this predicate was circular.
Committing an excerpt beside a digest and having a verifier recompute one from the other proves only that the author was self-consistent — no step contacts the source. Signing that is worse than leaving it unsigned, because a signature over a self-reported capture converts an unverified assertion into an attested one. For a project whose thesis is evidence a distrusting party can re-check, that is the failure we exist to prevent, in the artifact we sell.
Requiring the strongest tier instead would make the predicate useless for the sources that matter
most. A competitor's page has no third-party custody and will attest nothing; a chat thread has
neither. Refusing to emit evidence about those is refusing to do the work. Emit it and say what it
is — the same rule this predicate applies to UNCHECKED, and that the sibling execution predicate
applies to a self-reported clean row.
(The internal design note behind this field is docs/architecture/ACQUISITION-PROVENANCE.md in the
Probity repository. It is named rather than linked: this document is published standalone, and a
relative link out of the specification tree resolves to nothing for a reader who has only the
specification — which is the exact defect that once left 139 cross-references broken on the
published site.)
acquisition | Means | A challenger can |
|---|---|---|
self-captured | We fetched it and stored it | Re-fetch and compare, if the source still exists unchanged |
self-captured-witnessed | Plus a digest in an append-only log at capture time | Prove we did not alter the capture afterwards, and place it in time |
third-party-custody | An independent archive holds a copy | Fetch the bytes from someone who is not us |
origin-attested | Cryptographic proof the origin served these bytes | Verify without trusting us or the archive |
The weakest value in a bundle is surfaced in every rendering of it. A bundle is exactly as strong
as its weakest acquisition, and a reader who has to compute that will not. self-captured is never
upgraded by re-signing; only by obtaining different evidence.
Verdicts
Three, and the middle one is the point:
VERIFIED | The span appears verbatim in the committed source at the stated position |
ALTERED | The source is committed and the wording differs. A finding. |
UNCHECKED | No commitment covers this span. Not a finding, and must never be reported as one. |
An unchecked quotation is exactly as unverified as a fabricated one from this predicate's point of
view, and saying so is the honest output. A verifier that reports UNCHECKED as a pass has
reproduced the defect this exists to refuse.
An elision is not an alteration. A span written with an ellipsis asserts that its parts appear in that order with text omitted between them, which is honest quoting; each segment is verified separately and in order, and segments too short to be distinctive are rejected so an elision cannot smuggle in a match on stopwords.
Carriage
DSSE, over application/vnd.in-toto+json.
Not a Rekor intoto entry. Rekor v2 GA dropped that entry type; only hashedrekord and dsse
remain, so any design assuming an intoto entry is already broken against current infrastructure.
What this does not assert
- That the source is true, or that its author meant what the quotation implies. It asserts transcription fidelity and nothing about meaning.
- That the excerpt is authentic at
acquisition: self-captured. At that tier anyone with commit rights can commit a false excerpt and a matching root. That is why the field is required and why its weakest value is surfaced — the limit is made legible rather than hidden. - That the source still says this today. The commitment is to bytes captured at a stated time. Sources drift: better than 75% of scholarly references were measured to have drifted in one study, so a pin against a live source is a review scheduler, not a security control.