Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- On a Linux T3 server, open Settings > Providers for that environment and set Codex "Launch arguments" to
-c sandbox_workspace_write.network_access=true. - Start a new Codex thread in Auto mode.
- Ask the agent to run
gh api user --jq .login.
Expected behavior
The command runs inside the workspace-write sandbox with network access, because Codex is configured with sandbox_workspace_write.network_access = true.
Actual behavior
The sandbox has no network access. The command fails:
Get "https://api.github.com/user": proxyconnect tcp: dial tcp 127.0.0.1:43117: socket: operation not permitted
The Codex rollout for the turn records "sandbox_policy":{"type":"workspace-write","network_access":false,...}. The codex app-server process does have -c sandbox_workspace_write.network_access=true on its command line.
The thread starts with the correct policy. I sent the same thread/start request that T3 sends (sandbox: "workspace-write", approvalPolicy: "on-request") to a separate codex app-server started with the same -c argument. The response contained "networkAccess": true.
The value changes at the turn. buildTurnStartParams sends sandboxPolicy: runtimeModeToTurnSandboxPolicy(input.runtimeMode) on every turn. For Auto and Auto-accept-edits, that function returns { type: "workspaceWrite" } with no networkAccess field. Codex treats the missing field as false, and the turn policy replaces the thread policy that came from the configuration.
t3code/apps/server/src/provider/Layers/CodexSessionRuntime.ts
Lines 568 to 590 in 6391be2
| function runtimeModeToTurnSandboxPolicy( | |
| input: RuntimeMode, | |
| ): EffectCodexSchema.V2TurnStartParams__SandboxPolicy { | |
| switch (input) { | |
| case "approval-required": | |
| return { | |
| type: "readOnly", | |
| }; | |
| case "auto-accept-edits": | |
| case "auto": | |
| return { | |
| type: "workspaceWrite", | |
| }; | |
| case "full-access": | |
| default: | |
| return { | |
| type: "dangerFullAccess", | |
| }; | |
| } | |
| } | |
| function buildCodexTurnInstructions(input: { | |
| readonly interactionMode?: ProviderInteractionMode; |
| sandboxPolicy: runtimeModeToTurnSandboxPolicy(input.runtimeMode), |
As a result, the documented Codex setting and T3's own Launch arguments field have no effect in sandboxed modes. The only mode with network access is Full access, which removes the sandbox and all approvals. gh auth status also reports this failure as "The token in ... is invalid", which is misleading.
A possible fix is to build the turn policy from the thread's effective sandbox value, which thread/start returns, and change only its type. Another option is to send sandboxPolicy only when the runtime mode changes. Either option keeps the user's Codex settings for network access and writable roots.
Impact
Major degradation or frequent failure
Version or commit
T3 server 0.0.42; same code on main at 6391be2
Environment
T3 server on Debian 13 as a systemd user service; T3 desktop 0.0.42 on Windows as a remote client; Codex CLI 0.157.0; Auto mode
Logs or stack traces
Codex rollout turn_context for the failing turn:
"approval_policy": "on-request",
"approvals_reviewer": "auto_review",
"sandbox_policy": {"type": "workspace-write", "network_access": false, "exclude_tmpdir_env_var": false, "exclude_slash_tmp": false}Screenshots, recordings, or supporting files
No response
Workaround
None in a sandboxed mode. Escalated commands can reach the network after approval.
Related: #6415, which asked for Codex sessions to inherit configured permission profiles and was moved to Discussions.
Activity
juliusmarminge commentedon Sep 27, 2026
Triage
Confirmed on current
main. This is a bug, and it is not a duplicate of #6415 (that request was to inherit named Codex permission profiles and now lives in discussion #6976).thread/startandthread/resumeonly send a sandbox mode. For Auto and Auto-accept-edits that mode is"workspace-write", so Codex resolvessandbox_workspace_write.network_access(and writable roots) from config and launch arguments. That matches thenetworkAccess: trueresult from a standalonethread/startwith the same-cflag.Every
turn/startthen replaces that resolved policy.buildTurnStartParamsalways sendssandboxPolicy: runtimeModeToTurnSandboxPolicy(runtimeMode), and for both of those modes the policy is only{ type: "workspaceWrite" }:turn/start'ssandboxPolicyis defined as an override for this turn and subsequent turns. OnworkspaceWrite,networkAccessdefaults tofalseandwritableRootsdefaults to[], so the omitted fields wipe the config Codex had already applied. The rollout value"network_access": falseis that override, not the launch argument failing to reachcodex app-server. Full access is unaffected becausedangerFullAccesshas no network flag. Thegh auth status"token invalid" line isghmisreading the denied proxy dial (127.0.0.1:43117: operation not permitted).The policy is resent on purpose so a runtime-mode change sticks. A fix should keep that, and preserve the config-resolved workspace-write fields (
networkAccess,writableRoots, and the tmp exclusions) instead of hardcodingnetworkAccess: true.CodexSessionRuntime.test.tscurrently expects the bare{ type: "workspaceWrite" }policy, so those assertions need to move with the fix.highmanphil commentedon Oct 13, 2026
Still present on 0.0.46-nightly.20261012.3010. The turn defaults now live in
codexRuntimeModeTurnDefaultsinapps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts, which still returns{ type: "workspaceWrite" }for Auto and Auto-accept-edits. Configuredwritable_rootsget dropped the same way asnetwork_access.For scale: across about 330 Codex threads in Auto on one Linux server, 169 commands failed on DNS or connect (
gh,git fetch, npm, PyPI, App Store Connect). Another 60 failed with EROFS when writing to worktrees outside the cwd. SSH also fails inside the sandbox withBad owner or permissions on /etc/ssh/ssh_config.d/....Until this is fixed, I added an AGENTS.md rule that tells Codex to request escalation on the first try for network commands. The auto reviewer approves those, so it works around the problem but costs an approval round per command.
fix(harness): merge Codex turn overrides with the user's config
nsoliven commentedon Oct 16, 2026
Bump. Still the same issue.
poroburu commentedon Oct 20, 2026
Reproduced with T3 0.1.0, Codex 0.163.0, and a fresh delegated child in Auto mode.
We confirmed Codex loaded the intended config. A named profile extending
:workspace, with network enabled andfeatures.network_proxy = true, activated the managed proxy:However, after T3's bare
workspaceWriteturn override, the effective policy omitted an additional profile workspace root. That made our shared test limiter unwritable. A configured filesystem deny entry remained in the effective policy, but runtime read denial was not tested.We stopped verification and reverted the config. We are not claiming successful network, VSTest, or SSH execution.
PR #18344's explicit named-profile exclusion therefore leaves this use case unresolved. Should preserving named-profile permissions while enforcing runtime-mode changes be tracked separately under discussion #6976?
porointern commentedon Oct 25, 2026
Follow-up to the earlier report, 2026-10-25. To unblock our own use, we built v0.1.2 + #18344 (merged as-is, original commits) plus a small local follow-up, and qualified it on Linux with Codex 0.164.0: isolated T3 home and
CODEX_HOME, Auto mode, real sandboxed commands. Sharing what we found in case it helps scope a follow-up to #18344. We're not asking for a merge.Named profiles. Sending
turn/start.permissions = "<profile>"(with nosandboxPolicy) instead of the bareworkspaceWritefixes the profile case, as long as you respect managedallowedPermissionProfilesand the documentedsandbox_modeprecedence overdefault_permissions.permissionsis an experimental field that the generated stableturn/startencoder drops, so we send it over the raw transport. T3 already enablesexperimentalApi. Results with a profile extending:workspace(an extra writable root, a denied directory, a domain allowlist,network_proxy):gh apiA second issue in the same area: commits from T3 worktrees. Even with the correct profile, a sandboxed Codex agent can't
git commitin a T3 thread worktree. Codex protects the resolvedgitdir:of a writable root's.gitfile (.git/worktrees/<id>) as read-only unless an explicit rule names that exact path (default_read_only_subpaths_for_writable_rootin codex-rs/protocol/src/permissions.rs, rust-v0.164.0). The id changes per worktree, so user config can't express it.We worked around it with a per-thread
configoverride atthread/startandresume:permissions.<profile>.filesystemgrants write access to that one gitdir and to each module gitdir under it.config,config.worktree,hooksandinfostay read-only, because git runs those outside the sandbox. This loosens a deliberate Codex protection, so it's opt-in on our side and probably wants a design discussion rather than a PR.Happy to share the patch and the replay tests if that's useful.
porointern commentedon Oct 26, 2026
Update, 2026-10-26: the named-profile route described in our previous comment now supports our ordinary Codex engineering workflow, including the public-resource access that originally blocked us.
This is our patched v0.1.2 build with Codex 0.164.0, not a claim that stock T3 or current upstream
mainis fixed. We retained #18344's original commits and the existing local adapter follow-up; those local repairs were implemented with Claude Opus. Codex development and an Astra consult helped reconcile the later tests and narrow the landing. This round required no additional T3 production code: we used the corrected adapter and an ordinary owner-managed named permission profile with managed-proxy TCP networking enabled.The generic adapter requirement remains configuration fidelity: preserve the selected named profile on sandboxed turns (
turn/start.permissions, without a simultaneous baresandboxPolicythat replaces it), while respecting runtime-mode changes and managed restrictions. Deployment-specific domain/address policy should stay in Codex configuration. An opt-in per-thread overlay catalog, approval database or fresh-thread admission chain was unnecessary for this connectivity fix. This result does not establish correct selector precedence across every layered configuration combination.Actual ordinary Codex command checks demonstrated:
We retained filesystem, privilege and reviewer restrictions and did not switch to Full access to obtain networking. The accepted goal is functional public-resource usability comparable to our Claude workflow; it is not a claim that the two providers have identical security boundaries. Live DNS-rebinding/final-routing, exhaustive private-network topology, global UDP and cross-provider/host guarantees remain unverified. A later successful PDF text extraction was separate local tooling, not evidence of a T3 networking feature or completion of a textbook-grounded audit.
Our configuration change has completed its normal review/CI/merge process and we are keeping the tested configuration. The original bare-policy bug and named-profile inheritance remain distinct upstream concerns; this comment reports a working local route and its limits, not an upstream merge or a universal security certification.
TommyBez commentedon Nov 2, 2026
Also reproducible on v0.1.6 (macOS arm64, Codex adapter V2, runtime mode
auto), with git writes inside T3 thread worktrees:git mergein a thread worktree:fatal: update_ref failed for ref 'ORIG_HEAD': cannot lock ref 'ORIG_HEAD': Unable to create '<repo>/.git/worktrees/<name>/ORIG_HEAD.lock': Operation not permittedgit show --remerge-diffin a read-only review thread:error: unable to create temporary object directoryThe worktree's
.gitis agitdir:pointer andgit rev-parse --git-common-dirresolves to<repo>/.git, outside the turn'scwd.codexRuntimeModeTurnDefaults("auto")still sends a bare{ type: "workspaceWrite" }on everyturn/start(same onmainat 8657eec), so nowritableRootssurvive.Note for whoever picks this up: Codex also keeps each writable root's
.git(and a resolved worktreegitdir) read-only by design, so preserving the config (#18344) may not be enough for git metadata writes on its own. See #26865 for the same mechanism withdelegate_taskchildren and why a plainwritableRootsfix was withdrawn.Workaround on our side for now: git writes (merge/commit) go through Claude threads; Codex threads stay read-only for git.
junkovich commentedon Nov 4, 2026
I can reproduce this on Linux with T3 server
0.1.7-nightly.20261103.4138and Codex CLI0.166.0, using Auto.My config has
network_access = trueunder[sandbox_workspace_write], but commands exposeCODEX_SANDBOX_NETWORK_DISABLED=1. Runninggh api rate_limitfails with "error connecting to api.github.com". The same command succeeds when run with approval outside the sandbox. Neither GitHub API quota is exhausted.This also affects scheduled read-only audits: a run can finish with its GitHub lookups unsuccessful unless it requests an approved retry.
I haven't tested #18344. Thanks for working on this.
prrtzt-agents commentedon Nov 5, 2026
Still present in the latest stable, v0.1.7, on macOS with Codex 0.163.0.
RuntimePolicyV2.layerFromProjectStorebuilds the runtime policy fromruntimeMode,interactionModeandcwdonly, so no setting, provider instance config ort3 thread startoption can supply asandboxPolicy.codexRuntimeModeTurnDefaults("auto")then sends{ type: "workspaceWrite" }, andnetworkAccessdefaults tofalse.[sandbox_workspace_write] network_access = truein~/.codex/config.tomlhas no effect, and CodexlaunchArgs(-c ...) don't help either, because the turn override replaces the config-resolved policy.The effect: Codex review threads in Auto can't resolve
proxy.golang.orgorregistry.npmjs.org, so they can't rungo testorpnpm install. The same reviewer through plaincodex exec -c sandbox_mode="workspace-write"downloads modules and runs the tests. We're waiting for #18344 rather than switching review threads to Full access.