Repository navigation
CORE (signing sitting): chain checkpoints and inclusion proofs - #89
Merged
Merged
Conversation
WO-CARTRIDGE-SEAL part B, queued as a patch. Nothing under src/ changes on this branch. - proposals/chain-checkpoints.patch, generated by proposals/make-chain-checkpoints-patch.mjs (git diff --no-index over temp copies; never typed). Adds src/chain/merkle.js (RFC 9162 hashing, inclusion and consistency proofs), src/chain/checkpoint.js (C2SP signed-note and tlog-checkpoint, pure verifiers), src/store/checkpoints.js (sidecar receipts/checkpoints.jsonl, segment sealing every 1024 entries, incremental and full sealed verify, proofs), verifyRange in src/chain/index.js (verifyChain becomes verifyRange from genesis, results unchanged), and seal/verifyIncremental/verifySealed/proveInclusion/proveConsistency on the store. chain.jsonl is never rewritten. - proposals/chain-checkpoints/: the new files' sources, the N measurement script. proposals/chain-checkpoints.md: design, preflight, measurement, sitting steps, draft KNOWN-LIMITS entries. - test/chain-checkpoints.test.js: rehearsal. Live cases are TODO and fail until the patch lands; the queued-patch block applies it in a temp repo and runs every case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WO-CARTRIDGE-SEAL part B. CORE, queued for the signing sitting. Do not merge as a code change: nothing under
src/changes on this branch. The core change isproposals/chain-checkpoints.patch, generated bynode proposals/make-chain-checkpoints-patch.mjs(temp copies plusgit diff --no-index; never typed, per KNOWN-LIMITS 80). Design, preflight, measurement, sitting steps and draft limits:proposals/chain-checkpoints.md.What the patch does
src/chain/merkle.js(new): RFC 9162 section 2.1 hashing, inclusion proofs (2.1.3) and consistency proofs (2.1.4), plus a compact-range frontier so a tree extends without its leaves. Node builtins only.src/chain/checkpoint.js(new, pure): C2SPsigned-note(Ed25519, type 0x01, key ID per spec) and C2SPtlog-checkpoint(origin, size, base64 root; no extension lines).verifyInclusionProof(proof, publicKey)andverifyConsistencyProof(proof, publicKey)need only the proof and the chain's public key.src/store/checkpoints.js(new): sidecarreceipts/checkpoints.jsonl;chain.jsonlis never rewritten. Segment sealing every 1024 entries, one lock acquisition per segment. Optional head checkpoint (seal({ atHead: true })) so the newest receipt is provable now. Incremental verify from the newest checkpoint or from one the caller trusts. Full sealed verify. Proofs.src/chain/index.js:verifyRange(entries, key, { startSeq, prevHash });verifyChainbecomesverifyRangefrom genesis with identical results (asserted against today's liveverifyChainon seven chains).src/store/index.js:seal,verifyIncremental,verifySealed,proveInclusion,proveConsistencyon the store.Leaves are the existing entry hashes. Migration of an existing chain is the first
seal(): the first checkpoint covers seq 0..1023 of the file as it is.U+2014 note. The C2SP signature line begins with U+2014, which this repo bans. The code builds it with
String.fromCharCode(0x2014); nothing here contains the byte, and the rehearsal asserts the patch carries none. The format is the spec's, unchanged.Fail-first tests (
test/chain-checkpoints.test.js)Same shape as
test/chain-seq-continuity.test.js: the live block runs every case as TODO until the patch lands (landing tripwire:src/chain/merkle.jsexists); the queued block applies the patch in a temp repo and runs every case for real.seal). Control in the same case, true on main: today'sverifyChainreturns{ ok: true }on the rewritten chainverifySealedand a walk from an earlier trusted checkpoint reporthash mismatch at entry 10; from the newest checkpoint it is out of sight (documented, asserted)verifyCheckpoint,verifyIncremental,verifySealed;sealandproveInclusionrefuseSEGMENT_SIZEundefined, before building the fixture)chain.jsonlSHA-256 unchanged, all 40,000 entry hashes reproduced, 39 checkpoints, first covers 0..1023, full and incremental verify ok, proofs verify at seq 0, 1, 1023, 1024, 20000, 39935How checked: (1) branch run, live block 13 TODO, all failing with the errors above; (2) the same test file on a clean clone of
337358fwith no patch file: 13 TODO, 15 cancelled, exit 1 (loud); (3) patch applied in a throwaway clone: the live block runs all 13 cases un-todo'd and passes.Counts
git apply --check proposals/chain-checkpoints.patchon337358f: clean.337358f: 1379 tests, 1374 pass, 0 fail, 5 skipped.npm run limits-pin -- --stampas the sitting would: 1393 tests, 1387 pass, 0 fail, 6 skipped, 0 todo.matcher-history"temp dirs prefixed lotor-mh- are cleaned up" on main too (it counts the shared system temp dir). Every count above is from a run alone.bash .github/scripts/pr-guard.sh origin/main HEAD false):guard: clean.N and its measurement
node proposals/chain-checkpoints/measure-n.mjs, 20,000 entries, three runs: verifyChain ~80 us/entry, sign ~34, Merkle hashing ~2.9, parse ~0.75. N bounds the work an incremental verify does past the newest checkpoint. N = 1024 (power of two, so segments are perfect subtrees) puts the worst case at ~82 ms; a 40k full verify is ~3.2 s.Byte budget
The sealed segment adds nothing to a cartridge by itself. Measured on the 40k rehearsal chain for the non-core follow-on, which is NOT in this PR (the cartridge carrying proofs): path 16 hashes; signed note 219 B; compact binary proof 616 B for the first and 516 B for each further proof against one checkpoint. Against the 2,808 B useful PAP payload that is at most 5 proofs with an empty spine, about 3 next to a 1 KB compressed spine, before sealed-mode overhead.
Sitting
Apply with each target on the command line (KNOWN-LIMITS 82), one signature for one command:
then
npm run limits-pin -- --stamp,npm test, and append the draft limits fromproposals/chain-checkpoints.mdwith the next free numbers.Doubts and shortcuts
verifySealedpasses. No cosigner, no anchor (OpenTimestamps out of scope).seal(). Wiring it into the session-end hook (core) orpap-export(not core) is a separate decision. Sealing in the append path was rejected on purpose.proveInclusionandproveConsistencyread every entry the checkpoint covers (O(n), on demand). Not measured separately.loadChaindrops it silently (stricter, recorded in the proposal).proposals/chain-checkpoints.md, to be numbered when it lands, so they don't collide with the open part A PR.NEXT-SIGNING-SITTING.mddoes not exist in the repo, so no entry was added there.🤖 Generated with Claude Code