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
+17
-7
@@ -61,7 +61,10 @@ Its signing payload is the canonical JSON of `{"format": <the record's format>,
|
||||
| `ssh` | An SSH signature of the payload in the namespace `scienting`, armored. |
|
||||
| `gpg` | A detached, armored OpenPGP signature of the payload. |
|
||||
| `sigstore` | A Sigstore bundle for the payload, as JSON text. |
|
||||
| `cose` | Reserved: a COSE_Sign1 message over the payload, in Base64, as the IETF SCITT work shapes signed statements, so other transparency services can verify a receipt. No vector yet. |
|
||||
| `ml-dsa-65` | Reserved: an ML-DSA-65 signature (FIPS 204) of the payload, in Base64, for the time Ed25519 is no longer considered safe. No vector yet. |
|
||||
|
||||
A consumer treats a record signed with a scheme it does not implement as invalid, so a reserved scheme can be adopted by a new minor version without any format changing.
|
||||
`signature.key` is the signer's roster key and equals `by`.
|
||||
Self-hosted deployments sign with team keys or a private Sigstore deployment; the public Sigstore instance, whose log permanently records each signer's identity, is for public layers only.
|
||||
`signatures.jsonl` verifies one receipt and the RFC 8032 test vector.
|
||||
@@ -72,7 +75,12 @@ A receipt's items are `["node", id, node_hash]` for each listed node, in order,
|
||||
The root follows RFC 6962: a leaf hash is SHA-256 of the byte 0x00 and the item's canonical JSON; an interior hash is SHA-256 of 0x01 and the two child hashes; a list of *n* > 1 items splits after the largest power of two less than *n*.
|
||||
RFC 6962 has since been replaced by RFC 9162, which defines the same tree.
|
||||
A root over one item is that item's leaf hash; a receipt with no items is invalid.
|
||||
The tree allows a proof that one node was covered without revealing the others; `merkle.jsonl` gives roots for one to seven items.
|
||||
`merkle.jsonl` gives roots for one to seven items.
|
||||
|
||||
The tree allows a proof that one node was covered without revealing the others.
|
||||
An inclusion proof, `scienting-proof/1` with `kind` `inclusion`, holds the `receipt`, the `item`, its `index` among the receipt's items, the `size` of the tree, the `root` and the `path`: the audit path of RFC 9162 §2.1.3.1, as hex hashes, leaf-most first.
|
||||
A verifier recomputes the root from the item's leaf hash and the path as RFC 9162 §2.1.3.2 describes and compares it with the receipt's root.
|
||||
`proofs.jsonl` gives the path for every item of trees of one to seven items, and `fixtures/records/proof-inclusion.json` proves one node of the example paper's first receipt.
|
||||
|
||||
## 8.6 Receipt status
|
||||
|
||||
@@ -101,14 +109,16 @@ The roster is meant to be the one a store verifies against; see the [rationale](
|
||||
## 8.8 Spot-checks
|
||||
|
||||
For each F3 receipt, a second reviewer checks a sample of its nodes and signs a `qa:` receipt.
|
||||
A seed the reviewer controls could be ground, by varying the receipt until the sample falls on careful work, so the seed is a signature nobody can steer:
|
||||
A seed the reviewer controls could be ground, by varying the receipt until the sample falls on careful work, and a seed the checker controls could be ground by the checker, so the seed is a value neither can steer:
|
||||
|
||||
1. The seed is the checker's Ed25519 signature over the canonical JSON of `{"format": "scienting-spot-check/1", "receipt": <the original's record hash>}`, made with a key registered in the roster with the `spot-check` purpose before the original was signed.
|
||||
Ed25519 signatures are deterministic, so the checker cannot choose it, and the reviewer cannot predict it.
|
||||
1. The seed is the checker's VRF proof, under RFC 9381's ECVRF-EDWARDS25519-SHA512-TAI, for the input that is the canonical JSON of `{"format": "scienting-spot-check/1", "receipt": <the original's record hash>}`, made with an Ed25519 key registered in the roster with the `spot-check` purpose before the original was signed.
|
||||
A verifiable random function has exactly one valid proof for a key and an input, where an Ed25519 signature has many, so the checker cannot try proofs until the sample suits, and the reviewer cannot predict the proof without the checker's private key.
|
||||
2. The sample size is the smaller of *n* and the larger of `minimum` and ⌈`rate` × *n*⌉, for the original's *n* nodes; the defaults are a rate of `0.05` and a minimum of 10.
|
||||
3. Rank the original's node IDs by the hexadecimal SHA-256 of the seed's 64 bytes followed by the ID's UTF-8, then by ID, and take the first ones.
|
||||
3. Rank the original's node IDs by the hexadecimal SHA-256 of the proof's VRF output, the 64 bytes `ECVRF_proof_to_hash` gives, followed by the ID's UTF-8, then by ID, and take the first ones.
|
||||
|
||||
The `qa` object records `of`, `key`, `seed` (Base64), `rate`, `minimum`, the `sample` sorted by ID, and the `errors` found, each `{node, kind?, note?}`.
|
||||
The `qa` object records `of`, `key`, `seed` (the 80-byte proof, Base64), `rate`, `minimum`, the `sample` sorted by ID, and the `errors` found, each `{node, kind?, note?}`.
|
||||
A verifier checks the proof against the checker's public key and the input before it recomputes the sample.
|
||||
`vrf.jsonl` pins the VRF on the RFC's own example and on the example paper's seed.
|
||||
The `qa:` receipt's `nodes` are the sampled nodes at the hashes the original lists, its `scope` is `qa:` and the original's ID, every error names a sampled node, and it is signed at or after the original.
|
||||
Anyone can recompute the sample; `spotcheck.jsonl` does.
|
||||
The payload's identifier, `scienting-spot-check/1`, is registered with the other formats so that no file ever carries it by mistake; it names a signing payload, never a file.
|
||||
@@ -137,7 +147,7 @@ Most of a paper is never relied on, so a team proofreads the target set: the nod
|
||||
|
||||
The set is reported in document order, each node with the first reason that applies: `anchored`, `equation` or `digits`.
|
||||
Neighbors shown for context are not in the set, and nothing new comes to rest on a node below F3 without the node joining the set.
|
||||
`targets.jsonl` gives the set of `fixtures/packages/hale2026harmonic` under each digit rule.
|
||||
`targets.jsonl` gives the set of `fixtures/packages/hale2026harmonic-damb` under each digit rule.
|
||||
|
||||
## 8.11 Checklists
|
||||
|
||||
|
||||
Reference in new issue
Block a user