Skip to content

Instance-ledger kernel: signed, hash-chained, append-only history (SPEC-003 v1) - #389

Merged
IanFrelinger merged 1 commit into
masterfrom
claude/instance-ledger
Aug 24, 2026
Merged

Instance-ledger kernel: signed, hash-chained, append-only history (SPEC-003 v1)#389
IanFrelinger merged 1 commit into
masterfrom
claude/instance-ledger

Conversation

@IanFrelinger

Copy link
Copy Markdown
Owner

The kernel that will let ashlar verify say CERTIFIED with a real signature instead of VERIFIED · unsigned. An append-only, hash-chained, signed history under .ashlar/ledger/ — the read side of the same trust model the gate store (#386#388) writes.

What landed (src/ + docs/ only — kernel-first, clean layer gate)

  • InstanceLedger — one Ed25519-signed JSON entry per verification, each carrying Prev = the hash of its predecessor's canonical bytes. VerifyChain checks contiguous sequence, every signature, and every link, and throws (fail-closed) on any break; append verifies the existing chain before extending it, so a fresh entry can't bury a broken one. Verification is intrinsic — a keyless reader validates the whole chain.
  • Reuses the gate store's proven shapes: a FileShare.None cross-process append lock, temp-then-rename writes, fail-closed reads, and the SPEC-006 CanonicalJson + Ed25519 machinery.
  • docs/InstanceLedger.md — the on-disk format and an honest integrity statement.

Honest v1 scope (documented, not pretended)

  • Tail truncation isn't detectable without an external anchor — v2; guarded meanwhile by the policy's truncate_ledger never-entry.
  • The signer is the local operator key, not an org trust root — v2 (SPEC-006). The signer is deliberately not pinned across entries, so a key rotation is legitimate.

Review + verification

Adversarially reviewed across four lenses (chain integrity, canonicalization/hash determinism, append race, fail-open) — zero confirmed bugs. Applied its refuted-but-worthwhile hardening: collision-proof Subject (hash each document then combine), numeric file ordering with a foreign-file fail-close, a File.Move collision folded into the fail-closed contract, and stray-temp sweeping under the lock. 11 ledger tests (genesis, chaining, tamper/gap/reorder/foreign-file refusal, keyless read); kernel signing tests unaffected.

Next

The wiring slice: a provenance course in ProjectVerifier that runs VerifyChain, verify appending a signed entry on success, and the CERTIFIED display in VerifyCommand.

…tory (SPEC-003 v1)
The record that will let `ashlar verify` say CERTIFIED with a real signature instead of
VERIFIED · unsigned. An append-only, hash-chained, signed history under .ashlar/ledger/, one
JSON file per entry: each is Ed25519-signed (SPEC-006 keys) and carries the hash of its
predecessor, so modifying, inserting, or reordering the past is detected and refused loudly —
the same fail-closed stance as the gate store, on the same principle that a forged history is
worse than a missing one. Verification is intrinsic (each entry carries its own key), so a
keyless reader still validates the whole chain; append verifies the existing chain BEFORE
extending it, so a fresh entry cannot bury a broken one.
Honest about v1 scope: tail truncation is not detectable without an external anchor (v2;
guarded meanwhile by the policy's truncate_ledger never-entry), and the signer is the local
operator key, not an org trust root (v2). docs/InstanceLedger.md states both plainly.
Reuses the gate store's proven shapes — a FileShare.None cross-process append lock, temp-then-
rename writes, fail-closed reads — and the same CanonicalJson + Ed25519 machinery.
Adversarially reviewed across four lenses (chain integrity, canonicalization/hash determinism,
append race, fail-open); zero confirmed bugs. Applied its refuted-but-worthwhile hardening:
collision-proof Subject (hash each document then combine, no delimiter assumption), numeric
file ordering with a foreign-file fail-close, a File.Move collision normalised into the
fail-closed contract, and stray-temp sweeping under the lock. Kernel signing tests unaffected;
11 new ledger tests (genesis, chaining, tamper/gap/reorder/foreign-file refusal, keyless read).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@IanFrelinger
IanFrelinger merged commit 2443ad9 into masterAug 24, 2026
10 checks passed
@IanFrelinger
IanFrelinger deleted the claude/instance-ledger branch August 24, 2026 03:56
IanFrelinger added a commit that referenced this pull request Aug 24, 2026
…-003 wiring) (#390)
The payoff of the signing arc. `ashlar verify` no longer prints "unsigned" when a key exists —
it appends a signed entry to the instance ledger (#389) and renders CERTIFIED · signed
ed25519:… · ledger #N. The trust model becomes a concrete, visible artifact.
- provenance course (ProjectVerifier): joins the run only once a signed ledger exists and
checks that chain via InstanceLedger.VerifyChain, fail-closed — a tampered ledger fails
verification (exit 65), so `run` over a forged history is refused too. A keyless project
stays at three courses and reads unsigned; zero-setup is untouched.
- InstanceLedger.VerifyChain(): a synchronous form, since the verifier runs courses
synchronously. The async API and its 11 kernel tests are unchanged.
- VerifyCommand: async; on a passing verify with a key present, append a signed verification
entry (subject = hash of the exact documents) and render CERTIFIED with the fingerprint and
sequence; no key -> VERIFIED unsigned as before; corrupt key -> fail closed (exit 1); a
ledger that turns corrupt between the provenance course and the append -> refused (exit 65).
e2e-loop 70/70 (real binary): certifies with a key, writes a signed entry, second run adds
provenance and extends to #2, tampered ledger refused at 65 — keyless verify still unsigned at
three courses. Kernel 360/360 incl. new provenance tests. Adversarially reviewed (fail-closed +
keyless-regression lenses); zero confirmed findings.
Co-authored-by: PlzTouchGrass <130718671+PlzTouchGrass@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@IanFrelinger@PlzTouchGrass