{"record":{"id":"c6f295016b49a551","repo":"hoppscotch/hoppscotch","slug":"failed-to-generate-key-pair-w","errorCode":null,"errorMessage":"failed to generate key pair: %w","messagePattern":"failed to generate key pair: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/hoppscotch-selfhost-web/webapp-server/internal/crypto/keys.go","lineNumber":124,"sourceCode":"\tdir := filepath.Dir(path)\n\tif err := os.MkdirAll(dir, 0700); err != nil {\n\t\treturn fmt.Errorf(\"failed to create key directory: %w\", err)\n\t}\n\n\tencoded := base64.StdEncoding.EncodeToString(priv)\n\tif err := os.WriteFile(path, []byte(encoded), 0600); err != nil {\n\t\treturn fmt.Errorf(\"failed to write key file: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// generateAndPersist creates a new key and tries to save it.\n// If we can't persist, we log the key so operators can set it manually.\nfunc generateAndPersist(keyPath string) (*KeyPair, error) {\n\tpub, priv, err := ed25519.GenerateKey(rand.Reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to generate key pair: %w\", err)\n\t}\n\n\tkp := &KeyPair{\n\t\tSigningKey:   priv,\n\t\tVerifyingKey: pub,\n\t}\n\n\tif err := saveToFile(keyPath, priv); err == nil {\n\t\tlog.Printf(\"Generated and saved signing key to: %s\", keyPath)\n\t\tlog.Printf(\"Verifying key: %s\", base64.StdEncoding.EncodeToString(pub))\n\t\treturn kp, nil\n\t}\n\n\t// couldn't persist, log the key so it can be set via env var\n\t// this is annoying but better than silent failures\n\tkeyB64 := base64.StdEncoding.EncodeToString(priv)\n\n\tlog.Println(\"========================================\")","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/hoppscotch/hoppscotch/blob/1acb8a3a7581e4db32ba0d529170c4669a2e1053/packages/hoppscotch-selfhost-web/webapp-server/internal/crypto/keys.go#L106-L142","documentation":"`generateAndPersist` calls `ed25519.GenerateKey(rand.Reader)` to create a fresh signing key when no env var or on-disk key is found. If the crypto random source returns an error, the function wraps it: `failed to generate key pair: %w`. `rand.Reader` is Go's `crypto/rand` which reads from the OS CSPRNG (`/dev/urandom` on Linux, `RtlGenRandom` on Windows). Failure here means the operating system could not provide cryptographic randomness — an exceptional, system-level fault.","triggerScenarios":"The container or sandbox has no `/dev/urandom` mounted (e.g. a minimal chroot, a severely restricted seccomp profile, or a broken `devtmpfs`). File-descriptor exhaustion on Linux preventing the open of `/dev/urandom`. A custom `rand.Reader` was injected that returns an error. Extremely early in system boot before entropy is seeded on some embedded kernels.","commonSituations":"Running the webapp-server in a Docker container with `--security-opt no-new-privileges` plus an overly strict seccomp/AppArmor profile that blocks `/dev/urandom`; a scratch/distroless image missing the device node; a Kubernetes pod with a broken `readOnlyRootFilesystem` setup that masks `/dev`.","solutions":["Ensure `/dev/urandom` is available inside the container — run `head -c 32 /dev/urandom | xxd` from inside the pod; it must succeed.","If using a restricted seccomp profile, allow the `getrandom` syscall (syscall number 318 on amd64) or mount `/dev/urandom` via a hostPath/device.","Switch to providing the key explicitly via `WEBAPP_SERVER_SIGNING_KEY` or `WEBAPP_SERVER_SIGNING_SEED` so generation is never attempted at runtime.","On Kubernetes, do not set `readOnlyRootFilesystem` in a way that masks `/dev`; or use `WEBAPP_SERVER_SIGNING_SECRET` to derive the key deterministically."],"exampleFix":"# before — container masks /dev, GenerateKey fails\ndocker run --read-only --security-opt seccomp=blockall webapp-server\n# → failed to generate key pair: ... \n\n# after — allow getrandom / mount urandom, or pass a key explicitly\ndocker run -e WEBAPP_SERVER_SIGNING_SECRET=my-shared-secret webapp-server","handlingStrategy":"fallback","validationCode":"// Provide a key via env var so GenerateKey is never called.\n// In your Dockerfile / deployment:\n//   ENV WEBAPP_SERVER_SIGNING_SECRET=...\n// or derive a stable seed:\n//   ENV WEBAPP_SERVER_SIGNING_SEED=<base64 of 32 bytes>\n// Then in Go you can also assert the random source is usable:\nfunc ensureEntropyAvailable() error {\n    f, err := os.Open(\"/dev/urandom\")\n    if err != nil { return fmt.Errorf(\"no entropy source: %w\", err) }\n    f.Close()\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// In main(), handle GenerateKeyPair failure by falling back to\n// a deterministic secret (if acceptable for the deployment).\nkp, err := crypto.GenerateKeyPair()\nif err != nil {\n    log.Printf(\"key generation failed: %v; falling back to WEBAPP_SERVER_SIGNING_SECRET\", err)\n    if secret := os.Getenv(\"WEBAPP_SERVER_SIGNING_SECRET\"); secret != \"\" {\n        kp, err = crypto.GenerateKeyPair() // will pick up the secret\n    }\n    if err != nil {\n        log.Fatalf(\"cannot obtain signing key: %v\", err)\n    }\n}","preventionTips":["Always set one of WEBAPP_SERVER_SIGNING_KEY, _SEED, or _SECRET in production so generation is never attempted.","Ensure the container has /dev/urandom mounted and the getrandom syscall allowed.","Mount a persistent volume at /data/webapp-server so generated keys survive restarts.","Monitor startup logs for 'SIGNING KEY PERSISTENCE FAILED' to catch silent generation issues."],"tags":["go","crypto","ed25519","system","hoppscotch-selfhost-web"],"backgroundTag":null,"analyzedSha":"1acb8a3a7581e4db32ba0d529170c4669a2e1053","analyzedAt":"2026-08-12T11:34:52.648Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}