Support user-provided Seatbelt sandbox rules via config or drop-in file

Open 💬 2 comments Opened May 27, 2026 by wlccgp3

What variant of Codex are you using?

CLI (also applies to Desktop app on macOS)

What feature would you like to see?

Allow users to provide additional Seatbelt sandbox rules that get appended to the assembled policy at runtime, without recompiling Codex.

Motivation: Codex's Seatbelt base policy (seatbelt_base_policy.sbpl + restricted_read_only_platform_defaults.sbpl) covers common OS interactions but inevitably misses niche-but-legitimate ones. The current workarounds — per-command escalation or :danger-full-access — are either tedious or overly broad.

A concrete example: Chromium/Playwright headless (#21292) requires (allow mach-register (global-name-regex #"^org\.chromium\.")) for its multi-process IPC. Today, users must escalate every Playwright invocation or disable the sandbox entirely. A one-line user rule would fix it permanently and precisely.

Other cases that benefit from user-provided rules:

  • CLI tools that write config to non-standard ~/Library/Application Support/<tool>/ paths (e.g., glab-cli)
  • Language toolchain telemetry paths (e.g., Go writes to ~/Library/Application Support/go/telemetry/)
  • Platform-specific sysctl-read entries for ARM feature detection (hw.optional.neon_fp16, etc.)
  • Mach service lookups required by domain-specific frameworks

Proposed approach (one of):

  1. Drop-in file: Read ~/.codex/sandbox-extra.sb (or ~/.codex/sandbox.d/*.sb) if it exists, and append its contents to the assembled Seatbelt policy after all built-in sections.
  1. Config key: Add a sandbox_extra_rules key to config.toml under the permission profile:

``toml
[permissions.my-profile]
sandbox_extra_rules = """
(allow mach-register (global-name-regex #"^org\\.chromium\\."))
(allow mach-lookup (global-name "com.apple.CARenderServer"))
"""
``

  1. Config file reference: A sandbox_extra_rules_file key pointing to an external .sb file.

The drop-in file approach is simplest and follows established patterns (conf.d/ in nginx, systemd, etc.). It also keeps sandbox customization separate from the TOML config, which feels appropriate given that Seatbelt rules are a different language (TinyScheme S-expressions).

Implementation sketch (in codex-rs/sandboxing/src/seatbelt.rs):

// After assembling policy_sections from built-in .sbpl files + dynamic rules:
let extra_path = dirs::home_dir()
    .map(|h| h.join(".codex/sandbox-extra.sb"));
if let Some(path) = extra_path {
    if let Ok(extra) = std::fs::read_to_string(&path) {
        policy_sections.push(extra);
    }
}
let full_policy = policy_sections.join("\n");

This is ~5 lines of Rust with zero breaking changes. Users who don't create the file get identical behavior to today.

Security consideration: Users who can write ~/.codex/sandbox-extra.sb already have full access to their own machine — they could just run commands outside the sandbox. User-provided rules only _relax_ the sandbox (Seatbelt rules are additive on top of (deny default)), and the current permission profile system already trusts user-authored TOML config for filesystem and network policy. This is consistent with that trust model.

Additional information

I maintain a macOS Seatbelt sandbox wrapper (ccbox) for Claude Code that reuses Codex's base .sbpl policies. The wrapper uses a text-file-based approach for user rules (extra-allow.txt + editable sandbox-base.sb), and being able to add the Chromium mach-register rule without recompilation has been essential for Playwright/browser-based verification workflows.

Related: #21292 (Playwright Chromium fails in sandbox with MachPortRendezvousServer permission denied) — I posted a root-cause analysis and the specific fix there. That issue would be fully resolved by either upstreaming the rule into seatbelt_base_policy.sbpl or by this feature allowing users to add it themselves.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗