feat(release-artifact): verify at pin time, mirror offline, and lock what was verified - #267
Open
Alan-TheGentleman wants to merge 5 commits into
Open
Conversation
… release artifacts Implements design D1 steps 1-5 (the network/pin-time trust boundary): a from-scratch Ed25519/minisign wire-format verifier (node:crypto only, no new dependency), a checksums.txt line matcher, and a trusted-comment repo/tag binder, orchestrated by syncGentleAiRelease() which delegates D1 steps 6-11 and D2 extraction to lib/release-artifact.ts. A local --bootstrap-archive skips network and signature verification entirely and stamps every result development/bootstrap, matching the S-vs-R evidence ledger: assertReleaseAcceptanceEvidence throws on a bootstrap-sourced result, so it can never be relabeled release evidence. The network path fails closed with zero network activity while the trusted minisign public key is the pending sentinel (mirroring the existing GENTLE_AI_PENDING_DIGEST pattern) — gentle-ai has not yet published a signed release under this contract.
Writes the checked-in mirror files this unit introduces and the
canonical lock, produced by running scripts/sync-gentle-ai-release.mjs
--write --bootstrap-archive against a locally-built archive:
- contracts/release-artifact/v1/schemas/artifact-manifest.schema.json
- capabilities/review-integration-v2.semantic.json
- docs/gentle-ai/review-integration.md
- capabilities/gentle-ai-release.lock.json (canonical LF, 2-space,
path-sorted entries/generated, one trailing LF; release.version
asserted equal to INSTALLER_VERSION)
No gentle-ai release exists yet under the release-artifact contract
(tracked separately, out of scope here), so this lock is
development/bootstrap evidence, not release evidence: it pins today's
already-correct mirror bytes and the mechanism, and must be
regenerated by a real signed-release sync once one is published.
The existing contracts/review-integration/{v1,v2}/** files are
untouched byte-for-byte (git status shows zero diff on any
previously-tracked file).
…ot a hand-maintained map Deletes the ~60-entry hand-maintained contractHashes map. Extends the existing reconcileContractsOnDisk pattern (same exported name and 2-argument signature) to walk every full-mirror directory (contracts/, docs/gentle-ai/, capabilities/ minus the lock itself and the future hand-authored capabilities/native-cli-history.json) and compare against digests read from capabilities/gentle-ai-release.lock.json via the new mirrorDigestsFromLock/mirrorDigestDrift helpers. Each of the three failure modes (unlisted-on-disk, listed-but-missing, digest drift) names the exact file. assertLockReleaseVersionPin gates the lock's release.version against the authoritative INSTALLER_VERSION pin. Also fixes a gap surfaced by actually running this script end-to-end for the first time since P1a landed: lib/release-artifact.ts and runtime/release-artifact.mjs were missing from requiredPaths and the generated-runtime reconciliation. Adds capabilities/ and scripts/sync-gentle-ai-release.mjs to the required/packaged set, and notes in ci.yml that the existing "Verify package contents" step now performs this reconciliation fully offline.
The evidence class was carried only on the in-memory sync result, so the lock that actually gets checked in, reviewed and later trusted carried no indication of where it came from. That is the failure mode the spec exists to prevent. A bootstrap-derived lock has a placeholder release.commit and an archive.digestSource reading signed-checksums.txt, which any reader would take for a real signed release. Nothing in the file said otherwise. The lock now records evidence.class and evidence.signatureStatus, so a reader holding only the file on disk can still refuse it as pin or final-acceptance evidence. The checked-in lock is relabelled accordingly.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This was referenced Aug 1, 2026
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.
Second slice of the consumer chain, stacked on #266. Owns trust-order steps 1 through 5, the network boundary; #266 owns steps 6 through 11.
Where the signature check actually belongs
scripts/gentle-ai-installer.mjsdocuments that the pinned sha256 literals were derived by a human from the signedchecksums.txt, and install compares against that pin. So minisign verification and repo/tag binding belong to the pin-time sync job; install time inherits that trust through the lock.Shipping a verifier and a trusted key to every end user would be a larger new trust surface for no gain, and it was rejected in design rather than skipped by accident.
The lock is what makes every PR check offline
It binds release identity, contract major, archive digest, tree digest, canonical entries and generated-output digests. Only an explicit pin-bump job downloads anything.
scripts/verify-package-files.mjsloses its hand-maintained ~60-entrycontractHashesmap and reconciles against the lock instead. Three failure modes each fail naming the exact file: a mirror on disk the lock does not list, a lock entry with no file, and a drifted digest.The offline claim is proven, not asserted: the trusted key is a pending sentinel that throws before any network call, and
verify-package-files.mjs --checkpasses reading only the lock and local files.A defect this slice found and fixed in the previous one
Running the script end to end surfaced that
lib/release-artifact.tsand its generated runtime were missing fromrequiredPathsin the package check. Fixed here rather than left for someone to trip over.And a defect in this slice, caught in review
The evidence class was carried only on the in-memory sync result. The lock that actually gets checked in, reviewed and later trusted carried no indication of where it came from.
That is precisely the failure mode the spec exists to prevent. A bootstrap-derived lock has a placeholder
release.commitand anarchive.digestSourcereadingsigned-checksums.txt— any reader would take it for a real signed release, and nothing in the file said otherwise.The lock now persists
evidence.classandevidence.signatureStatus, so a reader holding only the file can still refuse it as pin or final-acceptance evidence. Two tests cover it, including one asserting the checked-in lock itself is labelled bootstrap. That fix is its own commit.Status of the checked-in lock
It is
development/bootstrapevidence and says so. No gentle-ai release exists under this contract yet, so it must be regenerated by a real network--writerun once the provider release lands and the real minisign key is provisioned.Tests
1067 pass, 3 fail. Those three are the known receipt-driven-development-disabled failures in
tests/native-review-parity-runtime.test.ts, verified identical on cleanmainbefore this chain started. No regressions.One interpretation flagged
The minisign trusted-comment repo and tag convention was defined here because design did not specify an exact wire format for this boundary. It is tested, but it is an interpretation and should be confirmed against the provider's actual signing output before the first real sync.