{"record":{"id":"adc1cddc17d95a10","repo":"JuliusBrussee/caveman","slug":"secretbox-production-requires-cave-kms-provider-s","errorCode":null,"errorMessage":"secretbox: production requires CAVE_KMS_PROVIDER=scaleway","messagePattern":"secretbox: production requires CAVE_KMS_PROVIDER=scaleway","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"shared/platform/secretbox/secretbox.go","lineNumber":64,"sourceCode":"\t\treturn nil, fmt.Errorf(\"%s must decode to exactly 32 bytes, got %d\", envKey, len(keyBytes))\n\t}\n\treturn keyBytes, nil\n}\n\n// Encrypt seals plaintext with AES-256-GCM and a fresh random nonce, returning\n// nonce(12) || ciphertext+tag as raw bytes.\nfunc Encrypt(plaintext []byte) ([]byte, error) {\n\tif useKMS() {\n\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\t\tdefer cancel()\n\t\twrapped, err := kms.Encrypt(ctx, plaintext)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"secretbox: KMS encrypt: %w\", err)\n\t\t}\n\t\treturn wrapped, nil\n\t}\n\tif runtimeenv.IsProduction() {\n\t\treturn nil, fmt.Errorf(\"secretbox: production requires CAVE_KMS_PROVIDER=scaleway\")\n\t}\n\tkeyBytes, err := loadKey()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tblock, err := aes.NewCipher(keyBytes)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"aes cipher: %w\", err)\n\t}\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"aes-gcm: %w\", err)\n\t}\n\tnonce := make([]byte, gcm.NonceSize())\n\tif _, err := rand.Read(nonce); err != nil {\n\t\treturn nil, fmt.Errorf(\"nonce entropy: %w\", err)\n\t}\n\t// Seal appends the ciphertext+tag to nonce, so the returned slice is the","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/secretbox/secretbox.go#L46-L82","documentation":"Encrypt reached the local-key branch while runtimeenv.IsProduction() is true and no KMS provider is configured. The package enforces a hard policy: production secrets must be envelope-encrypted via Scaleway KMS (CAVE_KMS_PROVIDER=scaleway), never with a local env key — a local key in prod would concentrate all ciphertexts behind one copyable variable. This error is the guard refusing to continue.","triggerScenarios":"Deploying with RUNTIME_ENV/production env detection active but CAVE_KMS_PROVIDER unset (or set to a value useKMS() doesn't recognize), so Encrypt falls through to the production check.","commonSituations":"Promoting a dev compose/k8s manifest to production without adding the KMS provider variable; runtimeenv detecting production (e.g. via RUNTIME_ENV=production) in a staging-like environment by accident; typo in the provider value","solutions":["Set CAVE_KMS_PROVIDER=scaleway in the production environment and configure the Scaleway credentials/key the kms package needs.","Confirm runtimeenv.IsProduction() is only true where intended — an env misclassified as production will demand KMS too.","Add the variable to the deployment checklist/manifest diff so it cannot be dropped silently."],"exampleFix":"# before (production deploy)\nRUNTIME_ENV=production\n# CAVE_KMS_PROVIDER missing -> \"secretbox: production requires CAVE_KMS_PROVIDER=scaleway\"\n\n# after\nRUNTIME_ENV=production\nCAVE_KMS_PROVIDER=scaleway\nSCW_ACCESS_KEY=...\nSCW_SECRET_KEY=... # via secret manager, not plaintext","handlingStrategy":"validation","validationCode":"func prodCryptoConfigured() bool {\n    return !runtimeenv.IsProduction() || strings.EqualFold(os.Getenv(\"CAVE_KMS_PROVIDER\"), \"scaleway\")\n}\n// fail deployment if !prodCryptoConfigured()","typeGuard":null,"tryCatchPattern":"if _, err := secretbox.Encrypt(pt); err != nil {\n    if strings.Contains(err.Error(), \"production requires CAVE_KMS_PROVIDER=scaleway\") {\n        // halt deploy/startup; set the provider + KMS credentials; do not bypass by unsetting production detection\n    }\n}","preventionTips":["Add CAVE_KMS_PROVIDER=scaleway to the production manifest/deploy checklist and diff it in CI.","Ensure runtimeenv production detection matches intent so staging isn't forced onto KMS accidentally.","Treat this error as a policy stop, never as something to work around with a local key in prod."],"tags":["secrets","kms","production","configuration","security-policy"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}