{"record":{"id":"520db03ad275b612","repo":"JuliusBrussee/caveman","slug":"native-session-key-random-w","errorCode":null,"errorMessage":"native session key random: %w","messagePattern":"native session key random: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/nativeruntime/marker.go","lineNumber":34,"sourceCode":"\nconst sessionKeyBytes = 32\n\nvar markerPattern = regexp.MustCompile(`\\[\\[caveman-session-v1 sid=\"([A-Za-z0-9_-]{1,384})\" sig=\"([0-9a-f]{64})\"\\]\\]`)\n\n// LoadOrCreateSessionKey returns one user-only HMAC key shared by CLI adapters\n// and local proxy. O_EXCL makes concurrent first startup converge on one key.\nfunc LoadOrCreateSessionKey(home string) ([]byte, error) {\n\tdir := filepath.Join(home, \"runtime\")\n\tif err := os.MkdirAll(dir, 0o700); err != nil {\n\t\treturn nil, fmt.Errorf(\"native session key mkdir: %w\", err)\n\t}\n\tif err := os.Chmod(dir, 0o700); err != nil {\n\t\treturn nil, fmt.Errorf(\"native session key chmod dir: %w\", err)\n\t}\n\tpath := filepath.Join(dir, \"session.key\")\n\tkey := make([]byte, sessionKeyBytes)\n\tif _, err := rand.Read(key); err != nil {\n\t\treturn nil, fmt.Errorf(\"native session key random: %w\", err)\n\t}\n\tfile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)\n\tif err == nil {\n\t\tif _, writeErr := file.Write(key); writeErr != nil {\n\t\t\t_ = file.Close()\n\t\t\t_ = os.Remove(path)\n\t\t\treturn nil, fmt.Errorf(\"native session key write: %w\", writeErr)\n\t\t}\n\t\tif syncErr := file.Sync(); syncErr != nil {\n\t\t\t_ = file.Close()\n\t\t\t_ = os.Remove(path)\n\t\t\treturn nil, fmt.Errorf(\"native session key sync: %w\", syncErr)\n\t\t}\n\t\tif closeErr := file.Close(); closeErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"native session key close: %w\", closeErr)\n\t\t}\n\t\treturn key, nil\n\t}","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/nativeruntime/marker.go#L16-L52","documentation":"LoadOrCreateSessionKey draws 32 bytes from crypto/rand for the new session key. rand.Read practically never fails on Linux (it can block until the pool is ready but errors only on a broken reader), so this error almost always indicates an extremely early boot entropy state or an OS/driver-level problem with the random device.","triggerScenarios":"First process executed in a freshly booted minimal VM/container before the CRNG is seeded; /dev/urandom unavailable due to a broken chroot or seccomp policy; hardware/kernel fault.","commonSituations":"CI containers or embedded images booting and immediately starting the proxy; overly restrictive sandboxes blocking getrandom(2).","solutions":["Simply retry after a moment — once the kernel CRNG is seeded this succeeds","Ensure the container/chroot exposes a working /dev/urandom and allows getrandom(2)","Use a modern base image; very old kernels lacked getrandom and fall back to weaker paths"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"var key []byte\nerr := backoffRetry(3, func() error {\n    var e error\n    key, e = nativeruntime.LoadOrCreateSessionKey(home)\n    return e\n}) // entropy failures self-heal once the kernel CRNG is seeded","preventionTips":["Let containers boot briefly before first start, or seed entropy (virtio-rng)","Keep /dev/urandom accessible in chroots/seccomp profiles","Use current base images with getrandom(2) support"],"tags":["crypto","entropy","nativeruntime","session-key","environment"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}