{"record":{"id":"2628138da15f016c","repo":"OpenNHP/opennhp","slug":"invalid-cookiesigningkeybase64-w","errorCode":null,"errorMessage":"invalid CookieSigningKeyBase64: %w","messagePattern":"invalid CookieSigningKeyBase64: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"endpoints/server/udpserver.go","lineNumber":292,"sourceCode":"\t\tlog.Critical(\"failed to create device: %v\", err)\n\t\treturn fmt.Errorf(\"failed to create device %v\", err)\n\t}\n\n\t// Stateless cookie signing key. In a multi-instance cluster all\n\t// nhp-server replicas must share the same value so any of them can\n\t// verify a cookie that a sibling minted. When the operator hasn't\n\t// configured one we mint a random per-process key — fine for a single\n\t// instance, broken for a cluster (the failure is silent: cookies\n\t// minted by replica A don't verify on replica B and the agent's RKN\n\t// stalls until timeout). Always log which mode we're in.\n\tcookieKey, cookieKeyErr := decodeCookieSigningKey(s.config.CookieSigningKeyBase64)\n\tif cookieKeyErr != nil {\n\t\t// Malformed (not empty) is an ops mistake — fail fast rather\n\t\t// than silently degrading to a per-process random key. Silent\n\t\t// fallback would let a cluster look healthy while its replicas\n\t\t// each mint cookies a sibling can't verify.\n\t\tlog.Critical(\"invalid CookieSigningKeyBase64 in config: %v\", cookieKeyErr)\n\t\treturn fmt.Errorf(\"invalid CookieSigningKeyBase64: %w\", cookieKeyErr)\n\t}\n\tif len(cookieKey) == 0 {\n\t\tcookieKey = make([]byte, 32)\n\t\tif _, readErr := rand.Read(cookieKey); readErr != nil {\n\t\t\tlog.Critical(\"failed to generate random cookie signing key: %v\", readErr)\n\t\t\treturn fmt.Errorf(\"failed to generate random cookie signing key: %v\", readErr)\n\t\t}\n\t\tlog.Info(\"CookieSigningKeyBase64 not set; using a random per-process key (single-instance only — clusters must share an operator-supplied key)\")\n\t} else {\n\t\tlog.Info(\"CookieSigningKeyBase64 configured; cookies are stateless and shared across the cluster\")\n\t\t// Catch operators who copy the docker-compose demo config and\n\t\t// forget to regenerate the shared key. The shipped value is\n\t\t// public (committed to docker/nhp-server/etc/config.toml so\n\t\t// `docker-compose up` works out of the box) — running it in\n\t\t// any environment a real client can reach lets anyone who has\n\t\t// browsed the repo mint cookies the server will accept.\n\t\t// Critical (not Warning) so it surfaces in default journalctl\n\t\t// filters and any oncall log-volume alarms.","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/udpserver.go#L274-L310","documentation":"UdpServer.Start refuses to boot when CookieSigningKeyBase64 is present in the config but not valid base64 (or decodes to an unusable key). The server deliberately fails fast instead of silently falling back to a per-process random key, because in a cluster each replica minting cookies a sibling cannot verify would break stateless cookie auth while looking healthy.","triggerScenarios":"Calling Start (via runApp) with a config.toml whose CookieSigningKeyBase64 is set to a non-empty string that fails base64 decoding or key validation in the cookie-key parsing step.","commonSituations":"Operators hand-editing config.toml and pasting a key with whitespace/quotes/truncation; secret managers returning the base64 of a base64; copying a key from docs with placeholder characters; CI templating leaving stray characters in the field.","solutions":["Re-run the base64 encoder over the raw key and paste only the exact single-line base64 output into CookieSigningKeyBase64","Verify the value decodes: `echo '<value>' | base64 -d | wc -c` and confirm it yields a 32-byte key","Regenerate a fresh key with the daemon's keygen command and use its base64 output","If no shared key is intended, remove the field entirely so Start generates a random per-process key (single-instance only)"],"exampleFix":"// before (config.toml)\nCookieSigningKeyBase64 = \"aGVsbG8gd29ybGQ=  \"  # trailing space/newline breaks decode\n// after\nCookieSigningKeyBase64 = \"<openssl rand -base64 32 output, trimmed>\"","handlingStrategy":"validation","validationCode":"key := cfg.CookieSigningKeyBase64\nif key != \"\" {\n    if _, err := base64.StdEncoding.DecodeString(strings.TrimSpace(key)); err != nil {\n        return fmt.Errorf(\"config: CookieSigningKeyBase64 is not valid base64: %w\", err)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate the key with `openssl rand -base64 32` and paste it in one unbroken line","Run `base64 -d` over the configured value as a deploy-time smoke test","Never hand-edit base64 strings; always regenerate via keygen"],"tags":["config","crypto","base64"],"backgroundTag":"invalid-config-value","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}