gastownhall/beads · error
identity: generate proxy secret: %w
Error message
identity: generate proxy secret: %w
What it means
WriteSecret generates 32 random bytes via crypto/rand.Read and encodes them as the control-listener secret. This error wraps a failure of crypto/rand.Read, which on virtually all platforms only fails if the operating system's cryptographic random source is unavailable or broken. The library throws it because it cannot produce a cryptographically secure secret without the OS entropy source.
Source
Thrown at internal/storage/dbproxy/identity/identity.go:45
func RootID(rootDir string) (string, error) {
abs, err := filepath.Abs(rootDir)
if err != nil {
return "", fmt.Errorf("identity: absolute root path: %w", err)
}
resolved, err := filepath.EvalSymlinks(abs)
if err != nil {
return "", fmt.Errorf("identity: resolve root path: %w", err)
}
sum := sha256.Sum256([]byte(resolved))
return hex.EncodeToString(sum[:]), nil
}
// WriteSecret creates and atomically writes a new control-listener secret.
// Each proxy start intentionally rotates the previous secret.
func WriteSecret(rootDir string) (string, error) {
raw := make([]byte, 32)
if _, err := rand.Read(raw); err != nil {
return "", fmt.Errorf("identity: generate proxy secret: %w", err)
}
secret := hex.EncodeToString(raw)
if err := atomicfile.WriteFile(filepath.Join(rootDir, SecretFileName), []byte(secret+"\n"), 0o600); err != nil {
return "", fmt.Errorf("identity: write proxy secret: %w", err)
}
return secret, nil
}
// ReadSecret reads and validates the control-listener secret.
func ReadSecret(rootDir string) (string, error) {
data, err := os.ReadFile(filepath.Join(rootDir, SecretFileName)) // #nosec G304 - rootDir is the workspace proxy root, not user input
if err != nil {
return "", fmt.Errorf("identity: read proxy secret: %w", err)
}
secret := strings.TrimSpace(string(data))
if len(secret) != 64 {
return "", errors.New("identity: invalid proxy secret")
}View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped error via errors.Unwrap to identify the OS-level cause
- Fix the environment: ensure /dev/urandom exists and seccomp/sandbox policy permits getrandom(2)
- Retry after the system entropy source is available (e.g. after boot completes)
- Check container runtime security profiles (Docker seccomp, gVisor) that may block getrandom
Defensive patterns
Strategy: retry
Try / catch
secret, err := identity.WriteSecret(rootDir)
if err != nil {
// crypto/rand failures are environmental; retry once after a short delay,
// then fail loudly with the wrapped cause
time.Sleep(100 * time.Millisecond)
secret, err = identity.WriteSecret(rootDir)
if err != nil {
return fmt.Errorf("entropy source unavailable: %w", err)
}
} Prevention
- Avoid sandboxes/seccomp profiles that block getrandom(2) or /dev/urandom
- Keep kernels reasonably modern (getrandom available since 3.17)
- In containers, mount a working /dev/urandom
- Treat this error as an environment health signal, not a code bug
When it happens
Trigger: Calling identity.WriteSecret(rootDir) when crypto/rand.Read fails: on Linux this can happen if getrandom(2) is blocked and /dev/urandom is unavailable; on very early boot before the entropy pool is initialized on old kernels; or in exotic sandboxes/seccomp profiles that block getrandom. It is extremely rare on modern systems.
Common situations: Running inside a container or sandbox with a restricted seccomp filter that denies getrandom, extremely stripped-down environments lacking /dev/urandom, or legacy kernels during early boot with insufficient entropy.
Related errors
- failed to generate id: %w
- httpapi: request id seed: %w
- identity: generate request nonce: %w
- failed to generate credential encryption key: %w
- no store is open for this workspace
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/620018a736630351.
Report an issue: GitHub.