JuliusBrussee/caveman · critical
binary signing private key does not match committed public k
Error message
binary signing private key does not match committed public key
What it means
Thrown by scripts/sign-binary-checksums.mjs when the private key from CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM derives a different public key than the committed public-key.pem passed on the command line. Both are normalized to canonical SPKI PEM before comparing, so formatting differences cannot cause a false mismatch — a hit means the key pair genuinely differs. This prevents signing binaries with a key the published verification flow does not trust.
Source
Thrown at scripts/sign-binary-checksums.mjs:49
);
}
function normalizePublicKey(value) {
return createPublicKey(value).export({ type: "spki", format: "pem" }).toString();
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
try {
const [checksumsPath, outputPath, publicKeyPath] = process.argv.slice(2);
const privateKeyPEM = process.env.CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM;
if (!checksumsPath || !outputPath || !publicKeyPath) {
throw new Error("usage: sign-binary-checksums.mjs <checksums.txt> <output.keysig> <public-key.pem>");
}
if (!privateKeyPEM) throw new Error("CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM is required");
const checksums = readFileSync(checksumsPath);
const publicKeyPEM = readFileSync(publicKeyPath, "utf8");
if (normalizePublicKey(privateKeyPEM) !== normalizePublicKey(publicKeyPEM)) {
throw new Error("binary signing private key does not match committed public key");
}
const bundle = checksumSignatureBundle(checksums, privateKeyPEM);
if (!verifyChecksumSignatureBundle(checksums, bundle, publicKeyPEM)) {
throw new Error("generated checksum signature failed local verification");
}
writeFileSync(outputPath, `${JSON.stringify(bundle)}\n`, { mode: 0o600 });
} catch (error) {
process.stderr.write(`${error.message}\n`);
process.exit(1);
}
}
View on GitHub (pinned to 27d5a3981a)
Solutions
- Export the private key that corresponds to the committed public-key.pem and re-run.
- If the rotation is intentional, generate the new key pair, commit the new public-key.pem, and update the CI secret to match, then re-run.
- Verify the pair locally: `diff <(openssl pkey -in priv.pem -pubout) keys/binary-public.pem`.
Example fix
# before: env holds rotated private key, repo holds old public key CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM="$(cat new-key.pem)" node scripts/sign-binary-checksums.mjs ... # after: commit the matching public key first git add keys/binary-public.pem && git commit -m 'rotate binary signing key' CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM="$(cat new-key.pem)" node scripts/sign-binary-checksums.mjs dist/checksums.txt dist/checksums.keysig keys/binary-public.pem
Defensive patterns
Strategy: validation
Validate before calling
# Verify the key pair matches BEFORE running the signer.
if [ "$(openssl pkey -in "$PRIV_PEM" -pubout | openssl pkey -pubin -outform DER | sha256sum)" != \
"$(openssl pkey -pubin -in "$PUB_PEM" -outform DER | sha256sum)" ]; then
echo "signing key does not match committed public key" >&2; exit 1
fi Try / catch
try { signRelease(); } catch (e) { if (/does not match committed public key/.test(e.message)) haltRotation(); else throw e; } Prevention
- Rotate keys as an atomic change: new private key secret + committed public key in the same release.
- Keep a local diff check between openssl-derived and committed public key in the release runbook.
When it happens
Trigger: Using a newly generated or wrong private key while the repo still carries the old committed public key; rotating the key pair but updating only one side; pointing the third argument at a stale copy of the public key.
Common situations: Post-rotation release where the new private key is in the environment but the old public-key.pem is still committed; multiple signing keys in an org and the wrong one configured; CI secret updated ahead of the repo.
Related errors
- generated checksum signature failed local verification
- usage: sign-binary-checksums.mjs <checksums.txt> <output.key
- CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM is required
- cave_mastra_terminal_failure
- cave_harness_incomplete_evidence
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/f2d98dda638010d8.
Report an issue: GitHub.