Hmbown/CodeWhale · error · Error

key is not pinned and active; use --public-key only for…

Error message

key is not pinned and active; use --public-key only for explicit offline verification

What it means

The verify subcommand normally loads the trusted key for envelope.key_id from the repo-pinned trust table (loadTrustedKeysFromRepo) and requires its status to be "active". If the key_id is unknown, unpinned, or not active (e.g. revoked), verification is refused with this message; --public-key is the explicit opt-in for offline verification against an ad-hoc key.

Solutions

  1. If you explicitly trust the key, pass --public-key <pem|base64> for offline verification
  2. Add/activate the key in the repo's pinned trusted-keys table and retry
  3. Check envelope.key_id for typos against the pinned table entries
  4. Re-sign with the currently active publishing key

Example fix

// before
node facts-publish.mjs verify envelope.json
// after
node facts-publish.mjs verify envelope.json --public-key ~/.secrets/cwf-prod.public.pem
Defensive patterns

Strategy: validation

Validate before calling

const trusted = loadTrustedKeysFromRepo().get(envelope.key_id);
if (!(trusted && trusted.status === 'active') && !flags['public-key']) {
  throw new Error(`key ${envelope.key_id} is not pinned and active; pass --public-key for offline verify`);
}

Try / catch

const exit = await run(['verify', envelopePath]).catch((e) => {
  if (e.message.includes('not pinned and active')) {
    // fall back to explicit offline verification
    return run(['verify', envelopePath, '--public-key', pubPem]);
  }
  throw e;
});

Prevention

When it happens

Trigger: Verifying an envelope signed with a key never added to the repo trust table; a key whose status in the pinned table is "revoked" or "pending"; a typo'd or rotated key_id in the envelope; verifying before the key-rotation SQL was applied to the repo.

Common situations: Rotating publishing keys and forgetting to commit the new key pin; verifying an old envelope after its key was revoked; receiving an envelope from another fork whose pinned key set differs.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/432bf1d92e237ae3. Report an issue: GitHub.

Appendix: source

Thrown at web/scripts/facts-publish.mjs:556

    if (!Number.isSafeInteger(factsVersion) || factsVersion <= 0) throw new Error("--facts-version (or source.facts_version) must be a positive integer");
    const keyId = String(flags["key-id"] ?? "");
    const privateKey = loadPrivateKeyFromEnv();
    const publishedAt = String(flags["published-at"] ?? nowIso());
    const payload = buildPayload(source, { channel, factsVersion, publishedAt });
    const envelope = buildEnvelope({ privateKey, keyId, payload });
    const text = `${JSON.stringify(envelope, null, 2)}\n`;
    if (flags.out) {
      writeFileSync(resolve(String(flags.out)), text);
      console.error(`wrote ${flags.out} (channel=${channel} facts_version=${factsVersion} key_id=${keyId} sha256=${envelope.sha256})`);
    } else process.stdout.write(text);
    return 0;
  }
  if (cmd === "verify") {
    const envelope = readJson(resolve(String(positional[1] ?? "")));
    let pub = flags["public-key"];
    if (!pub) {
      const trusted = loadTrustedKeysFromRepo().get(envelope.key_id);
      if (!trusted || trusted.status !== "active") throw new Error("key is not pinned and active; use --public-key only for explicit offline verification");
      pub = trusted.publicKey;
    }
    const result = verifyEnvelope(envelope, String(pub));
    console.log(JSON.stringify({ ok: result.ok, errors: result.errors, channel: envelope.channel, facts_version: envelope.facts_version, key_id: envelope.key_id, sha256: result.sha256 ?? null }, null, 2));
    return result.ok ? 0 : 1;
  }
  if (cmd === "emit-sql") {
    const envelope = readJson(resolve(String(positional[1] ?? "")));
    let pub = flags["public-key"];
    if (!pub) pub = activePublishingKey(envelope, [...loadTrustedKeysFromRepo().values()]).key.publicKey;
    process.stdout.write(emitSql(envelope, { publishedBy: String(flags["published-by"] ?? ""), publicKeyB64: pub ? String(pub) : undefined, notes: String(flags.notes ?? "") }));
    return 0;
  }
  if (cmd === "publish") {
    const envelope = readJson(resolve(String(positional[1] ?? "")));
    if (flags["public-key"] !== undefined) throw new Error("--public-key is only for offline verify/emit-sql; publication requires the active pinned table");
    const { key, check } = activePublishingKey(envelope, [...loadTrustedKeysFromRepo().values()]);
    const pub = key.publicKey;

View on GitHub (pinned to 433685b202)