Conversation
…relaxed `claude --bg` / `claude agents` start the daemon on demand. The client spawns it detached through `launchctl asuser <uid>`, with cwd set to $HOME. The child stays inside the Seatbelt sandbox, and Bun's startup getcwd opens "." to read the directory path. When that open is denied, the fallback walk reads /Users. Both reads were denied, so the daemon exited with "An unknown error occurred (Unexpected)" before the client could reach it. With the cwd fixed, the daemon then needs to bind its control socket under the hardcoded /tmp/cc-daemon-<uid>/. $TMPDIR does not cover that path. The relaxed profile now grants a file-read-data literal on $HOME (the directory listing only) and read/write on /tmp/cc-daemon-<uid>/. Verified by running `claude daemon run` from $HOME under `airlock run --profile claude-relaxed`: the socket binds and `claude daemon status` reaches it. A known gap remains. /bin/ps is setuid and Seatbelt forbids exec'ing it (forbidden-exec-sugid), so the daemon writes a lock without a start-time identity and `claude daemon stop` refuses to signal it. The daemon still exits when its last client disconnects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Under
airlock run --profile claude-relaxed, starting a Claude Code background session fails:The client probes for a GUI session with
launchctl asuser <uid> /usr/bin/true, then spawns the daemon withlaunchctl asuser <uid> claude daemon ...andcwd = $HOME.launchctl asuseronly execs the command, so the daemon stays inside the Seatbelt sandbox. At startup, Bun'sgetcwdopens.to read the directory path. When that open is denied, the fallback walks up withreaddir. The sandbox denied both reads (deny file-read-data /Users/paveqand/Usersin the log), so Bun aborted.After the cwd fix, the daemon still has to bind its control socket at
/tmp/cc-daemon-<uid>/<hash>/control.sock. That path is hardcoded, and the agent profile denies/tmpcontents.Fix (claude-relaxed only)
(allow file-read-data (literal "$HOME")): lists the names directly under$HOME; nothing below it becomes readable.(allow file-read* file-write* (subpath "/{private/,}tmp/cc-daemon-<uid>")): covers only the per-uid directory, not the rest of/tmp.SECURITY.md and README.md are updated to match.
Verification
cd $HOME && claude daemon rununderairlock run --profile claude-relaxed. Before the fix it failed with the sameUnexpectederror. After the fix the socket binds, andclaude daemon statusfrom inside the sandbox reports the socket path.cargo test --lib: 481 passed, including a new test that checks the rules are present inclaude-relaxedand absent fromclaude.Known gap
/bin/psis setuid, and Seatbelt always forbids sandboxed processes from exec'ing setuid binaries (forbidden-exec-sugid). The daemon's start-time probe (ps -o lstart=) therefore fails, so it writes a lock without a start-time identity, andclaude daemon stoprefuses to signal it. The daemon still exits when its last client disconnects.Log noise left as-is
These denials are harmless, and startup works without them:
kern.memorystatus_vm_pressure_level(Bun)kern.singleuser(launchctl)/runmetadatadiagnosticd🤖 Generated with Claude Code