ashlar verify: CERTIFIED with a real signature via the instance ledger (SPEC-003 wiring) - #390
Merged
Merged
Conversation
…-003 wiring) 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: Claude Opus 4.8 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 24, 2026
IanFrelinger added a commit
that referenced
this pull request
Aug 24, 2026
…ndle (mesh slice 3) (#394) Turn a certified project into something you can hand someone: the project staged beside a self-contained runtime and a launcher that VERIFIES before it runs. Download, unzip, run — offline, no install, and it proves its own certification on launch. - `ashlar export native --path <proj> --out <dir> [--rid <rid>] [--zip]` refuses to export a project that does not verify, stages app/ (contract, operator-owned policy, signed ledger, bricks), writes run.sh/run.cmd (verify-then-run), bundle.json, and a README. With --runtime it publishes a self-contained single-file runtime beside the app; the staging is deterministic and unit-tested, the publish is a separate step that degrades gracefully (RUNTIME.md) when the CLI is not publish-clean. - The staged app self-verifies OFFLINE with no origin key: the provenance course confirms the signed ledger chain intrinsically — the download proving its own certification. Fixes a real integrity hole a review surfaced (present since the ledger landed, #390), HIGH: the ledger's Subject (hash of the certified documents) was WRITE-ONLY — the provenance course checked chain integrity but never that the ledger covered the CURRENT documents. So an edited ashlar.yaml/ashlar.policy.yaml with an intact ledger passed as CERTIFIED, and a tampered downloaded bundle would have run. Now: - the provenance course FAILS unless the ledger head attests the current documents; - a keyed `verify` re-certifies (appends an entry covering the current documents, then the head matches) so the normal edit→verify loop still works; - a keyless `verify` of altered documents fails closed (exit 65) — the launcher aborts, the tampered app never runs; - export's "certified" and bundle.json require the head to cover the staged documents. Kernel 394/394 (incl. edit-after-certification fails provenance); CLI 18/18; e2e 90/90 with a full export: certified bundle staged, launcher self-proves, a keyless downloader confirms the chain offline, a tampered contract is refused, unverified projects refused. Also flags (as a follow-up task) that the product CLI references three test projects, which blocks the self-contained single-file publish (NETSDK1191). Co-authored-by: PlzTouchGrass <130718671+PlzTouchGrass@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <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 freeto 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.
ashlar verifynow says CERTIFIED with a real signature (SPEC-003 wiring)The payoff of the signing arc:
ashlar verifyno longer prints unsigned when a key exists — itwrites a signed entry to the instance ledger (#389) and renders CERTIFIED · signed ed25519:… ·
ledger #N. The abstract "trust model" becomes a concrete, screenshot-able artifact.
What changed
provenancecourse (ProjectVerifier) — joins the run ONLY once a signed ledger exists, andchecks that history's whole chain via
InstanceLedger.VerifyChain, fail-closed: a tamperedledger fails verification (exit 65), so a
runover a forged history is refused too. A keyless,never-certified project stays at three courses and reads unsigned — zero-setup is untouched.
InstanceLedger.VerifyChain()— a synchronous form of the chain check, because the verifierruns its courses synchronously. The async API and its 11 kernel tests are unchanged.
VerifyCommand— after a passing verify: with an operator key, append a signedverificationentry (subject = hash of the exact documents) and render CERTIFIED with the fingerprint and
sequence; with no key, render VERIFIED · unsigned as before; with a corrupt key, fail closed
(exit 1). A ledger that turns corrupt between the provenance course and the append is refused
(exit 65), never certified onto a broken history.
Verification
verifycertifies with akey (CERTIFIED · ed25519 · ledger feat(policy): add Policy Pack v0.1 + config hygiene #1), writes a signed entry, a second run adds the provenance
course and extends to feat(policy): complete Policy Pack v0.1 with enhanced schemas and CI #2, and a tampered ledger is refused at exit 65 — while the keyless
verify-fresh-projectscenario still prints VERIFIED · unsigned with three courses.ProjectVerifierprovenance tests (clean ledger → passingcourse; corrupt ledger → failed verification) and the unchanged ledger + signing suites.
Honest scope (unchanged from the ledger kernel)
Tail truncation isn't detectable without an external anchor (v2; guarded by the
truncate_ledgernever-entry), and the signer is the local operator key, not an org trust root (v2).
[coordinated-integration]
Rationale for the layer-boundary hatch: this is one inseparable slice — the
provenancecourse andsync ledger API live in
src/(the verifier and kernel), and their only consumer, the CERTIFIEDdisplay, lives in
application/(VerifyCommand). Splitting them would ship a course no verb callsand a display with nothing to render. The application verbs it joins already live on master, and
cert-gate(the sole required check) gates the merge.