{"record":{"id":"490e1e910b56ce86","repo":"gastownhall/beads","slug":"identity-write-proxy-secret-w","errorCode":null,"errorMessage":"identity: write proxy secret: %w","messagePattern":"identity: write proxy secret: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/identity/identity.go","lineNumber":49,"sourceCode":"\t}\n\tresolved, err := filepath.EvalSymlinks(abs)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"identity: resolve root path: %w\", err)\n\t}\n\tsum := sha256.Sum256([]byte(resolved))\n\treturn hex.EncodeToString(sum[:]), nil\n}\n\n// WriteSecret creates and atomically writes a new control-listener secret.\n// Each proxy start intentionally rotates the previous secret.\nfunc WriteSecret(rootDir string) (string, error) {\n\traw := make([]byte, 32)\n\tif _, err := rand.Read(raw); err != nil {\n\t\treturn \"\", fmt.Errorf(\"identity: generate proxy secret: %w\", err)\n\t}\n\tsecret := hex.EncodeToString(raw)\n\tif err := atomicfile.WriteFile(filepath.Join(rootDir, SecretFileName), []byte(secret+\"\\n\"), 0o600); err != nil {\n\t\treturn \"\", fmt.Errorf(\"identity: write proxy secret: %w\", err)\n\t}\n\treturn secret, nil\n}\n\n// ReadSecret reads and validates the control-listener secret.\nfunc ReadSecret(rootDir string) (string, error) {\n\tdata, err := os.ReadFile(filepath.Join(rootDir, SecretFileName)) // #nosec G304 - rootDir is the workspace proxy root, not user input\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"identity: read proxy secret: %w\", err)\n\t}\n\tsecret := strings.TrimSpace(string(data))\n\tif len(secret) != 64 {\n\t\treturn \"\", errors.New(\"identity: invalid proxy secret\")\n\t}\n\tif _, err := hex.DecodeString(secret); err != nil {\n\t\treturn \"\", errors.New(\"identity: invalid proxy secret\")\n\t}\n\treturn secret, nil","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/identity/identity.go#L31-L67","documentation":"WriteSecret writes the generated hex secret to <rootDir>/proxy.secret using atomicfile.WriteFile with 0600 permissions. This error wraps any failure from that atomic write: inability to create the root directory path, permission denied, read-only filesystem, disk full, or a failure in the atomic rename/replace step. The library throws it because the control-listener secret could not be persisted for later readers.","triggerScenarios":"Calling identity.WriteSecret(rootDir) when rootDir does not exist or is not writable, when proxy.secret exists but is not writable/replaceable by the current user, when the filesystem is read-only or full, or when atomicfile's temp-file + rename sequence fails (e.g. cross-device issue or locked directory).","commonSituations":"Running bd as a different user than the one who owns the workspace (stale permissions), running with a read-only mount or full disk, pointing at a rootDir path that was never created, or SELinux/AppArmor blocking writes to the directory.","solutions":["Ensure rootDir exists and is writable: os.MkdirAll(rootDir, 0o755) and check ownership","Check disk space (df) and that the filesystem is mounted read-write","Fix file/directory permissions on <rootDir>/proxy.secret (chown/chmod) to match the running user","Read the wrapped error (errors.Unwrap) for the exact OS cause (fs.ErrNotExist, fs.ErrPermission, etc.)"],"exampleFix":"// before\nsecret, err := identity.WriteSecret(rootDir) // fails if rootDir missing\n// after\nif err := os.MkdirAll(rootDir, 0o755); err != nil {\n    return err\n}\nsecret, err := identity.WriteSecret(rootDir)","handlingStrategy":"validation","validationCode":"info, err := os.Stat(rootDir)\nif err != nil {\n    return fmt.Errorf(\"root %q missing: %w\", rootDir, err)\n}\nif !info.IsDir() {\n    return fmt.Errorf(\"%q is not a directory\", rootDir)\n}\nif err := unix.Access(rootDir, unix.W_OK); err != nil {\n    return fmt.Errorf(\"root %q not writable: %w\", rootDir, err)\n}","typeGuard":null,"tryCatchPattern":"secret, err := identity.WriteSecret(rootDir)\nif err != nil {\n    var perr *fs.PathError\n    if errors.As(err, &perr) && errors.Is(perr.Err, fs.ErrPermission) {\n        // fix ownership/permissions on rootDir or proxy.secret\n    }\n    return err\n}","preventionTips":["Always create rootDir with os.MkdirAll before WriteSecret","Run all bd processes for a workspace as the same user (secret file is 0600)","Check disk space and read-only mounts in deployment health checks","Ensure temp-file creation in rootDir is allowed (atomic rename needs a writable directory)"],"tags":["go","filesystem","permissions","atomic-write"],"backgroundTag":"file-write-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}