{"record":{"id":"1bc5668e994fe446","repo":"JuliusBrussee/caveman","slug":"caveman-binary-signing-private-key-pem-is-required","errorCode":null,"errorMessage":"CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM is required","messagePattern":"CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/sign-binary-checksums.mjs","lineNumber":45,"sourceCode":"      \"sha256\",\n      checksums,\n      createPublicKey(publicKeyPEM),\n      Buffer.from(bundle?.messageSignature?.signature ?? \"\", \"base64\"),\n    );\n}\n\nfunction normalizePublicKey(value) {\n  return createPublicKey(value).export({ type: \"spki\", format: \"pem\" }).toString();\n}\n\nif (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {\n  try {\n    const [checksumsPath, outputPath, publicKeyPath] = process.argv.slice(2);\n    const privateKeyPEM = process.env.CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM;\n    if (!checksumsPath || !outputPath || !publicKeyPath) {\n      throw new Error(\"usage: sign-binary-checksums.mjs <checksums.txt> <output.keysig> <public-key.pem>\");\n    }\n    if (!privateKeyPEM) throw new Error(\"CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM is required\");\n    const checksums = readFileSync(checksumsPath);\n    const publicKeyPEM = readFileSync(publicKeyPath, \"utf8\");\n    if (normalizePublicKey(privateKeyPEM) !== normalizePublicKey(publicKeyPEM)) {\n      throw new Error(\"binary signing private key does not match committed public key\");\n    }\n    const bundle = checksumSignatureBundle(checksums, privateKeyPEM);\n    if (!verifyChecksumSignatureBundle(checksums, bundle, publicKeyPEM)) {\n      throw new Error(\"generated checksum signature failed local verification\");\n    }\n    writeFileSync(outputPath, `${JSON.stringify(bundle)}\\n`, { mode: 0o600 });\n  } catch (error) {\n    process.stderr.write(`${error.message}\\n`);\n    process.exit(1);\n  }\n}\n","sourceCodeStart":27,"sourceCodeEnd":61,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/scripts/sign-binary-checksums.mjs#L27-L61","documentation":"Thrown by scripts/sign-binary-checksums.mjs when the environment variable CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM is unset or empty. The script takes the public key from the command line but the private key only from the environment, so the secret never appears in shell history or process arguments. This check runs after argument validation and before any file is read.","triggerScenarios":"Running the signer in a shell or CI job where CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM was never exported, was exported empty, or was lost when switching agents/machines.","commonSituations":"New release machine or CI runner without the secret configured; signing from a fresh shell after the key was only `export`ed in another session; secret name typo.","solutions":["Provide the key via the environment: `CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM=\"$(cat signing-key.pem)\" node scripts/sign-binary-checksums.mjs ...`.","In CI, add CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM as a secret on the signing job.","Keep the private key out of files and arguments — if it lives only on another machine, copy it securely first."],"exampleFix":"# before\nnode scripts/sign-binary-checksums.mjs dist/checksums.txt dist/checksums.keysig keys/binary-public.pem\n# after\nCAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM=\"$(cat ~/.secure/binary-signing.pem)\" \\\n  node scripts/sign-binary-checksums.mjs dist/checksums.txt dist/checksums.keysig keys/binary-public.pem","handlingStrategy":"validation","validationCode":"#!/usr/bin/env bash\nset -euo pipefail\n: \"${CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM:?CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM is required}\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fail fast in the release script with ${VAR:?} before doing any work.","Store the secret in CI as a masked variable and on the signer via a secret manager — never in argv or files in the repo."],"tags":["signing","environment","secrets","release"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}