Sandbox: granular Mach IPC control — the sandbox is all-or-nothing today
The problem: no middle ground
The macOS sandbox currently offers two modes: fully sandboxed (Seatbelt blocks everything not explicitly allowed) or fully unsandboxed (dangerouslyDisableSandbox: true). There's no way to selectively relax one aspect of the sandbox while keeping the rest enforced.
This becomes a real issue when a command needs one specific capability that the sandbox blocks. Today the only option is to disable the entire sandbox for that command — dropping filesystem isolation, network filtering, and everything else — just to allow a single IPC call.
Real-world example: 1Password CLI + sandbox
I run Claude Code with the sandbox enabled, using 1Password (op CLI) for credential management. The op CLI communicates with the 1Password desktop app via Mach IPC (XPC) for biometric authentication (Touch ID). The sandbox blocks this:
kernel: (Sandbox) Sandbox: op(43886) deny(1) mach-lookup 2BUA8C4S2C.com.1password.browser-helper
Everything else works — unix sockets are reachable (allowUnixSockets), filesystem access is fine, network is fine. The only thing blocked is one Mach service lookup.
Workarounds I had to implement
To make this work, I had to:
- Split credential retrieval and API calls into separate Bash invocations —
op readruns unsandboxed (dangerouslyDisableSandbox: true), writes the token to a file, then a second sandboxed Bash call reads the token and makes the API request. This keeps curl sandboxed but adds complexity.
- Hardcode
/tmp/claude/for token files —$TMPDIRdiffers between sandboxed (/tmp/claude) and unsandboxed (/var/folders/...) contexts. Token files must use the sandboxed path so both calls can access them.
- Add CLAUDE.md instructions telling every session to use
dangerouslyDisableSandbox: trueforop read— becauseexcludedCommands: ["op"]doesn't reliably bypass Seatbelt (it still blocks the Mach lookup even in a fresh session with the setting confirmed loaded).
- Maintain a PreToolUse hook (
api-gate.sh) restrictingoptoop readfrom a specific vault — since the sandbox can't scope the command, the hook provides the access control layer.
The result works but it's fragile, and the security model is worse than it needs to be: every op read invocation drops the entire sandbox instead of just allowing one Mach service.
What I'd like to see
An allowMachLookup setting, following the same allowlist model as allowUnixSockets:
{
"sandbox": {
"allowMachLookup": [
"2BUA8C4S2C.com.1password.browser-helper"
]
}
}
This would generate a Seatbelt rule like:
(allow mach-lookup (global-name "2BUA8C4S2C.com.1password.browser-helper"))
With this, op read would work inside the sandbox — filesystem isolation, network filtering, and all other restrictions stay active. No need for dangerouslyDisableSandbox, no split Bash calls, no TMPDIR workarounds.
1Password is the most accessible secret management for Claude Code users
1Password is one of the most widely adopted credential management tools — it works for individual developers and scales to small teams sharing vaults. The op CLI with desktop app integration (Touch ID) is arguably the easiest way to give Claude Code secure, human-gated access to API tokens and secrets. It doesn't require infrastructure (no HashiCorp Vault, no self-hosted service), works out of the box on macOS, and the biometric gate means credentials are never auto-dispensed.
The sandbox blocking this specific integration pushes users toward less secure alternatives: hardcoded tokens, env vars in shell profiles, or disabling the sandbox entirely. Supporting op via allowMachLookup would make the recommended secure workflow the path of least resistance.
Broader applicability
This isn't just about 1Password. Other macOS CLI tools use Mach IPC too — security (Keychain access), xcrun (Xcode tools), and anything that communicates with a desktop app via XPC. A generic allowMachLookup setting handles all of these without needing tool-specific workarounds.
Side note: excludedCommands bug
sandbox.excludedCommands is documented to run commands outside the sandbox. With "op" in excludedCommands and a fresh session, op read still fails with the same Mach IPC denial. Verified the setting is loaded via jq '.sandbox.excludedCommands' ~/.claude/settings.json. This may be a separate bug worth looking into.
Environment
- macOS 26.4, Apple Silicon (M2 Pro)
- Claude Code 2.1.86
- 1Password CLI 2.33.1
- 1Password desktop app with CLI integration enabled
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗