{"record":{"id":"2e0a65dac180346a","repo":"ruvnet/ruflo","slug":"claude-flow-encrypt-at-rest-is-set-but-claude-flow","errorCode":null,"errorMessage":"CLAUDE_FLOW_ENCRYPT_AT_REST is set but CLAUDE_FLOW_ENCRYPTION_KEY 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":"CLAUDE_FLOW_ENCRYPT_AT_REST is set but CLAUDE_FLOW_ENCRYPTION_KEY 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":"exception","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/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/encryption/vault.ts#L65-L101","documentation":"Thrown by getKey() in the encryption vault when the at-rest encryption feature flag CLAUDE_FLOW_ENCRYPT_AT_REST is present in the environment but CLAUDE_FLOW_ENCRYPTION_KEY is not. The vault fails fast at key-load time rather than silently skipping encryption or deriving a weak key. The key must encode exactly 32 bytes (AES-256) as 64-char hex or 44-char base64; passphrase/keychain support (ADR-096) is not yet implemented.","triggerScenarios":"Setting CLAUDE_FLOW_ENCRYPT_AT_REST=true (or any value) in a shell, .env file, docker-compose environment block, or CI variables without also setting CLAUDE_FLOW_ENCRYPTION_KEY, then invoking any CLI/MCP path that initializes the vault (e.g. storing secrets or encrypted memory). Also triggered when the key variable name is misspelled (CLAUDE_FLOW_ENCRYPTION_KEY vs ENCRYPTION_KEY) or when a secret-manager injection step silently fails so only the flag reaches the process.","commonSituations":"CI pipelines that copy the enable flag from docs but never provision the key; Docker/Kubernetes env blocks where the flag is hardcoded but the key comes from a secret that was not mounted; teams enabling encrypt-at-rest after reading an ADR that mentions only the flag; wrapper scripts that export the flag globally in ~/.bashrc.","solutions":["Generate and set a proper key: CLAUDE_FLOW_ENCRYPTION_KEY=$(openssl rand -hex 32) (or openssl rand -base64 32 for 44-char base64) alongside CLAUDE_FLOW_ENCRYPT_AT_REST","If encryption was not intended, unset CLAUDE_FLOW_ENCRYPT_AT_REST — the vault only requires a key when the flag is set","Export both variables together from one wrapper script or secret-manager entry so they can never diverge (flag without key)","Verify the variable name is exactly CLAUDE_FLOW_ENCRYPTION_KEY (not CLAUDE_FLOW_ENCRYPTIONKEY or ENCRYPTION_KEY) with `env | grep CLAUDE_FLOW_ENCRYPT`"],"exampleFix":"# before — flag only, getKey() throws\nexport CLAUDE_FLOW_ENCRYPT_AT_REST=true\nnpx @claude-flow/cli@latest memory store --key k --value v\n\n# after — flag + 32-byte hex key\nexport CLAUDE_FLOW_ENCRYPT_AT_REST=true\nexport CLAUDE_FLOW_ENCRYPTION_KEY=\"$(openssl rand -hex 32)\"\nnpx @claude-flow/cli@latest memory store --key k --value v","handlingStrategy":"validation","validationCode":"// Fail with your own contextual message before any vault-touching call\nconst flag = process.env.CLAUDE_FLOW_ENCRYPT_AT_REST;\nconst key = process.env.CLAUDE_FLOW_ENCRYPTION_KEY;\nif (flag && !key) {\n  throw new Error(\n    'encrypt-at-rest enabled but CLAUDE_FLOW_ENCRYPTION_KEY is missing. ' +\n    'Generate one: openssl rand -hex 32'\n  );\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Export CLAUDE_FLOW_ENCRYPT_AT_REST and CLAUDE_FLOW_ENCRYPTION_KEY together from one wrapper script or secret-manager entry so they never diverge","Add a startup env assertion in CI/boot scripts (fail before work starts, not mid-run)","Document key generation (openssl rand -hex 32) next to every mention of the flag"],"tags":["encryption","env-vars","configuration","vault","fail-fast"],"backgroundTag":"missing-env-var","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}