JuliusBrussee/caveman · error

has unsupported algorithm

Error message

${label} has unsupported algorithm ${String(info.alg)}

What it means

Thrown by decodeReceiptKey when the ReceiptPublicKey's alg field is anything other than the exact string "Ed25519". The receipt verifier only implements Ed25519 signature verification, so keys declared with another algorithm (or a differently cased/misspelled alg) are rejected outright.

Solutions

  1. Set alg to exactly "Ed25519" (capital E, capital E, digits 25519) in the key entry.
  2. Re-export the bundle using the official tooling so the algorithm field matches.
  3. If your key genuinely is another algorithm, generate an Ed25519 key instead — the verifier supports nothing else.

Example fix

// before
{"key_id": "k1", "alg": "ed25519", "key": "..."}
// after
{"key_id": "k1", "alg": "Ed25519", "key": "..."}
Defensive patterns

Strategy: validation

Validate before calling

if (info.alg !== 'Ed25519') throw new Error(`unsupported alg ${info.alg}; use Ed25519`);

Type guard

function isEd25519KeyInfo(info: unknown): info is { alg: 'Ed25519'; key: string; key_id: string } {
  return typeof info === 'object' && info !== null && (info as any).alg === 'Ed25519';
}

Try / catch

try {
  verifyBundle(bundle);
} catch (e) {
  if ((e as Error).message.includes('unsupported algorithm')) console.error('only Ed25519 keys are supported (exact casing)');
  throw e;
}

Prevention

When it happens

Trigger: Verifying a bundle whose public key entry has alg set to e.g. 'ed25519' (lowercase), 'EdDSA', 'RSA', or is missing/null. Case-sensitive strict equality against 'Ed25519' is enforced.

Common situations: Generating keys with a JOSE/JWT library that uses alg 'EdDSA'; hand-writing bundle JSON with lowercase 'ed25519'; an exporter emitting 'ed25519' vs the verifier's 'Ed25519' convention.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/0d5324862ca19811. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:17849

  let prev: Receipt | undefined;
  for (const r of sorted) {
    const decoded = keys.get(r.signature?.key_id);
    if (!decoded) return `seq ${r.seq}: no trusted public key for key_id ${String(r.signature?.key_id)}`;
    const err = verifyReceipt(r, decoded.key, decoded.info.key_id);
    if (err) return err;
    if (prev) {
      if (r.seq !== prev.seq + 1) return `seq ${r.seq}: not strictly after ${prev.seq}`;
      if (r.prev_receipt_hash !== prev.receipt_hash) return `seq ${r.seq}: prev_receipt_hash does not link to seq ${prev.seq}`;
      if (r.day <= prev.day) return `seq ${r.seq}: day ${r.day} does not follow ${prev.day}`;
    }
    prev = r;
  }
  return null;
}

function decodeReceiptKey(info: ReceiptPublicKey, label: string): DecodedReceiptKey {
  if (!info || typeof info.key_id !== "string" || !info.key_id.trim()) throw new Error(`${label} key_id is required`);
  if (info.alg !== "Ed25519") throw new Error(`${label} has unsupported algorithm ${String(info.alg)}`);
  if (typeof info.key !== "string" || !info.key.trim()) throw new Error(`${label} key is required`);
  const raw = Buffer.from(info.key, "base64");
  if (raw.length !== 32 || raw.toString("base64") !== info.key) throw new Error(`${label} must be a canonical base64 Ed25519 public key`);
  return { info, raw, key: ed25519PublicKey(raw) };
}

function decodeUniqueKeyring(infos: ReceiptPublicKey[], label: string): Map<string, DecodedReceiptKey> {
  const keys = new Map<string, DecodedReceiptKey>();
  for (const [index, info] of infos.entries()) {
    const decoded = decodeReceiptKey(info, `${label}[${index}]`);
    if (keys.has(decoded.info.key_id)) throw new Error(`${label} contains duplicate key_id ${decoded.info.key_id}`);
    keys.set(decoded.info.key_id, decoded);
  }
  return keys;
}

function embeddedReceiptKeys(bundle: ReceiptBundle): { current: DecodedReceiptKey; keys: Map<string, DecodedReceiptKey> } {
  if (bundle.schema !== RECEIPT_BUNDLE_V1 && bundle.schema !== RECEIPT_BUNDLE_V2) throw new Error(`unsupported bundle schema ${String(bundle.schema)}`);

View on GitHub (pinned to 3ee70a1026)