{"record":{"id":"6d8692f72e9bdbf2","repo":"ruvnet/ruflo","slug":"env-enable-flag-is-set-but-env-key-var-is-no","errorCode":null,"errorMessage":"${ENV_ENABLE_FLAG} is set but ${ENV_KEY_VAR} is not. Provide a 32-byte key as 64-char hex or 44-char base64. See ADR-096 for keychain/passphrase support (coming in a follow-up).","messagePattern":"(.+?) is set but (.+?) is not\\. Provide a 32-byte key as 64-char hex or 44-char base64\\. See ADR-096 for keychain/passphrase support \\(coming in a follow-up\\)\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/encryption/vault.ts","lineNumber":83,"sourceCode":" * Resolve a 32-byte encryption key from CLAUDE_FLOW_ENCRYPTION_KEY.\n *\n * Phase 1 supports only the env-var source; keychain and passphrase\n * resolution are deferred to a follow-up iteration (see ADR-096). When\n * encryption is enabled but no key resolves, this throws with a clear\n * message rather than silently falling back to plaintext (fail-closed).\n *\n * Accepted encodings (auto-detected by length):\n *   - 64-char hex (32 bytes)\n *   - 44-char base64 (32 bytes + padding)\n *   - exactly 32 raw bytes (rare; for callers that pre-decode)\n *\n * Anything else is rejected — we'd rather fail loudly than encrypt with a\n * truncated key.\n */\nexport function getKey(): Buffer {\n  const raw = process.env[ENV_KEY_VAR];\n  if (!raw) {\n    throw new Error(\n      `${ENV_ENABLE_FLAG} is set but ${ENV_KEY_VAR} is not. ` +\n      `Provide a 32-byte key as 64-char hex or 44-char base64. ` +\n      `See ADR-096 for keychain/passphrase support (coming in a follow-up).`,\n    );\n  }\n  return decodeKey(raw);\n}\n\n/**\n * Decode a key string. Exposed for testing and for the future passphrase\n * resolver, which will scrypt-derive a Buffer and hand it back through here\n * to share the same length-check.\n */\nexport function decodeKey(raw: string): Buffer {\n  const trimmed = raw.trim();\n  // Hex first — strict 64 chars [0-9a-fA-F]\n  if (/^[0-9a-fA-F]{64}$/.test(trimmed)) {\n    return Buffer.from(trimmed, 'hex');","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/encryption/vault.ts#L65-L101","documentation":"Thrown by getKey() when CLAUDE_FLOW_ENCRYPT_AT_REST is set but CLAUDE_FLOW_ENCRYPTION_KEY is not. Encrypt-at-rest is opt-in; enabling it without supplying a key would be unsafe, so the vault refuses rather than generating an ephemeral key that would make existing ciphertext undecryptable after restart. ADR-096 tracks future keychain/passphrase support.","triggerScenarios":"Setting CLAUDE_FLOW_ENCRYPT_AT_REST=1 (or any truthy value) in the environment without also setting CLAUDE_FLOW_ENCRYPTION_KEY, or setting it in one process/shell but not the one performing encryption.","commonSituations":"Configuring encrypt-at-rest in a .env file but forgetting the key line, deploying with a secrets manager that failed to inject the key, or enabling the flag locally to test without yet generating a key.","solutions":["Generate a 32-byte key and set CLAUDE_FLOW_ENCRYPTION_KEY to its 64-char hex: `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`.","Store the key in your secrets manager / .env (NEVER commit it) and ensure it is present in every process that reads or writes ciphertext.","Until you have a key, unset CLAUDE_FLOW_ENCRYPT_AT_REST to disable the feature."],"exampleFix":"# before\nCLAUDE_FLOW_ENCRYPT_AT_REST=1\n# CLAUDE_FLOW_ENCRYPTION_KEY missing\n# after\nCLAUDE_FLOW_ENCRYPT_AT_REST=1\nCLAUDE_FLOW_ENCRYPTION_KEY=$(node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\")","handlingStrategy":"validation","validationCode":"function ensureVaultConfigured(): void {\n  const enabled = process.env.CLAUDE_FLOW_ENCRYPT_AT_REST;\n  const key = process.env.CLAUDE_FLOW_ENCRYPTION_KEY;\n  if (enabled && !key) {\n    throw new Error('CLAUDE_FLOW_ENCRYPT_AT_REST is set but CLAUDE_FLOW_ENCRYPTION_KEY is not');\n  }\n}\nensureVaultConfigured();","typeGuard":"const vaultConfigured = (): boolean =>\n  !process.env.CLAUDE_FLOW_ENCRYPT_AT_REST || Boolean(process.env.CLAUDE_FLOW_ENCRYPTION_KEY);","tryCatchPattern":"try {\n  getKey();\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (msg.includes('CLAUDE_FLOW_ENCRYPTION_KEY is not')) {\n    console.error('Generate a key: node -e \"console.log(crypto.randomBytes(32).toString(\\'hex\\'))\"');\n    process.exit(2);\n  }\n  throw e;\n}","preventionTips":["Set both env vars together via your secrets manager.","Generate the key once with crypto.randomBytes(32).toString('hex').","Add a startup self-check that fails fast if the pair is inconsistent."],"tags":["encryption","env","config","security","vault"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}