Skip to main content
When the sandbox blocks an operation, macOS logs the denial event through the unified logging system. Use /usr/bin/log (not shell-shadowed log) to capture and analyze these events.

Live Denial Monitoring

1

Start denial stream

Open a terminal and run the live denial filter:
/usr/bin/log stream --style compact --predicate 'eventMessage CONTAINS "Sandbox:" AND eventMessage CONTAINS "deny("'
This shows all sandbox denial events in real-time as they occur.
2

Run your sandboxed command

In a separate terminal, execute the safehouse-wrapped command:
safehouse -- npm install
3

Analyze denial output

Watch the log stream for denied operations. Each denial line follows this format:
deny(<pid>) <operation> <path-or-name>

Using the —explain Flag

The --explain flag shows effective workdir, grants, and profile selection without running a command:
safehouse --explain --stdout
Use --explain to verify which directories and integrations are active before running your command.

Log Stream Variants

Filter by process pattern

/usr/bin/log stream --style compact \
  --predicate 'eventMessage CONTAINS "Sandbox: 2.1.34(" AND eventMessage CONTAINS "deny("'
Narrow results to a specific agent or PID pattern.

Kernel-level denials

/usr/bin/log stream --style compact --info --debug \
  --predicate '(processID == 0) AND (senderImagePath CONTAINS "/Sandbox")'
Captures low-level sandbox events from the kernel.

Recent history

/usr/bin/log show --last 2m --style compact \
  --predicate 'process == "sandboxd"'
Review sandboxd activity from the last 2 minutes.

Filter common noise

/usr/bin/log stream --style compact \
  --predicate 'eventMessage CONTAINS "Sandbox:" AND eventMessage CONTAINS "deny(" AND NOT eventMessage CONTAINS "duplicate report" AND NOT eventMessage CONTAINS "/dev/dtracehelper" AND NOT eventMessage CONTAINS "apple.shm.notification_center" AND NOT eventMessage CONTAINS "com.apple.diagnosticd" AND NOT eventMessage CONTAINS "com.apple.analyticsd"'
Excludes common macOS system noise.

Suppressing DTrace Noise

DTrace helper denials are usually harmless system noise. Suppress them with:
DYLD_USE_DTRACE=0 safehouse -- your-command

Converting Denials to Allow Rules

When you identify a legitimate denial, map it to the appropriate allow rule:
Denial TypeExample DenialAllow Rule Pattern
File operationsdeny(1234) file-read* /path/to/file(allow file-read* (literal "/path/to/file"))
Sysctl readdeny(1234) sysctl-read kern.hostname(allow sysctl-read (sysctl-name "kern.hostname"))
Mach lookupdeny(1234) mach-lookup com.apple.cfprefsd.daemon(allow mach-lookup (global-name "com.apple.cfprefsd.daemon"))
Networkdeny(1234) network-outbound localhost:8080(allow network-outbound (local ip "localhost:*"))
Always use the narrowest rule that unblocks the workflow. Prefer literal over subpath for file paths when possible.

Building a Profile from Scratch

If you’re authoring a new integration profile:
1

Start with base policy

Create a minimal .sb file:
(version 1)
(deny default)
2

Run with log stream active

Start the denial monitor in one terminal, then run your sandboxed workflow in another.
3

Map each denial to a rule

For each deny(...) event, add the minimum necessary allow rule to your profile.
4

Test full workflows

Exercise complete toolchain workflows (git, npm, cargo, etc.) since child processes inherit the sandbox policy.

Correlating with Filesystem Activity

For deeper filesystem behavior analysis, combine sandbox logs with fs_usage:
sudo fs_usage -w -f filesystem <pid> | grep -iE "open|create|write|rename"
Replace <pid> with the process ID from the denial log.

Common Issues

Nested sandbox failure

Symptom: sandbox-exec cannot nest errorCause: Already running inside a sandboxFix: Run from an unsandboxed terminal session

Git operations blocked

Symptom: git commands fail with permission errorsCause: Missing workdir grant or git integration not enabledFix: Verify workdir with --explain, ensure git integration is active

Network requests denied

Symptom: HTTP requests fail silentlyCause: Network profile not includedFix: Network is enabled by default; check for custom --append-profile overrides

Toolchain not found

Symptom: node, python, cargo not executableCause: Toolchain binary not in allowed pathsFix: Verify toolchain profile includes your installation path (see profiles/30-toolchains/)

Next Steps

Testing

Validate policy behavior with the test suite

Contributing

Submit new profiles or fixes to the project

Build docs developers (and LLMs) love