{"record":{"id":"eb8a3ed25d6e526d","repo":"nats-io/nats-server","slug":"jetstream-encryption-key-may-not-be-used-with-tpm","errorCode":null,"errorMessage":"JetStream encryption key may not be used with TPM options","messagePattern":"JetStream encryption key may not be used with TPM options","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/jetstream.go","lineNumber":403,"sourceCode":"\t\t\t\tif err := os.Rename(old, new); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// This function sets/updates the jetstream encryption key and cipher based\n// on options. If the TPM options have been specified, a key is generated\n// and sealed by the TPM.\nfunc (s *Server) initJetStreamEncryption() (err error) {\n\topts := s.getOpts()\n\n\t// The TPM settings and other encryption settings are mutually exclusive.\n\tif opts.JetStreamKey != _EMPTY_ && opts.JetStreamTpm.KeysFile != _EMPTY_ {\n\t\treturn fmt.Errorf(\"JetStream encryption key may not be used with TPM options\")\n\t}\n\t// if we are using the standard method to set the encryption key just return and carry on.\n\tif opts.JetStreamKey != _EMPTY_ {\n\t\treturn nil\n\t}\n\t// if the tpm options are not used then no encryption has been configured and return.\n\tif opts.JetStreamTpm.KeysFile == _EMPTY_ {\n\t\treturn nil\n\t}\n\n\tif opts.JetStreamTpm.Pcr == 0 {\n\t\t// Default PCR to use in the TPM. Values can be 0-23, and most platforms\n\t\t// reserve values 0-12 for the OS, boot locker, disc encryption, etc.\n\t\t// 16 used for debugging. In sticking to NATS tradition, we'll use 22\n\t\t// as the default with the option being configurable.\n\t\topts.JetStreamTpm.Pcr = 22\n\t}\n","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/jetstream.go#L385-L421","documentation":"initJetStreamEncryption rejects configs that set both a direct JetStream encryption key and TPM-based key sealing, because the two key sources are mutually exclusive — the server cannot decide whether to use the provided key or a TPM-sealed one.","triggerScenarios":"Server options include both a non-empty JetStreamKey (e.g. via --jetstream_key or JetStreamKey in config) and a non-empty JetStreamTpm.KeysFile (TPM keys file). Returned from EnableJetStream startup path.","commonSituations":"Merging config fragments where one sets jetstream key and another sets tpm block; leftover flag from a previous setup combined with new TPM config; automation templates writing both options.","solutions":["Remove the JetStreamKey option and keep only the TPM settings, or vice versa","Audit the final merged config/flags to ensure only one key source is set","If TPM is desired, delete the plain key; if a plain key is desired, remove the jetstream tpm block"],"exampleFix":"// before\njetstream {\n  key: \"my-secret-key\"\n  tpm { keys_file: \"/etc/nats/tpm-keys\" }\n}\n// after\njetstream {\n  tpm { keys_file: \"/etc/nats/tpm-keys\" }\n}","handlingStrategy":"validation","validationCode":"// Pre-validate options before EnableJetStream\nif opts.JetStreamKey != \"\" && opts.JetStreamTpm.KeysFile != \"\" {\n    return fmt.Errorf(\"set either jetstream key or tpm keys_file, not both\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep key configuration in one templated source to avoid merged conflicts","Lint server configs to reject both options at once","Document that TPM and plain key are mutually exclusive","After config reloads, dump effective options and check for both fields"],"tags":["jetstream","encryption","tpm","configuration-conflict"],"backgroundTag":"jetstream-encryption-config-conflict","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}