macOS: network_access = true in config.toml silently ignored by seatbelt sandbox — workaround included

Open 💬 13 comments Opened Feb 2, 2026 by fede-kamel
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Problem

On macOS, setting network_access = true under [sandbox_workspace_write] in ~/.codex/config.toml has no effect. The seatbelt sandbox unconditionally sets CODEX_SANDBOX_NETWORK_DISABLED=1, causing all outbound network calls (OCI CLI, curl, API clients, etc.) to fail with connection timeouts.

This affects any workflow that requires network access from within a Codex session — cloud CLI tools (OCI, AWS, GCP), package registries, API calls, etc.

Environment:

  • macOS (Darwin 25.2.0)
  • Codex CLI installed via Homebrew (/opt/homebrew/bin/codex)
  • config.toml with network_access = true under [sandbox_workspace_write]

Reproduction:

  1. Set network_access = true in ~/.codex/config.toml:

``toml
[sandbox_workspace_write]
network_access = true
``

  1. Launch codex and run any network command (e.g. curl https://example.com or an OCI/AWS CLI call)
  2. Command fails with connection timeout / "not reachable"

Root cause: The macOS seatbelt sandbox profile (codex-rs/core/src/seatbelt_network_policy.sbpl) enforces network blocking at the OS level regardless of config.toml.

Confirmed Workaround

The only reliable fix is bypassing the seatbelt sandbox via CLI flag:

codex --sandbox danger-full-access "your prompt"

Or as a persistent shell alias:

alias codex='CODEX_SANDBOX_NETWORK_DISABLED=0 codex --sandbox danger-full-access'

Setting sandbox_mode = "danger-full-access" in config.toml alone is not sufficient — the CLI flag is required.

Proposed Fix

The seatbelt profile should respect the network_access = true configuration from config.toml the same way the Linux Landlock sandbox does. When a user explicitly opts in to network access via config, the seatbelt policy should conditionally allow outbound connections instead of unconditionally blocking them.

A potential implementation path:

  1. In codex-rs/core, read network_access from the resolved config before generating the seatbelt profile
  2. Conditionally include/exclude the network deny rule in seatbelt_network_policy.sbpl
  3. This would make macOS behavior consistent with the documented config.toml settings

Happy to submit a PR if the team agrees with this approach.

Related Issues

  • #8714 (per-project config ignored, CODEX_SANDBOX_NETWORK_DISABLED=1 forced)
  • #5041 (VS Code extension blocks network with danger-full-access)
  • #3454 (network_access permission not working)
  • #5090 (sandbox/network enforcement regression)

View original on GitHub ↗

13 Comments

github-actions[bot] contributor · 5 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #8714
  • #9298
  • #8634

Powered by Codex Action

fede-kamel · 5 months ago

Not a duplicate — those issues were closed without a workaround or root cause analysis.

This issue provides both:

  1. Confirmed workaround (shell alias with --sandbox danger-full-access + CODEX_SANDBOX_NETWORK_DISABLED=0)
  2. Root cause analysis of the full config → seatbelt policy flow
  3. Two PRs with code contributions:
  • #10391 — README troubleshooting section so users can actually find the workaround
  • #10392 — Test coverage for network_access: true in the seatbelt path (unit + integration tests proving the OS-level sandbox behavior), plus tracing in derive_sandbox_policy() so users can debug why network is blocked

The integration tests in #10392 confirm the seatbelt code on main does correctly handle network_access: true — the policy includes network rules and socket creation succeeds under sandbox-exec. This means the bug users are hitting is likely a version mismatch between their installed binary and current main, or the config resolution path silently falling back to defaults (which the new tracing would surface).

The pre-existing seatbelt tests (create_seatbelt_args_with_read_only_git_and_codex_subpaths, create_seatbelt_args_with_read_only_git_pointer_file) also have a macOS /var/folders vs /private/var/folders path mismatch bug — unrelated to this issue but worth noting.

Closing this as a duplicate of issues that were themselves closed without resolution doesn't help the users still hitting this. Happy to iterate on the PRs if the team has feedback.

jawwadfirdousi · 5 months ago

Can confirm the issue on Codex MacOS app as well.
The app also does not respect the setting

fede-kamel · 5 months ago

Feel free to try this solving your problem. It works for me ; curious to know

fede-kamel · 4 months ago

Quick follow-up after re-checking current main: the macOS seatbelt path now has substantial sandbox/network changes (including dynamic network policy and proxy-aware routing in recent sandbox PRs), which looks like it may have addressed the original network_access behavior reported here. @etraut-openai could you confirm whether this issue is now considered fixed on latest releases, or if there is still a reproducible gap for macOS users? If fixed, I can help validate and close with tested version details.

bc-lee · 4 months ago

I encountered the same issue. Using MCP is an interesting option, but in my workflow I need to invoke Bazel directly. Bazel itself provides many tools such as bazel query, so agents still need to invoke bazel directly to work and to diagnose problems.

I added this to my Codex settings:

# $HOME/.codex/config.toml
[sandbox_workspace_write]
writable_roots = ["/private/var/tmp", "/var/tmp"]
# network_access = true # dangerous, do not enable

Even with that, the project still fails like this:

$ codex sandbox macos --full-auto --log-denials bazelisk build //:hello
Starting local Bazel server and connecting to it...
Server crashed during startup. Now printing /private/var/tmp/_bazel_redacted/01c67db48b5725ab953a9bd9ac1470c4/server/jvm.out
OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
gRPC server failed to bind to IPv4 and IPv6 localhosts on port 0: [IPv4] Failed to bind to address /127.0.0.1:0
[IPv6] Failed to bind to address /[0:0:0:0:0:0:0:1]:0
com.google.devtools.build.lib.util.AbruptExitException: gRPC server failed to bind to IPv4 and IPv6 localhosts on port 0: [IPv4] Failed to bind to address /127.0.0.1:0
[IPv6] Failed to bind to address /[0:0:0:0:0:0:0:1]:0
	at com.google.devtools.build.lib.server.GrpcServerImpl.serve(GrpcServerImpl.java:430)
	at com.google.devtools.build.lib.runtime.BlazeRuntime.serverMain(BlazeRuntime.java:1066)
	at com.google.devtools.build.lib.runtime.BlazeRuntime.main(BlazeRuntime.java:770)
	at com.google.devtools.build.lib.bazel.Bazel.main(Bazel.java:96)
Caused by: java.io.IOException: Failed to bind to address /127.0.0.1:0
	at io.grpc.netty.NettyServer.start(NettyServer.java:328)
	at io.grpc.internal.ServerImpl.start(ServerImpl.java:183)
	at io.grpc.internal.ServerImpl.start(ServerImpl.java:92)
	at com.google.devtools.build.lib.server.GrpcServerImpl.serve(GrpcServerImpl.java:427)
	... 3 more
Caused by: java.net.SocketException: Operation not permitted
	at java.base/sun.nio.ch.Net.bind0(Native Method)
	at java.base/sun.nio.ch.Net.bind(Unknown Source)
	at java.base/sun.nio.ch.ServerSocketChannelImpl.netBind(Unknown Source)
	at java.base/sun.nio.ch.ServerSocketChannelImpl.bind(Unknown Source)
	at io.netty.channel.socket.nio.NioServerSocketChannel.doBind(NioServerSocketChannel.java:141)
	at io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:562)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1334)
	at io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:600)
	at io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:579)
	at io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:973)
	at io.netty.channel.AbstractChannel.bind(AbstractChannel.java:260)
	at io.netty.bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:356)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Unknown Source)

=== Sandbox denials ===
(bazel) mach-lookup com.apple.logd
(bazel) mach-lookup com.apple.distributed_notifications@Uv3
(bazel) mach-lookup com.apple.CoreServices.coreservicesd
(bazel) mach-lookup com.apple.DiskArbitration.diskarbitrationd
(daemonize) sysctl-read security.mac.lockdown_mode_state
(daemonize) sysctl-read kern.bootargs
(daemonize) file-write-data /dev/dtracehelper
(java) sysctl-read security.mac.lockdown_mode_state
(java) sysctl-read kern.bootargs
(java) file-write-data /dev/dtracehelper
(java) mach-lookup com.apple.system.notification_center
(java) mach-lookup com.apple.logd
(java) mach-lookup com.apple.system.opendirectoryd.membership
(java) mach-lookup com.apple.bsd.dirhelper
(java) mach-lookup com.apple.SystemConfiguration.configd
(java) network-outbound /private/var/run/mDNSResponder
(java) network-bind local:*:0

I am not an expert on Seatbelt, but it seems like Codex could potentially support an option to allow binding to a local port. That would not weaken security very much, but it would make Bazel work in this setup. I am not sure whether this is feasible, but it would be great if Codex could support it. Thanks.

robertpatrick · 4 months ago

It seems pretty non-intuitive to configure workspace-write mode but, on the Mac, silently ignore that setting and change to seatbelt mode. If seatbelt mode is desirable, it seems like I should be able to disable it without resorting to danger-full-access mode.

fede-kamel · 4 months ago

@etraut-openai following up on this issue after the latest report on March 17, 2026: has this been addressed in current releases, or is there still an open macOS behavior gap here?

The latest feedback suggests the remaining problem may now be less about raw network egress and more about the UX/config model on macOS: users configure workspace-write, but Codex appears to silently enforce seatbelt semantics unless they jump all the way to danger-full-access.

If this is fixed, could you share which release/version contains the fix and what the expected behavior is on macOS for:

  • workspace-write
  • network_access = true
  • disabling or bypassing seatbelt without using danger-full-access

That would help clarify whether this issue should be closed or reframed.

BigCactusLabs · 3 months ago

Adding data from a Tuist + SPM iOS project:

The silent network block causes tuist generate to hang indefinitely when a project is connected to Tuist Cloud (fullHandle set in Tuist.swift). Tuist attempts auth against tuist.dev during workspace generation — with network silently blocked, the call never completes and never errors. No timeout, no log output, just a permanent stall.

Workaround: Setting optionalAuthentication: true in Tuist's generationOptions makes it skip server interactions gracefully when auth is unavailable. Generate goes from hanging indefinitely to completing in ~3 seconds with a warning.

Additional seatbelt findings from the same project:

  • swift package resolve fails with sandbox_apply: Operation not permitted (nested sandbox-exec). Fix: --disable-sandbox flag.
  • xcodebuild -list fails with exit 66 and misleading "not a workspace file" error — actually caused by missing CoreSimulator XPC/mach-lookup services in the seatbelt allowlist, not a bad workspace.
  • network_access = true and broad writable_roots in .codex/config.toml had no observable effect on any of these behaviors.

The core issue remains: workspace-write with network_access = true silently enforces seatbelt with no network. Every tool that phones home (Tuist Cloud, CocoaPods trunk, SwiftPM registry auth, Fastlane) will hang or fail without tool-specific opt-outs.

robertpatrick · 3 months ago

When Codex on a Mac cannot even run a Maven build because it blocks both the ability to download dependencies and write into $HOME/.m2/repository, might as well call the mode worthless. Dangerous mode is the only mode that lets developers use Codex to do their day-to-day work…

fede-kamel · 3 months ago

Root Cause Analysis & Fix

After tracing the full code path from config parsing through sandbox enforcement, I've identified the root cause and submitted a fix with full test coverage.

Root Cause

The issue is in dynamic_network_policy_for_network() (codex-rs/sandboxing/src/seatbelt.rs). It combines three unrelated conditions into a single guard:

let should_use_restricted_network_policy =
    !proxy.ports.is_empty() || proxy.has_proxy_config || enforce_managed_network;

When enforce_managed_network is true or a proxy config is present but no usable proxy ports exist, the function enters the restricted path, builds an empty network policy (no ports to allow through), and returns it — silently blocking all network regardless of the user's network_access = true.

Two additional findings that make this worse than it appears:

  1. enforce_managed_network is not an admin policy flag. It's set in exec.rs:241 as params.network.is_some() — a proxy-presence indicator, not a security enforcement mechanism. There is no admin-level override in the current architecture.
  1. The core code path never uses it. core/src/seatbelt.rs hardcodes enforce_managed_network to false in spawn_command_under_seatbelt(). The flag only takes effect through the SandboxManager transform path.

The Fix

Separated the proxy-routing logic from the no-proxy fallback:

| Scenario | Before (broken) | After (fixed) |
|----------|-----------------|---------------|
| Usable proxy exists | Route through proxy | Route through proxy (unchanged) |
| No proxy + network_access=true | Silent empty policy → blocked | Full network + tracing::warn! |
| No proxy + network_access=false | Blocked (fail-closed) | Blocked (unchanged) |

Explicit user config is honored when there is no proxy to route through. Fail-closed behavior is preserved when the user did not request network access. All overrides are logged via tracing::warn! so the behavior is never silent.

Additionally, derive_sandbox_policy() in config/mod.rs now logs the resolved sandbox mode, its source (cli-flag / config-profile / config-toml / default), and the network_access value — making the entire config resolution chain visible with RUST_LOG=info.

Test Coverage

  • 8 new unit tests covering all combinations of network_access × enforce_managed_network × proxy config
  • 1 integration test proving actual socket creation succeeds/fails under real sandbox-exec on macOS (localhost bind, CI-safe)
  • All 24 seatbelt tests pass; 2 pre-existing failures on main are unrelated (/var/folders path mismatch)

PR

https://github.com/fede-kamel/codex/pull/1 — ready for review, rebased on current main. Supersedes #10392.

Unable to open directly against this repo due to contribution policy restrictions. Happy to resubmit upstream if the team opens it up.

---

cc @etraut-openai @bolinfest @pakrym-oai @rreichel3-oai — you've been the most active on the sandbox/seatbelt code recently. Would appreciate your review.

cc @robertpatrick @BigCactusLabs @bc-lee @jawwadfirdousi — this should resolve the Maven, Tuist, and general network_access = true issues you've reported.

fede-kamel · 3 months ago

End-to-End Verification on macOS

Built codex from the fix branch (744f337c3) and tested under real seatbelt enforcement. All tests run on macOS Darwin 25.2.0 (aarch64).

Test Results

| Test | Mode | network_access | Result |
|------|------|-------------------|--------|
| curl https://example.com | seatbelt (workspace-write) | true | HTTP 200 |
| curl https://example.com | seatbelt (workspace-write) | false | HTTP 000 (blocked) |
| TCP socket bind 127.0.0.1:0 | seatbelt (workspace-write) | true | SUCCESS: bound to 127.0.0.1:52930 |
| TCP socket bind 127.0.0.1:0 | seatbelt (workspace-write) | false | Operation not permitted |
| cargo test -p codex-sandboxing seatbelt | unit + integration | — | 24/24 pass |
| cargo check -p codex-core | compile | — | clean |

Reproduction

# Clone the fix
git clone git@github.com:fede-kamel/codex.git && cd codex
git checkout fix/macos-seatbelt-network-tests-and-tracing-v2

# Build
cd codex-rs && cargo build -p codex-cli --release

# Test: network allowed with network_access=true
./target/release/codex sandbox macos --full-auto \
  -- curl -s -o /dev/null -w "%{http_code}" https://example.com
# → 200

# Control: network blocked with network_access=false
./target/release/codex sandbox macos --full-auto \
  -c "sandbox_workspace_write.network_access=false" \
  -- curl -s -o /dev/null -w "%{http_code}" --max-time 5 https://example.com
# → 000

# Run tests
cargo test -p codex-sandboxing seatbelt
# → 24 passed (2 pre-existing failures on main, unrelated)

@robertpatrick @BigCactusLabs — if you want to validate this against your Maven / Tuist workflows before the team merges, the steps above should get you going.

fede-kamel · 3 months ago

@etraut-openai ^