feat: minted keys, VRF seeds, RFC 9162 proofs, history roots, tag URIs and a transparency log
Keys (spec/02 §2.4), the rule paperstack settled on:
- A work key is a Scholar-style citation key, a hyphen and the first four
Crockford base32 characters of SHA-256("scienting-key/1:" + identity),
where the identity is the first of doi, arxiv, isbn, pmid, pmcid and
openalex in same_as, else the folded title, year and family name. A
collision lengthens the later key by two characters; a key is minted
once and never recomputed. A document key adds .si, .v<seq> or .<type>
for versions other than the version of record.
- Every fixture key is now minted (hale2026harmonic-damb,
okafor2025solvent-acth, writer2027fixed-ynr7.v3, tiny2026tiny-j4qm), so
every node and record ID was re-minted and the docs rewritten to match.
The validator checks the document key against the work key and version
(doc.key.work) and the hash part against the identifier (doc.key.suffix).
vectors/keys.jsonl pins the cases paperstack's tests pin.
Review:
- Spot-check seeds are ECVRF-EDWARDS25519-SHA512-TAI proofs (RFC 9381),
implemented in tools/oracle/vrf.py in plain Python and reproduced
against the RFC's Example 16; the sample ranks by the VRF output. The
earlier Ed25519 seed could be ground by a dishonest checker.
- RFC 9162 inclusion and consistency proofs (crypto.inclusion_path,
verify_inclusion, consistency_proof, verify_consistency) with
scienting-proof/1, vectors/proofs.jsonl and two fixture records.
- document.json carries `history`, the root over the corrections, with
the empty-tree hash for none (spec/11 §11.5, rule doc.history).
- A provisional transparency log (spec/23): log entries, signed-note
checkpoints with key hashes as transparency logs share them, the rule
that a logged revocation beats a self-asserted time, and vectors.
- Reserved signature schemes cose and ml-dsa-65 (spec/08 §8.4).
Identifiers and text:
- urn:scienting: becomes tag:scient.ing,2026: (RFC 4151), since the URN
namespace was never registered.
- Unicode 16.0 is pinned for normalization and categories (spec/01 §1.4);
the checker refuses another version.
Checking:
- check.py: keys, proofs, VRF and checkpoint vectors; the proof and
checkpoint records; a check that every ID and hash the prose quotes
exists in the fixtures or vectors; three more broken packages
(history-wrong, key-suffix, key-work).
- stress.py: key, VRF, proof and checkpoint properties.
- Docs: a keys section in the concepts and FAQ, rationale entries for
keys, VRF, proofs, tag URIs and the Unicode pin, a "Looking ahead"
section, media-type registration and OCI distribution notes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
307 files changed
+2698
-1497
No files matched your search
+37
-37
@@ -1,17 +1,17 @@
|
||||
# Walkthrough: one paper, start to finish
|
||||
|
||||
This guide follows the example paper `hale2026harmonic` from a flawed first reading to a corrected, reviewed and signed document.
|
||||
This guide follows the example paper `hale2026harmonic-damb` from a flawed first reading to a corrected, reviewed and signed document.
|
||||
The paper is synthetic, written for the fixtures.
|
||||
Its problems are modeled on the failures that the specification's rules address.
|
||||
|
||||
Three readers can use the guide, and each gets something different.
|
||||
A newcomer knows what a paper and a citation are, not hashes or JSON, so every term is defined where it first appears.
|
||||
An implementer who will write a reader, writer, converter or checker gets the chapter and test vector for each step.
|
||||
An expert gets the status: [paperspec](https://git.scient.ing/infra/paperspec) is at version 0.1.0, and each format is `candidate` or `provisional` in [`registry/formats.json`](../registry/formats.json) (18 candidate, 11 provisional).
|
||||
An expert gets the status: [paperspec](https://git.scient.ing/infra/paperspec) is at version 0.1.0, and each format is `candidate` or `provisional` in [`registry/formats.json`](../registry/formats.json) (19 candidate, 13 provisional).
|
||||
The `scienting-` prefix on format names, as in `scienting-doc/1`, is the formats' namespace.
|
||||
It has nothing to do with the repository's name.
|
||||
|
||||
Open [fixtures/packages/hale2026harmonic/](../fixtures/packages/hale2026harmonic/) beside this page, because every ID below is in it.
|
||||
Open [fixtures/packages/hale2026harmonic-damb/](../fixtures/packages/hale2026harmonic-damb/) beside this page, because every ID below is in it.
|
||||
A package is a folder of files, mostly JSON Lines (`.jsonl`: one JSON record per line).
|
||||
The hand-written source of the first reading is [fixtures/src/hale2026harmonic.json](../fixtures/src/hale2026harmonic.json); no converter ran on it, and [`tools/build.py`](../tools/build.py) expands it into the package.
|
||||
Each step says what a converter, a person or a checker does.
|
||||
@@ -26,7 +26,7 @@ Whether it allows processing in the team's cloud is unknown.
|
||||
|
||||
The publisher stamped the download in the margin with the institution and the time, so two downloads rarely share a SHA-256.
|
||||
A page fingerprint avoids the problem of downloads that differ in their stamps.
|
||||
It is a hash of a page's text, size and images after the stamp lines are removed ([spec/07](../spec/07-geometry.md)), and the rules `downloaded-via` and `sharing-guidelines` fired on both pages (see [`pages.jsonl`](../fixtures/packages/hale2026harmonic/pages.jsonl)).
|
||||
It is a hash of a page's text, size and images after the stamp lines are removed ([spec/07](../spec/07-geometry.md)), and the rules `downloaded-via` and `sharing-guidelines` fired on both pages (see [`pages.jsonl`](../fixtures/packages/hale2026harmonic-damb/pages.jsonl)).
|
||||
Page 101's fingerprint is `6272e6f3…` in this copy, stamped October 11, 2026, and in a second download from another institution, stamped March 2, 2027 ([vectors/fingerprints.jsonl](../vectors/fingerprints.jsonl)).
|
||||
|
||||
## 2. The first reading, with its mistakes
|
||||
@@ -57,34 +57,34 @@ Nothing changes until a correction passes the gate ([spec/11](../spec/11-correct
|
||||
|
||||
## 3. Twelve corrections
|
||||
|
||||
Each fix is appended to [`corrections.jsonl`](../fixtures/packages/hale2026harmonic/corrections.jsonl), numbered, with its evidence (a crop, or a person who read the page), its author (`ana` throughout) and its reason.
|
||||
Each fix is appended to [`corrections.jsonl`](../fixtures/packages/hale2026harmonic-damb/corrections.jsonl), numbered, with its evidence (a crop, or a person who read the page), its author (`ana` throughout) and its reason.
|
||||
|
||||
| # | Operation | On | What it did |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | `replace` | cell `nkm3xn5` | `3l0.0` to `310.0`. A digit changed, so the gate required a crop, and it has one. |
|
||||
| 2 | `replace` | sentence `e4fkseg` | `10-3` to the runs `10` and superscript `−3`. |
|
||||
| 3 | `split` | sentence `e4fkseg` | Split before "Table 1"; the first part keeps `e4fkseg`, the second is new: `r1gacps`. |
|
||||
| 4 | `merge` | sentence `eajq93t` | Joined with `e9rh611`, which is retired with `eajq93t` as its successor. |
|
||||
| 5 | `merge` | paragraph `zyqr0yc` | Joined with `g44k023`, retired the same way. |
|
||||
| 6 | `delete` | paragraph `nm7vyzk` | The running head is gone; its IDs are retired with no successor. |
|
||||
| 7 | `relabel` | equation `qn08tj9` | `(l)` to `(1)`. |
|
||||
| 8 | `delete` | row `tfjge3c` | The garbled CT–HC row and its four cells. |
|
||||
| 9 | `insert` | row `fbg2mwq` | The CT–HC row, read again: five new cells. |
|
||||
| 10 | `insert` | row `j0x5twd` | The missing OW–HW row. |
|
||||
| 11 | `set` | cell `vtszvw9` | Set B's `colspan` from 1 to 2. |
|
||||
| 12 | `relabel` | figure `ajfvq4s` | `Figure l` to `Figure 1`. |
|
||||
| 1 | `replace` | cell `c20cna6` | `3l0.0` to `310.0`. A digit changed, so the gate required a crop, and it has one. |
|
||||
| 2 | `replace` | sentence `ec5jc93` | `10-3` to the runs `10` and superscript `−3`. |
|
||||
| 3 | `split` | sentence `ec5jc93` | Split before "Table 1"; the first part keeps `ec5jc93`, the second is new: `a4jrz2t`. |
|
||||
| 4 | `merge` | sentence `xjns57z` | Joined with `ayea71r`, which is retired with `xjns57z` as its successor. |
|
||||
| 5 | `merge` | paragraph `gwzbj3s` | Joined with `ncx2v4f`, retired the same way. |
|
||||
| 6 | `delete` | paragraph `v8e4z6z` | The running head is gone; its IDs are retired with no successor. |
|
||||
| 7 | `relabel` | equation `tkxsmts` | `(l)` to `(1)`. |
|
||||
| 8 | `delete` | row `rncchma` | The garbled CT–HC row and its four cells. |
|
||||
| 9 | `insert` | row `kx2kz2y` | The CT–HC row, read again: five new cells. |
|
||||
| 10 | `insert` | row `ke0jxpn` | The missing OW–HW row. |
|
||||
| 11 | `set` | cell `xyy7x6c` | Set B's `colspan` from 1 to 2. |
|
||||
| 12 | `relabel` | figure `v7cx6qs` | `Figure l` to `Figure 1`. |
|
||||
|
||||
The document is now at revision 12.
|
||||
No uncorrected cell, sentence or row changed its ID or its hashes.
|
||||
Only corrected nodes and their ancestors got new `node_hash` values; the sentence `sxgcc9h` is one of the ancestors, because it holds the relabeled equation.
|
||||
Only corrected nodes and their ancestors got new `node_hash` values; the sentence `rr20tev` is one of the ancestors, because it holds the relabeled equation.
|
||||
Inserting a row adds a row node.
|
||||
The table's own hash (`own_hash`) covers only its label and column definitions, so every other cell, and every value already taken from them, is untouched.
|
||||
|
||||
## 4. IDs that never move
|
||||
|
||||
The sentence about water is still `f929eq9` after twelve corrections, the corrected cell is still `nkm3xn5`, and the sentence that lost its superscript is still `e4fkseg`.
|
||||
The retired IDs are recorded in [`ids.jsonl`](../fixtures/packages/hale2026harmonic/ids.jsonl), so anything that pointed at them can follow the trail.
|
||||
Legacy aliases are recorded there too: `b-p01-text-003`, the ID an older tool gave the water sentence, still leads to `f929eq9`.
|
||||
The sentence about water is still `hca6dc5` after twelve corrections, the corrected cell is still `c20cna6`, and the sentence that lost its superscript is still `ec5jc93`.
|
||||
The retired IDs are recorded in [`ids.jsonl`](../fixtures/packages/hale2026harmonic-damb/ids.jsonl), so anything that pointed at them can follow the trail.
|
||||
Legacy aliases are recorded there too: `b-p01-text-003`, the ID an older tool gave the water sentence, still leads to `hca6dc5`.
|
||||
|
||||
A fresh conversion of the same input mints the same IDs.
|
||||
A reading by a different converter is aligned with this document sentence by sentence ([spec/05 §5.6](../spec/05-hashes.md), pinned by [vectors/alignment.jsonl](../vectors/alignment.jsonl)).
|
||||
@@ -116,13 +116,13 @@ The table shows how each anchor resolves at revision 12 ([vectors/resolve.jsonl]
|
||||
| The water sentence, through the alias `b-p01-text-003` | `current` | The alias leads to the same node. |
|
||||
| The cell that read `3l0.0` | `stale` | Its text changed and "3l0.0" is no longer in it; a person must look again. |
|
||||
| "treated as zero" | `reattached` | The sentence changed twice, and the quote occurs in it exactly once, so the anchor is re-pinned. |
|
||||
| "fitted constants" | `stale` | After the split, those words are in the new sentence `r1gacps`, not in `e4fkseg`. |
|
||||
| "least squares", in the half-sentence `e9rh611` | `moved` | That ID was merged into `eajq93t`, which still holds the quote. |
|
||||
| The paragraph `g44k023` | `moved` | Merged into `zyqr0yc`, which holds all of its sentences. |
|
||||
| "fitted constants" | `stale` | After the split, those words are in the new sentence `a4jrz2t`, not in `ec5jc93`. |
|
||||
| "least squares", in the half-sentence `ayea71r` | `moved` | That ID was merged into `xjns57z`, which still holds the quote. |
|
||||
| The paragraph `ncx2v4f` | `moved` | Merged into `gwzbj3s`, which holds all of its sentences. |
|
||||
| The running head | `stale` | Deleted, with no successor. |
|
||||
| The equation | `reattached` | Only its number changed. |
|
||||
| The figure | `reattached` | Only its label changed, by a `relabel`. |
|
||||
| The CT–HC cell `1.090` in the garbled row | `reattached` | Its row was deleted, and exactly one cell in Table 1 has the same row and column headers and the same text: the new `e0n07n0`. |
|
||||
| The CT–HC cell `1.090` in the garbled row | `reattached` | Its row was deleted, and exactly one cell in Table 1 has the same row and column headers and the same text: the new `b5gd0h1`. |
|
||||
| Section 2 | `changed-below`, drift high | The section itself is unchanged, and 53% of what it holds or held was touched. |
|
||||
| Table 1 | `changed-below`, drift high | Rows were replaced and added. |
|
||||
| The table body | `changed-below`, drift high | 75% of the cells it holds or held were touched. |
|
||||
@@ -141,23 +141,23 @@ See [comparison.md](comparison.md).
|
||||
A receipt is a signed record that a person compared a set of nodes with the page.
|
||||
It lifts a node to F3, the top fidelity level (F0 is one reading; F1, every doubtful place answered; F2, an independent witness agrees) ([spec/08](../spec/08-fidelity.md)).
|
||||
One signature covers a Merkle tree, a hash over everything reviewed in a sitting, built as in RFC 6962 (a Request for Comments standard; replaced by [RFC 9162](https://www.rfc-editor.org/rfc/rfc9162), which defines the same tree).
|
||||
The receipts are in [`receipts.jsonl`](../fixtures/packages/hale2026harmonic/receipts.jsonl), and the keys that signed them in [`roster.json`](../fixtures/packages/hale2026harmonic/roster.json).
|
||||
The receipts are in [`receipts.jsonl`](../fixtures/packages/hale2026harmonic-damb/receipts.jsonl), and the keys that signed them in [`roster.json`](../fixtures/packages/hale2026harmonic-damb/roster.json).
|
||||
|
||||
- **Before the corrections**, `ana` had signed receipt `c5cx7n4tga6y9` for three cells at revision 0.
|
||||
- **Before the corrections**, `ana` had signed receipt `npc15pn6675z5` for three cells at revision 0.
|
||||
Two of them never changed, so the receipt still counts for them.
|
||||
For the corrected cell it no longer counts, because the cell's hash moved on.
|
||||
- After the corrections, `ana` signs `eqgbbpwb6725v` at F3 for 23 nodes: the cells of Table 1's three data rows (15), its four column headers, the equation, and three sentences (`e4fkseg`, `f929eq9` and `sxgcc9h`, the one holding the equation).
|
||||
`ben` spot-checks it with receipt `h6yr2ddx6d3ef`.
|
||||
- After the corrections, `ana` signs `y2zw8ctg5s8dp` at F3 for 23 nodes: the cells of Table 1's three data rows (15), its four column headers, the equation, and three sentences (`ec5jc93`, `hca6dc5` and `rr20tev`, the one holding the equation).
|
||||
`ben` spot-checks it with receipt `mg18668nzxvap`.
|
||||
His signature, made with his separate spot-check key `ben-spot`, picks 10 nodes nobody could have chosen, and he finds no errors.
|
||||
- `ana` signs `zaxky8c1y12tc` for two sentences of section 1, including the new `r1gacps`.
|
||||
- `cara` signs `adtrpq41np7sd` for three sentences on page 2, then revokes it herself (`q0yg0w6ygwwxh`) because she had compared them with the crops of another paper.
|
||||
Those sentences fall back to their earlier levels, which is why `eajq93t` is still F0.
|
||||
- `ana` signs `gena76nkb6cnc` for the figure's three caption sentences. `ben`'s spot-check `tejzs01eqn2ym` finds a missing period in `nmm4md0`, which suspends the receipt until she reviews the sitting again and signs a new one.
|
||||
- `ana` signs `mvjmb94nz7rdh` for two sentences of section 1, including the new `a4jrz2t`.
|
||||
- `cara` signs `q465jpwz8afhe` for three sentences on page 2, then revokes it herself (`pcbhtpwvysgq6`) because she had compared them with the crops of another paper.
|
||||
Those sentences fall back to their earlier levels, which is why `xjns57z` is still F0.
|
||||
- `ana` signs `gx2s84z9jf9xv` for the figure's three caption sentences. `ben`'s spot-check `yt70fcc3qm0xb` finds a missing period in `caehhp6`, which suspends the receipt until she reviews the sitting again and signs a new one.
|
||||
|
||||
The document's report, in [`document.json`](../fixtures/packages/hale2026harmonic/document.json), counts the result over 45 leaves: 27 at F3, 13 at F2, 4 at F1 and 1 at F0.
|
||||
The 27 are the 23 nodes of `eqgbbpwb6725v`, the 2 of `zaxky8c1y12tc`, and 2 of the 3 caption sentences.
|
||||
The document's report, in [`document.json`](../fixtures/packages/hale2026harmonic-damb/document.json), counts the result over 45 leaves: 27 at F3, 13 at F2, 4 at F1 and 1 at F0.
|
||||
The 27 are the 23 nodes of `y2zw8ctg5s8dp`, the 2 of `mvjmb94nz7rdh`, and 2 of the 3 caption sentences.
|
||||
Those two still count because Ben's spot-check is itself a valid receipt, and he compared them with the page and found them right.
|
||||
The third, `nmm4md0`, stays at F2: a spot-check never raises a node it found in error ([spec/08 §8.2](../spec/08-fidelity.md#82-levels)).
|
||||
The third, `caehhp6`, stays at F2: a spot-check never raises a node it found in error ([spec/08 §8.2](../spec/08-fidelity.md#82-levels)).
|
||||
|
||||
## 7. Data out of the table
|
||||
|
||||
@@ -171,7 +171,7 @@ The reference checker, [`tools/check.py`](../tools/check.py), confirms each CSV
|
||||
## 8. Sharing without the text
|
||||
|
||||
The team wants to move its notes to another of its own machines without copying the paper there.
|
||||
[`hale2026harmonic.checks-only`](../fixtures/packages/hale2026harmonic.checks-only/) is the same document with every leaf's text withheld and every hash replaced by an HMAC under the team's key ([spec/12 §12.5](../spec/12-package.md), [spec/05 §5.8](../spec/05-hashes.md)).
|
||||
[`hale2026harmonic-damb.checks-only`](../fixtures/packages/hale2026harmonic-damb.checks-only/) is the same document with every leaf's text withheld and every hash replaced by an HMAC under the team's key ([spec/12 §12.5](../spec/12-package.md), [spec/05 §5.8](../spec/05-hashes.md)).
|
||||
An HMAC is a hash mixed with a secret key.
|
||||
Anyone can recover a plain hash of a short text such as "310.0" by hashing guesses; a keyed hash resists that.
|
||||
Corrections, receipts, the roster and the dataset are left out too.
|
||||
|
||||
Reference in new issue
Block a user