Repository navigation
fix(release): sign every release artifact with minisign; verify in installer and self-update - #186
Merged
Merged
Conversation
…he installer and self-update The installer shipped a placeholder key (RWQ_REPLACE_ME...) and releases had no .minisig files, so downloads were never verified. Now: - minisign.pub (key id 01A1B54137EC392A) committed at the repo root. - release.yml: the publish job installs a sha256-pinned minisign 0.12, checks each artifact against its .sha256, signs it with the production key (secrets MINISIGN_SECRET_KEY + MINISIGN_PASSWORD), trusted comment "timestamp:<t> version:<tag> file:<artifact>", verifies against the committed key, uploads <artifact>.minisig, and refuses to publish a release missing any signature. - install.sh: real public key; fails closed on a missing signature, failed verification, or a trusted comment naming another file/release (C0MPUTE_ALLOW_UNSIGNED=1 is the explicit escape hatch). Without minisign installed it fetches the pinned upstream minisign 0.12 release, checked by a hard-coded sha256, into the temp dir (no root). - c0mpute-update: self-update fetches <artifact>.minisig and verifies it with the compiled-in key (minisign-verify crate, pure Rust) after the sha256 check and before swapping; no signature, no update. Unit tests with a test keypair cover good, tampered, wrong key, wrong file/release, garbage sig, and that RELEASE_PUBKEY matches minisign.pub. - SECURITY.md + README document the key and how to verify by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
ThreatCrush Security Scan13 finding(s) HIGH/CRITICAL: 1 | MEDIUM: 8 | LOW: 4
Snippets are redacted; ThreatCrush never prints matched credential material. |
…v literal gitleaks' generic-api-key rule flagged the public key in a workflow env var. The publish job now sparse-checks-out minisign.pub and verifies against it; .gitleaksignore covers the earlier branch commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 9, 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.
Fixes the gap reported on r/LocalLLM:
scripts/install.shcarried a placeholder minisign key (RWQ_REPLACE_ME_WITH_PROD_MINISIGN_PUBKEY) and releases had no.minisigfiles, so the downloaded binary was never verified.What changes
minisign.pubat the repo root: production keyRWQqOew3QbWhAWH36TA5roreh18RgRqgozSwhE3yZtdza7eeLKmbDbJM(key id01A1B54137EC392A). The secret key is password-protected and stored in the team vaultc0mpute--signingand in the repo secretsMINISIGN_SECRET_KEY+MINISIGN_PASSWORD.release.yml: thepublishjob installs minisign 0.12 (pinned by sha256), checks each artifact against its.sha256, signs it with trusted commenttimestamp:<t> version:<tag> file:<artifact>, verifies it against the committed key, uploads<artifact>.minisig, and refuses to publish if any signature is missing. The secrets are only exposed to that one job.scripts/install.sh(signature section only): the real key. It fails closed when the signature is missing, fails to verify, or names another file or release in its trusted comment.C0MPUTE_ALLOW_UNSIGNED=1is the explicit escape hatch. Ifminisignis not installed, the installer downloads the pinned upstream minisign 0.12 release into the temp dir and checks it against a hard-coded sha256 (Linux x86_64/aarch64, macOS arm64; no root). On Intel macs it asks forbrew install minisign.c0mpute-update): this closes the auto-update path too. Updates fetch<artifact>.minisigand verify it against the compiled-inRELEASE_PUBKEYusing the pure-Rustminisign-verify0.3. That runs after the sha256 check and before the binary swap; no signature means no update. Unit tests use a test keypair and cover: a good signature passes; tampered bytes, the wrong key, the wrong file or release and a garbage or missing signature all fail; andRELEASE_PUBKEYequalsminisign.pub. The key is a const, notinclude_str!, becauseDockerfile.webcopies onlynode/into the Rust stage.SECURITY.md(new) and the README document the key and how to verify a download by hand.Backfill
Releases v0.2.83 through v0.2.88 (current Latest) now have
.minisigfiles for all 5 artifacts. That covers the installer's walk-back window, so the fail-closed installer works the moment this deploys.Tests (run locally against the real releases, pinned-minisign path and system-minisign path)
latestvia c0mpute.com (v0.2.88)v0.2.86signature verification FAILED, exit 1signs a different file, exit 1not for release v0.2.87, exit 1.minisigrefusing to install an unverified binary, exit 1The Rust unit tests run in CI (
build + test). This box has no C toolchain.How it reaches production
c0mpute.com/install.shreadsscripts/install.shfrom the deployed checkout at request time (apps/web/src/app/install.sh/route.ts, cached for 5 min), so the Deploy to dev2 workflow on merge ships the installer change. The release workflow change applies to tags cut after the merge. The self-update check ships in the next release binary.🤖 Generated with Claude Code