Skip to content

ashlar verify: CERTIFIED with a real signature via the instance ledger (SPEC-003 wiring) - #390

Merged
IanFrelinger merged 1 commit into
masterfrom
claude/verify-certified
Aug 24, 2026
Merged

ashlar verify: CERTIFIED with a real signature via the instance ledger (SPEC-003 wiring)#390
IanFrelinger merged 1 commit into
masterfrom
claude/verify-certified

Conversation

@IanFrelinger

Copy link
Copy Markdown
Owner

ashlar verify now says CERTIFIED with a real signature (SPEC-003 wiring)

The payoff of the signing arc: ashlar verify no longer prints unsigned when a key exists — it
writes 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

  • provenance course (ProjectVerifier) — joins the run ONLY once a signed ledger exists, and
    checks that history's whole chain via InstanceLedger.VerifyChain, fail-closed: a tampered
    ledger fails verification (exit 65), so a run over 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 verifier
    runs its courses synchronously. The async API and its 11 kernel tests are unchanged.
  • VerifyCommand — after a passing verify: with an operator key, append a signed verification
    entry (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

  • e2e-loop 70/70 (real CLI binary, fresh process each; was 65, +5): verify certifies with a
    key (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-project scenario still prints VERIFIED · unsigned with three courses.
  • Kernel 360/360, including new ProjectVerifier provenance tests (clean ledger → passing
    course; corrupt ledger → failed verification) and the unchanged ledger + signing suites.
  • CLI builds clean; public-API snapshots unaffected.

Honest scope (unchanged from the ledger kernel)

Tail truncation isn't detectable without an external anchor (v2; guarded by the truncate_ledger
never-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 provenance course and
sync ledger API live in src/ (the verifier and kernel), and their only consumer, the CERTIFIED
display, lives in application/ (VerifyCommand). Splitting them would ship a course no verb calls
and 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.

…-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>
@IanFrelinger
IanFrelinger merged commit d55fa54 into masterAug 24, 2026
18 checks passed
@IanFrelinger
IanFrelinger deleted the claude/verify-certified branch August 24, 2026 04:22
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>
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