{"record":{"id":"433b018d6181f6ad","repo":"JuliusBrussee/caveman","slug":"bundle-completeness-cannot-be-attested-by-unsigned","errorCode":null,"errorMessage":"bundle completeness cannot be attested by unsigned export metadata","messagePattern":"bundle completeness cannot be attested by unsigned export metadata","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":17869,"sourceCode":"  const raw = Buffer.from(info.key, \"base64\");\n  if (raw.length !== 32 || raw.toString(\"base64\") !== info.key) throw new Error(`${label} must be a canonical base64 Ed25519 public key`);\n  return { info, raw, key: ed25519PublicKey(raw) };\n}\n\nfunction decodeUniqueKeyring(infos: ReceiptPublicKey[], label: string): Map<string, DecodedReceiptKey> {\n  const keys = new Map<string, DecodedReceiptKey>();\n  for (const [index, info] of infos.entries()) {\n    const decoded = decodeReceiptKey(info, `${label}[${index}]`);\n    if (keys.has(decoded.info.key_id)) throw new Error(`${label} contains duplicate key_id ${decoded.info.key_id}`);\n    keys.set(decoded.info.key_id, decoded);\n  }\n  return keys;\n}\n\nfunction embeddedReceiptKeys(bundle: ReceiptBundle): { current: DecodedReceiptKey; keys: Map<string, DecodedReceiptKey> } {\n  if (bundle.schema !== RECEIPT_BUNDLE_V1 && bundle.schema !== RECEIPT_BUNDLE_V2) throw new Error(`unsupported bundle schema ${String(bundle.schema)}`);\n  if (bundle.verification_coverage !== undefined && bundle.verification_coverage !== INCLUDED_RECEIPTS_ONLY) throw new Error(`unsupported unsigned verification coverage ${String(bundle.verification_coverage)}`);\n  if (bundle.completeness_attested === true) throw new Error(\"bundle completeness cannot be attested by unsigned export metadata\");\n  const current = decodeReceiptKey(bundle.public_key, \"public_key\");\n  if (bundle.public_keys !== undefined && !Array.isArray(bundle.public_keys)) throw new Error(\"public_keys must be an array\");\n  if (bundle.schema === RECEIPT_BUNDLE_V2 && (!Array.isArray(bundle.public_keys) || bundle.public_keys.length === 0)) throw new Error(\"v2 bundle requires public_keys\");\n  const keys = decodeUniqueKeyring(bundle.public_keys ?? [], \"public_keys\");\n  const currentInRing = keys.get(current.info.key_id);\n  if (currentInRing && !currentInRing.raw.equals(current.raw)) throw new Error(`public_key conflicts with public_keys entry ${current.info.key_id}`);\n  if (bundle.schema === RECEIPT_BUNDLE_V2 && !currentInRing) throw new Error(\"v2 public_keys must include public_key\");\n  if (!currentInRing) keys.set(current.info.key_id, current);\n  return { current, keys };\n}\n\nasync function pinnedReceiptKeys(file: string, current: DecodedReceiptKey): Promise<{ keys: Map<string, DecodedReceiptKey>; trust: string }> {\n  const source = (await readFile(file, \"utf8\")).trim();\n  if (!source.startsWith(\"{\")) {\n    const pinned = decodeReceiptKey({ ...current.info, key: source }, \"--pubkey\");\n    if (!pinned.raw.equals(current.raw)) throw new Error(\"bundle public key does not match the published --pubkey\");\n    return { keys: new Map([[current.info.key_id, pinned]]), trust: \"pinned_public_key\" };\n  }","sourceCodeStart":17851,"sourceCodeEnd":17887,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L17851-L17887","documentation":"Thrown by embeddedReceiptKeys when an unsigned bundle has completeness_attested set to true. Because the bundle itself is unsigned, nothing cryptographically backs a claim that the export is complete (contains every receipt); the verifier therefore refuses to honor such an attestation, preventing an unverifiable completeness claim from being trusted.","triggerScenarios":"Verifying an unsigned bundle where completeness_attested is true — set by hand-editing the JSON, misusing a signed-export field in an unsigned bundle, or an exporter bug.","commonSituations":"Copy-pasting fields from a signed bundle template into an unsigned one; toggling the flag hoping to silence a completeness warning; tooling that copies signed export metadata onto unsigned exports.","solutions":["Set completeness_attested to false (or remove it) in the unsigned bundle and re-run verification.","Use the signed export path if you genuinely need an attested-complete bundle.","Re-export with the official unsigned export command instead of editing the JSON."],"exampleFix":"// before\n{\"completeness_attested\": true, ...} // unsigned bundle\n// after\n{\"completeness_attested\": false, ...} // or use a signed bundle for attestation","handlingStrategy":"validation","validationCode":"if (!isSignedBundle && bundle.completeness_attested === true) throw new Error('completeness_attested is not allowed on unsigned bundles');","typeGuard":"function isAttestableBundle(b: { schema: string; completeness_attested?: boolean }): boolean {\n  return b.schema.includes('signed') || b.completeness_attested !== true;\n}","tryCatchPattern":"try {\n  verifyBundle(bundle);\n} catch (e) {\n  if ((e as Error).message.includes('completeness')) console.error('unsigned bundles cannot attest completeness; use a signed export');\n  throw e;\n}","preventionTips":["Never set completeness_attested manually; let the export tooling decide.","Use the signed export path when a completeness attestation is required.","Treat unsigned bundles as coverage-limited by design."],"tags":["validation","crypto","trust"],"backgroundTag":"invalid-config-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}