Hmbown/CodeWhale · error · Error

--out is required (write the private key OUTSIDE any…

Error message

--out <path> is required (write the private key OUTSIDE any repository)

What it means

The keygen subcommand requires an explicit --out path where the generated ed25519 private key (PKCS#8 PEM) will be written with 0600 permissions. If the flag is absent, out resolves to null and this error is thrown, deliberately instructing you to write the key outside any repository.

Solutions

  1. Add --out /secure/path/outside/repo/cwf-x.pem (a directory outside any git repository)
  2. Ensure the parent directory exists or is creatable (the script mkdirs it with mode 0700)
  3. Note the file is opened O_EXCL|O_NOFOLLOW: choose a path where no file already exists

Example fix

// before
node facts-publish.mjs keygen --key-id cwf-prod
// after
node facts-publish.mjs keygen --key-id cwf-prod --out ~/.secrets/cwf-prod.pem
Defensive patterns

Strategy: validation

Validate before calling

if (!outPath) throw new Error('keygen requires --out pointing outside any repository');
if (existsSync(outPath)) throw new Error('output file already exists (O_EXCL will fail)');

Try / catch

try {
  await run(['keygen', '--key-id', keyId, '--out', outPath]);
} catch (e) {
  if (e.message.includes('--out <path> is required')) console.error('Pass a path outside the repo, e.g. ~/.secrets/<key-id>.pem');
  throw e;
}

Prevention

When it happens

Trigger: Running `facts-publish.mjs keygen --key-id cwf-x` without --out, or passing --out with an empty string value so flags.out is falsy.

Common situations: Following docs that show keygen without the flag; shell quoting problems making --out empty; forgetting that keys must live outside the repo to avoid committing secrets.

Understand the failure class

Background: "--flag is required" and "must specify" CLI errors: how missing-required-flag validation works and how to fix it — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

  return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(readBoundedFile(path)));
}

function nowIso() {
  return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
}

async function main(argv) {
  const { positional, flags } = parseArgs(argv);
  const cmd = positional[0];
  if (!cmd || flags.help) {
    console.log(readFileSync(fileURLToPath(import.meta.url), "utf8").split("\n").slice(1, 26).join("\n"));
    return 0;
  }
  if (cmd === "keygen") {
    const keyId = String(flags["key-id"] ?? "");
    if (!KEY_ID_RE.test(keyId)) throw new Error("--key-id must match cwf-[a-z0-9-]{1,32}");
    const out = flags.out ? resolve(String(flags.out)) : null;
    if (!out) throw new Error("--out <path> is required (write the private key OUTSIDE any repository)");
    refuseUnderCi();
    const { privateKey, publicKey } = generateKeyPairSync("ed25519");
    mkdirSync(dirname(out), { recursive: true, mode: 0o700 });
    const fd = openSync(out, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 0o600);
    try { writeFileSync(fd, privateKey.export({ type: "pkcs8", format: "pem" })); }
    finally { closeSync(fd); }
    const raw = rawPublicKeyFromKeyObject(publicKey);
    console.log(JSON.stringify({
      key_id: keyId,
      algorithm: "ed25519",
      public_key_b64: raw.toString("base64"),
      public_key_bytes: [...raw],
      private_key_file: out,
      note: "Private key written with mode 0600. Move it into custody (password manager); never commit it.",
    }, null, 2));
    return 0;
  }
  if (cmd === "sign") {

View on GitHub (pinned to 433685b202)