JuliusBrussee/caveman · error
native session key chmod
Error message
native session key chmod: %w
What it means
The existing session.key passed the 32-byte size check but os.Chmod(path, 0600) failed. Almost always the file is owned by a different uid (a previous sudo run minted it), and chmod on a file you do not own is denied even inside a directory you control. The runtime enforces 0600 as a hard invariant for the shared HMAC key, so it refuses to continue with a group/world-readable key.
Solutions
- sudo chown $(id -u):$(id -g) <home>/runtime/session.key
- Or discard and re-mint: sudo rm <home>/runtime/session.key, then restart as the normal user
- Stop launching caveman under sudo; fix the launcher script
- lsattr <home>/runtime/session.key to rule out the immutable attribute
Defensive patterns
Strategy: validation
Validate before calling
keyPath := filepath.Join(home, "runtime", "session.key")
if info, err := os.Stat(keyPath); err == nil {
if st, ok := info.Sys().(*syscall.Stat_t); ok && int(st.Uid) != os.Getuid() {
return fmt.Errorf("%s owned by uid %d — run: sudo chown %d:%d %s (or remove it to re-mint)", keyPath, st.Uid, os.Getuid(), os.Getgid(), keyPath)
}
}
// safe to call nativeruntime.LoadOrCreateSessionKey(home) Type guard
func isPermissionErr(err error) bool {
return errors.Is(err, fs.ErrPermission)
} Try / catch
key, err := nativeruntime.LoadOrCreateSessionKey(home)
if err != nil {
if errors.Is(err, fs.ErrPermission) && strings.Contains(err.Error(), "session key chmod") {
log.Printf("key file is foreign-owned — sudo chown %d:%d %s or sudo rm it and restart", os.Getuid(), os.Getgid(), filepath.Join(home, "runtime", "session.key"))
}
return err
} Prevention
- Never run caveman under sudo — the minted key ends up root-owned and every later unprivileged start fails the 0600 enforce
- When migrating accounts, chown the runtime dir and key together with the home
- After any admin restores files into the home, verify key ownership before starting
- Removing the foreign key is always safe recovery: a new one is minted, only old marker correlation is lost
When it happens
Trigger: session.key created under sudo/root and then caveman run as the normal user; the file was copied in by another user with ownership preserved; the immutable attribute was set on the file.
Common situations: The sudo-first-run pattern on shared machines; homes restored by an admin; files migrated between accounts with cp -p.
Related errors
- native session key chmod dir
- native session key create
- native session key mkdir
- sqlite parent is group/world writable
- : : inspect database parent
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/2fa252f174668cd6.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/nativeruntime/marker.go:64
return nil, fmt.Errorf("native session key sync: %w", syncErr)
}
if closeErr := file.Close(); closeErr != nil {
return nil, fmt.Errorf("native session key close: %w", closeErr)
}
return key, nil
}
if !errors.Is(err, os.ErrExist) {
return nil, fmt.Errorf("native session key create: %w", err)
}
key, err = os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("native session key read: %w", err)
}
if len(key) != sessionKeyBytes {
return nil, fmt.Errorf("native session key length = %d, want %d", len(key), sessionKeyBytes)
}
if err := os.Chmod(path, 0o600); err != nil {
return nil, fmt.Errorf("native session key chmod: %w", err)
}
return key, nil
}
// SessionMarker builds model-temporary correlation context. Local proxy removes
// valid markers byte-surgically before provider inspection or forwarding.
func SessionMarker(key []byte, sessionID string) (string, error) {
if len(key) != sessionKeyBytes || sessionID == "" || len(sessionID) > 256 {
return "", errors.New("native session marker: invalid key or session id")
}
encoded := base64.RawURLEncoding.EncodeToString([]byte(sessionID))
sig := markerMAC(key, encoded)
return fmt.Sprintf(`[[caveman-session-v1 sid="%s" sig="%s"]]`, encoded, sig), nil
}
// StripSessionMarkers removes only valid HMAC-signed markers. Invalid marker-
// shaped user text remains byte-identical. Conflicting valid session IDs are
// stripped but return no correlation identity.View on GitHub (pinned to 766dce6b13)