1# Codex security cookbook
2
3> For the complete documentation index, see [llms.txt](https://learn.chatgpt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.
4
5<noscript>
6
7</noscript>
8
9Rolling out Codex means deciding which accounts, files, and services it should be able to access and which actions it should be able to take. This cookbook helps enterprise administrators turn those decisions into configuration, test the results, and understand where additional protections are needed.
10
11These security recommendations are intended to help you give developers useful access to Codex while reducing the risk of unintended changes, exposure of sensitive data, and changes to the safeguards themselves. Use them to choose controls that fit your workflows and risk requirements, understand their limits, and verify how they work in your environment. They complement the identity, endpoint, network, and service protections your organization already manages.
12
13You can start with a specific task—such as restricting sign-in to your company workspace—or a broader goal, such as letting developers use Codex while protecting production.
14
15Choose the starting point that fits your work:
16
17- **Configuration recipes** walk through individual controls, with setup instructions, verification checks, rollback guidance, and limitations.
18
19- **Combined recipes** show how to combine several controls with protections in your identity, source control, deployment, and other systems to meet a common security goal.
20
21The examples give you a starting point to adapt to your organization. Use each configuration example with its accompanying guidance: where it belongs, which clients or environments it applies to, and what it does and does not control.
22
23Before a wider rollout, try the configuration on a test endpoint. Check that an intended workflow succeeds and an intentionally restricted action is blocked. Use those results to refine the setup and explain the boundaries to the people who will use Codex.
24
25## Choose your starting point
26
27Use the page outline to open a section below.
28
29| If you want to | Start with |
30| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
31| Configure one specific control | [Configuration recipes](#configuration-recipes) |
32| Let developers use Codex while protecting production | [Let developers use Codex while protecting production](#let-developers-use-codex-while-protecting-production) |
33| Reduce the risk of publishing internal code | [Reduce the risk of publishing internal code](#reduce-the-risk-of-publishing-internal-code) |
34| Keep Codex from changing its own safeguards | [Keep Codex from changing its own safeguards](#keep-codex-from-changing-its-own-safeguards) |
35
36The combined recipes include the relevant controls, external protections, and checks for each goal.
37
38## Start here
39
401. Inventory the surfaces in scope: desktop app, CLI, IDE extension, Codex Cloud, CI runners, and remote hosts.
41
422. Pilot the configuration on a test endpoint before a broad rollout.
43
443. Record the minimum supported Codex version. Permission-profile allowlists require Codex 0.138.0 or later.
45
464. Keep a rollback copy of each managed configuration bundle.
47
485. Test one allowed workflow and one intentionally blocked workflow after every change.
49
50Managed requirements are admin-enforced constraints; managed defaults are startup values. Use requirements for supported authentication, sandbox, approval, MCP, plugin, hook, and network restrictions. Authentication requirements must be delivered locally or through macOS MDM; cloud-managed requirements do not enforce them. See [Managed configuration](https://learn.chatgpt.com/docs/enterprise/managed-configuration) and the [Configuration Reference](https://learn.chatgpt.com/docs/config-file/config-reference).
51
52### Where configuration belongs
53
54Adapt these example configurations to your organization before rolling them out to a pilot group. The TOML blocks are policy fragments: merge the settings you need into your existing files, without duplicating tables, and replace example names, domains, and paths. The checks describe expected outcomes to validate in your environment.
55
56For local Codex, an administrator can deploy enforced requirements in `/etc/codex/requirements.toml` on macOS and Linux or `%ProgramData%\OpenAI\Codex\requirements.toml` on Windows. On managed macOS devices, an MDM can deliver the `com.openai.codex:requirements_toml_base64` preference. Cloud-managed policy can also be configured in the Admin Console under **Agent Security** by selecting a policy, choosing the applicable environment or OS, and configuring **Requirements**. Protect the policy file and its parent directory from modification by the Codex user. Settings in a regular `config.toml` or managed Defaults provide startup values rather than enforced requirements. See [Managed configuration](https://learn.chatgpt.com/docs/enterprise/managed-configuration) and [Agent Security](https://learn.chatgpt.com/docs/enterprise/agent-security).
57
58## Configuration recipes
59
60Choose the control you need. Open a recipe to see its configuration, verification checks, rollback guidance, and limitations. Check items as you verify them; progress is saved in this browser when browser storage is available.
61
62| Control | Recipe |
63| --------------------- | --------------------------------------------------------------------------------------------- |
64| Authentication | [Restrict sign-in to the company workspace](#restrict-sign-in-to-the-company-workspace) |
65| Runtime permissions | [Restrict permission profiles and Full Access](#restrict-permission-profiles-and-full-access) |
66| MCP servers | [Allow only approved MCP servers](#allow-only-approved-mcp-servers) |
67| Monitoring and audit | [Monitor Codex activity](#monitor-codex-activity) |
68| Networking | [Restrict network destinations](#restrict-network-destinations) |
69| Files and credentials | [Protect secrets and unrelated repositories](#protect-secrets-and-unrelated-repositories) |
70| Sensitive commands | [Require approval for sensitive commands](#require-approval-for-sensitive-commands) |
71
72
73
74<a id="restrict-sign-in-to-the-company-workspace"></a>
75
76
77
78### Restrict sign-in to the company workspace
79
80
81
82<PersistentChecklist client:load id="codex-security-cookbook-restrict-sign-in-to-the-company-workspace" itemIds={["sign-out-of-codex", "try-a-personal-chatgpt-account-expected-personal-workspace-rejected", "try-codex-apikey-login-expected-rejected", "sign-in-with-the-corporate-account-and-approved-workspace", "restart-the-desktop-app-cli-and-ide-extension-then"]} description="This browser could not save your verification progress.">
83
84Use this recipe when employees should sign in to Codex through your company’s ChatGPT workspace, with personal ChatGPT accounts and API-key authentication prohibited on managed endpoints.
85
86> **Intended result**
87>
88> On company-managed devices, Codex accepts only ChatGPT sign-in to the company’s ChatGPT workspace.
89
90#### Prerequisites
91
92- The UUID of the approved ChatGPT Enterprise workspace.
93
94- A way to distribute Codex managed configuration to endpoints.
95
96- A decision to prohibit API-key authentication on managed endpoints.
97
98#### Configure
99
1001. Record the approved workspace UUID in your internal configuration repository.
101
1022. Add enforced authentication requirements to the local `requirements.toml`:
103
104```toml
105 allowed_login_methods = ["chatgpt"]
106 allowed_chatgpt_workspaces = ["00000000-0000-0000-0000-000000000000"]
107```
108
109 The first key permits only ChatGPT authentication; the second restricts sign-in to the approved workspace. Replace the illustrative UUID with your workspace UUID. See the [requirements reference](https://learn.chatgpt.com/docs/config-file/config-reference#requirementstoml).
110
1113. Deploy through local `requirements.toml` or macOS MDM requirements using the paths in [Where configuration belongs](#where-configuration-belongs). These authentication requirements apply before credentials load and before cloud policy is retrieved; Codex ignores them in cloud-managed requirements. Protect the file and its parent directory from user modification. Legacy `managed_config.toml` defaults do not enforce this restriction.
112
1134. During the pilot, sign users out or remove previously stored personal Codex sessions. Do not assume a new policy retroactively invalidates an existing session.
114
1155. Pair the login restriction with workspace roles and group access. Authentication controls which identity signs in; RBAC controls which Codex features that identity may use. See [Authentication](https://learn.chatgpt.com/docs/auth).
116
117#### Verify
118
119- [ ] Sign out of Codex.
120
121- [ ] Try signing in with a personal ChatGPT account. Expected: Sign-in to the personal workspace is rejected.
122
123- [ ] Try Codex API-key login. Expected: API-key login is rejected.
124
125- [ ] Sign in to the approved workspace with the corporate account. Expected: Sign-in succeeds.
126
127- [ ] Restart the desktop app, CLI, and IDE extension, then repeat.
128
129#### Roll back
130
131Restore the previous local authentication requirements and redeploy the reviewed bundle.
132
133#### Boundaries
134
135This controls managed Codex clients on managed endpoints. Pair it with endpoint application controls and network policy if the requirement is “no personal Codex use from corporate assets.”
136
137</PersistentChecklist>
138
139
140
141
142
143
144
145<a id="restrict-permission-profiles-and-full-access"></a>
146
147
148
149### Restrict permission profiles and Full Access
150
151
152
153<PersistentChecklist client:load id="codex-security-cookbook-restrict-permission-profiles-and-full-access" itemIds={["open-the-permissions-picker-expected-full-access-unavailable-or", "launch-with-dangerouslybypassapprovalsandsandbox-expected-rejected-or-forced-to-a", "launch-with-sandbox-dangerfullaccess-askforapproval-never-expected-rejected", "run-a-normal-workspace-task-expected-workspace-work-succeeds"]} description="This browser could not save your verification progress.">
154
155Use this recipe when developers need read-only or workspace-scoped access, and you want to prevent them from selecting Full Access or bypassing approvals and sandboxing with `--yolo`.
156
157> **Intended result**
158>
159> Users can select only approved read-only or workspace-scoped profiles. They cannot select `danger-full-access`, `approval_policy = "never"`, or `--yolo`.
160
161#### Configure
162
1631. Define an approved workspace profile:
164
165```toml
166 [permissions.corp-workspace]
167 description = "Company workspace access with no unrestricted host access."
168 extends = ":workspace"
169```
170
1712. Enforce the allowed profiles and approval policies:
172
173```toml
174 allowed_approval_policies = ["on-request", "untrusted"]
175 allowed_approvals_reviewers = ["user"]
176 default_permissions = "corp-workspace"
177
178 [allowed_permission_profiles]
179 ":read-only" = true
180 "corp-workspace" = true
181 ":danger-full-access" = false
182```
183
184 The allowlist is complete: profiles omitted from the allowlist or set to false are denied, including profiles added in future versions. See the [Configuration Reference](https://learn.chatgpt.com/docs/config-file/config-reference).
185
1863. For legacy clients that still use `sandbox_mode`, use:
187
188```toml
189 allowed_approval_policies = ["on-request", "untrusted"]
190 allowed_sandbox_modes = ["read-only", "workspace-write"]
191```
192
1934. Deliver this as admin-enforced requirements, not a user-editable config file.
194
1955. If you later permit Auto-review, add it deliberately after testing. Auto-review changes who reviews a boundary crossing; it does not restore a removed boundary. See [Auto-review](https://learn.chatgpt.com/docs/sandboxing/auto-review).
196
197#### Verify
198
199- [ ] Open the permissions picker. Expected: Full Access is unavailable or rejected.
200
201- [ ] Launch with `--dangerously-bypass-approvals-and-sandbox`. Expected: Codex rejects the launch or forces a compatible profile.
202
203- [ ] Launch with `--sandbox danger-full-access --ask-for-approval never`. Expected: The launch is rejected.
204
205- [ ] Run a normal workspace task. Expected: The task succeeds; boundary crossings still require approval.
206
207#### Roll back
208
209Restore the prior requirements bundle. Keep unrestricted access blocked unless there is a separately approved exception.
210
211#### Boundaries
212
213This controls Codex runtime permissions. It does not make a privileged operating-system account unprivileged.
214
215</PersistentChecklist>
216
217
218
219
220
221
222
223<a id="allow-only-approved-mcp-servers"></a>
224
225
226
227### Allow only approved MCP servers
228
229
230
231<PersistentChecklist client:load id="codex-security-cookbook-allow-only-approved-mcp-servers" itemIds={["configure-the-approved-server-exactly-expected-starts", "change-one-url-character-expected-disabled", "change-the-stdio-argument-count-or-order-or-supply", "in-a-disposable-trusted-project-try-harmless-cwd-env", "add-an-unapproved-name-expected-disabled", "test-a-pluginbundled-server-expected-only-the-approved-identity"]} description="This browser could not save your verification progress.">
232
233Use this recipe when your organization needs to decide which MCP servers Codex may use and enforce an approved list.
234
235Allowing a server and configuring it are separate steps. An allowlist entry permits a matching server; it does not create the server configuration. Configure the server separately with the matching name and identity, along with the authentication it requires.
236
237> **Intended result**
238>
239> Codex enables only MCP servers whose configured name and identity match the enterprise-approved list.
240
241#### Configure
242
2431. Inventory each approved server: owner, purpose, transport, exact executable or URL, authentication, data classification, and write-capable tools.
244
2452. Start with deny-all. An empty managed table disables all MCP servers:
246
247```toml
248 [mcp_servers]
249```
250
2513. Add approved HTTP servers by exact URL:
252
253```toml
254 [mcp_servers.internal_docs]
255 identity = { url = "https://mcp.example.com/docs" }
256```
257
2584. Add approved stdio servers by executable and positional arguments:
259
260```toml
261 [mcp_servers.internal_build.identity]
262 command = { executable = "/usr/local/bin/company-mcp", args = [
263 { match = "exact", value = "serve" },
264 { match = "prefix", value = "--workspace=" },
265 ] }
266```
267
268 The structured matcher checks the executable, argument count, and argument order. It does not constrain `cwd`, `env`, or `env_vars`. Treat control of the full launch context as a prerequisite for approving a stdio server: protect its executable and dependencies, and prevent untrusted user or project configuration from supplying a working directory or environment that can change its behavior. If you cannot enforce that boundary independently, leave the stdio server unapproved and use a reviewed HTTP server instead.
269
2705. Add plugin-bundled MCP identities separately under `plugins.<plugin>.mcp_servers.<server>`.
271
2726. Store the approved list in a reviewed configuration repository and deploy it through admin-enforced requirements.
273
274The client enables a server only when both its name and identity match an approved entry. See [Managed configuration](https://learn.chatgpt.com/docs/enterprise/managed-configuration).
275
276#### Verify
277
278- [ ] Configure the approved server exactly. Expected: The server starts.
279
280- [ ] Change one URL character. Expected: The server is disabled.
281
282- [ ] Change the stdio argument count or order, or supply an argument outside the configured argument rules. Expected: The server is disabled. Values that still satisfy an allowed prefix remain permitted.
283
284- [ ] In a disposable trusted project, try harmless `cwd`, `env`, and `env_vars` overrides while retaining the matching executable and arguments. Identity matching alone does not reject these overrides. Verify that your independent launch-context control rejects unsafe overrides or that the stdio server remains unapproved; do not use a real injection payload.
285
286- [ ] Add an unapproved name. Expected: The server is disabled.
287
288- [ ] Test a plugin-bundled server. Expected: The server works only with the approved identity.
289
290#### Roll back
291
292Remove the newly added entry. To stop all MCP usage immediately, deploy an empty `mcp_servers` table.
293
294#### Boundaries
295
296Identity allowlisting does not prove that a server’s tools, upstream data, credentials, or launch environment are safe. Local stdio MCP processes run outside the local command sandbox; matching an executable and arguments does not constrain the server’s access or prevent environment-based code loading. Review the full launch context and server permissions independently. See [MCP identity requirements](https://learn.chatgpt.com/docs/enterprise/managed-configuration#enforce-command-rules-from-requirements).
297
298</PersistentChecklist>
299
300
301
302
303
304
305
306<a id="monitor-codex-activity"></a>
307
308
309
310### Monitor Codex activity
311
312
313
314<PersistentChecklist client:load id="codex-security-cookbook-monitor-codex-activity" itemIds={["reads-one-allowed-file", "attempts-one-blocked-file-read", "runs-one-allowed-command", "attempts-one-blocked-egress-request", "requests-and-receives-a-denied-approval", "ends-the-session", "otel-shows-runtool-events", "hooks-show-the-lifecycle", "proxy-logs-show-allowed-and-blocked-destinations", "compliance-ingestion-receives-supported-records", "the-siem-correlates-them-to-the-user-or-workload"]} description="This browser could not save your verification progress.">
315
316Use this recipe when your security team needs to understand what Codex is doing on endpoints and in your network. It brings together run and tool events, approval decisions, workspace audit records, and network logs for investigation.
317
318> **Intended result**
319>
320> Security teams can correlate Codex runs, tool decisions, approvals, workspace audit records, and actual network egress without relying on the model’s own narrative.
321
322#### Use four evidence sources
323
3241. Codex OpenTelemetry: run, API, model, sandbox, approval, and tool events.
325
3262. Managed lifecycle hooks: endpoint-side records around tool use and permission requests.
327
3283. Network proxy or gateway logs: actual DNS/HTTP destinations and connection outcomes.
329
3304. Compliance API: workspace-level audit and investigation records.
331
332No single source provides complete network monitoring.
333
334#### Configure
335
3361. Send Codex OpenTelemetry logs to your collector, with raw prompts redacted:
337
338```toml
339 [otel]
340 environment = "enterprise"
341 log_user_prompt = false
342
343 [otel.exporter.otlp-http]
344 endpoint = "https://otel.example.com/v1/logs"
345 protocol = "binary"
346 headers = { "x-otlp-api-key" = "${OTLP_TOKEN}" }
347```
348
349 Provide `OTLP_TOKEN` to the Codex process through your approved secret manager or process environment before starting the client. Keep the token out of deployed TOML files and source control.
350
351 Codex can emit conversation starts, API requests, streaming events, prompt metadata, tool decisions, and tool results. See [Advanced Configuration](https://learn.chatgpt.com/docs/config-file/config-advanced).
352
3532. Deploy OTel outside project-local configuration. Project `.codex/config.toml` files cannot override telemetry routing.
354
3553. Add managed lifecycle hooks for `SessionStart`, `PreToolUse`, `PermissionRequest`, `PostToolUse`, `Stop`, and `SessionEnd`.
356
3574. Install hook scripts in an administrator-managed directory and set `allow_managed_hooks_only = true` if user, project, and plugin hooks should be ignored.
358
3595. Route sandboxed command traffic through an independently logged proxy. OTel records that a tool ran; the proxy records where the subprocess attempted to connect.
360
3616. Configure separate telemetry for web search, apps, connectors, MCP servers, browser activity, and Codex Cloud. Local command proxy rules do not automatically cover them.
362
3637. Stream supported Compliance API records into the SIEM and apply your own retention and legal-hold controls. See [Compliance API and audit events](https://learn.chatgpt.com/docs/enterprise/compliance-api).
364
3658. Correlate user or service account, host or runner, conversation ID, model, client version, permission profile, approval decision, destination, and tool name.
366
367#### Verify
368
369Run a controlled session that does the following:
370
371- [ ] Reads one allowed file.
372
373- [ ] Attempts one blocked file read.
374
375- [ ] Runs one allowed command.
376
377- [ ] Attempts one blocked egress request.
378
379- [ ] Requests approval and receives a denial.
380
381- [ ] Ends the session.
382
383Expected results by evidence source:
384
385- [ ] OTel shows run and tool events.
386
387- [ ] Hooks show the session lifecycle.
388
389- [ ] Proxy logs show allowed and blocked destinations.
390
391- [ ] Compliance ingestion receives supported records.
392
393- [ ] The SIEM correlates those records with the user or workload.
394
395#### Roll back
396
397Disable the exporter and hooks by restoring the previous configuration. Do not remove independent proxy logging as part of a telemetry rollback.
398
399#### Boundaries
400
401Do not promise that Codex telemetry captures every command argument, output byte, or network packet. Document the coverage of each layer.
402
403</PersistentChecklist>
404
405
406
407
408
409
410
411<a id="restrict-network-destinations"></a>
412
413
414
415### Restrict network destinations
416
417
418
419<PersistentChecklist client:load id="codex-security-cookbook-restrict-network-destinations" itemIds={["request-an-approved-domain-expected-succeeds", "request-an-unapproved-domain-expected-blocked", "request-localhosthttplocalhost-a-private-ip-and-a-cloud-metadata", "in-cloud-attempt-post-to-an-allowed-domain-expected", "confirm-proxygateway-logs-contain-both-allowed-and-denied-requests"]} description="This browser could not save your verification progress.">
420
421Use this recipe when Codex should have no internet access or should reach only approved destinations. Choose the local or cloud procedure for the environment you are configuring, and keep their separate policy scopes in mind.
422
423> **Intended result**
424>
425> Codex has no internet access, or reaches only approved domains, according to the configuration you choose. Codex Cloud (Legacy) environments can additionally restrict HTTP methods.
426
427#### Configure current Codex Cloud
428
4291. Create separate environments for different trust levels, such as no-internet-review, approved-dependencies-build, and cyber-lab.
430
4312. In the environment configuration, leave **Allow Codex to access internet** off unless the workflow needs internet access.
432
4333. When access is required, turn on **Allow Codex to access internet**. Under **Allow domains**, choose **Custom domains only**, then add exact required hosts under **Additional allowed domains**.
434
4354. Save and test the services during setup. Publish or republish the environment, then test access to approved and unapproved destinations in a new task. Review setup scripts separately: pin dependencies, prefer approved mirrors, and avoid executable downloads that have not been reviewed.
436
437See [Connect to services](https://learn.chatgpt.com/docs/environments/cloud-environments#connect-to-services). Current Codex Cloud settings do not provide the Legacy HTTP-method selector.
438
439#### Configure Codex Cloud (Legacy)
440
4411. Keep agent internet access **Off** when unnecessary. Legacy Cloud blocks internet during the agent phase by default; setup scripts still run with internet access for dependency installation.
442
4432. When agent access is required, turn it **On** with a narrow allowlist. Start with **None** and add exact domains.
444
4453. For download-only workflows, allow only `GET`, `HEAD`, and `OPTIONS`. This blocks `POST`, `PUT`, `PATCH`, `DELETE`, and other methods. Review setup scripts separately because the agent-phase restriction does not govern their internet access.
446
447See [Legacy agent internet access](https://learn.chatgpt.com/docs/cloud/internet-access). These presets and HTTP-method controls apply only to Legacy Cloud.
448
449#### Configure local Codex
450
4511. In administrator-managed `requirements.toml`, define and select the approved network profile, and restrict users to that profile:
452
453```toml
454 default_permissions = "corp-build"
455
456 [allowed_permission_profiles]
457 "corp-build" = true
458
459 [permissions.corp-build]
460 description = "Workspace access with approved dependency destinations."
461 extends = ":workspace"
462
463 [permissions.corp-build.network]
464 enabled = true
465 allow_local_binding = false
466
467 [permissions.corp-build.network.domains]
468 "registry.npmjs.org" = "allow"
469 "pypi.org" = "allow"
470 "files.pythonhosted.org" = "allow"
471```
472
4732. Deploy the reviewed requirements bundle using [Managed configuration](https://learn.chatgpt.com/docs/enterprise/managed-configuration). A named profile alone is inactive; `default_permissions` selects it, and the allowlist prevents selection of other profiles.
474
4753. Activate the administrator-managed proxy in the same requirements bundle so domain restrictions are enforced and users cannot expand them. Deploy the proxy and network-enabled profile together; a network-enabled profile without an active proxy grants direct network access without enforcing its domain rules:
476
477 > **Caution:** `[experimental_network]` is experimental and may change. Validate it on each supported local client version and operating system before broader rollout. Windows support is limited; do not apply this policy to Windows users until you have tested enforcement in your environment.
478
479```toml
480 [experimental_network]
481 enabled = true
482 managed_allowed_domains_only = true
483
484 [experimental_network.domains]
485 "registry.npmjs.org" = "allow"
486 "pypi.org" = "allow"
487 "files.pythonhosted.org" = "allow"
488```
489
4904. Keep local port binding off unless the workflow needs it. Configure access to private network targets separately; allowing a domain does not by itself approve its private addresses. See [Network access requirements](https://learn.chatgpt.com/docs/enterprise/managed-configuration#configure-network-access-requirements).
491
4925. Configure web search, apps, MCP servers, browser activity, and Codex Cloud separately. Local sandbox rules do not control them.
493
494#### Verify
495
496- [ ] Send a request to an approved domain and confirm that it succeeds.
497
498- [ ] Send a request to an unapproved domain and confirm that it is blocked.
499
500- [ ] Send requests to [localhost](http://localhost), a private IP, and a cloud metadata endpoint. Confirm that each request is blocked unless explicitly approved.
501
502- [ ] In Codex Cloud (Legacy), attempt a `POST` request to an allowed domain. Confirm that the request is blocked when only `GET`, `HEAD`, and `OPTIONS` are allowed.
503
504- [ ] Confirm that proxy or gateway logs contain entries for both allowed and denied requests.
505
506#### Roll back
507
508Return the environment to Off or restore the prior domain policy. Avoid rolling back to unrestricted internet.
509
510#### Boundaries
511
512An approved full sandbox escalation can bypass the local command proxy. Test ordinary sandboxed commands and approved escalated commands separately before relying on the restriction.
513
514An allowed domain can still host prompt injection, compromised packages, or unsafe documentation.
515
516</PersistentChecklist>
517
518
519
520
521
522
523
524<a id="protect-secrets-and-unrelated-repositories"></a>
525
526
527
528### Protect secrets and unrelated repositories
529
530
531
532<PersistentChecklist client:load id="codex-security-cookbook-protect-secrets-and-unrelated-repositories" itemIds={["read-a-normal-file", "read-each-canary", "search-recursively-for-secrets", "print-its-command-environment", "read-a-neighboring-repository"]} description="This browser could not save your verification progress.">
533
534Use this recipe when Codex needs access to approved source directories while secrets, `.env` files, SSH keys, and unrelated repositories should remain out of reach. It covers restricting file access and reducing the number of credential-bearing environment variables passed to spawned commands.
535
536> **Intended result**
537>
538> Codex can work in approved source directories but cannot read common secret-bearing files or inherit a broad set of credential-bearing environment variables.
539
540#### Configure
541
5421. Define deny-read patterns in a workspace profile:
543
544```toml
545 [permissions.corp-workspace]
546 description = "Workspace edits with secret-bearing paths denied."
547 extends = ":workspace"
548
549 [permissions.corp-workspace.filesystem]
550 glob_scan_max_depth = 6
551
552 [permissions.corp-workspace.filesystem.":workspace_roots"]
553 "**/.env*" = "deny"
554 "**/*.pem" = "deny"
555 "**/*.p12" = "deny"
556 "**/id_*" = "deny"
557 "**/.aws/**" = "deny"
558 "**/.kube/**" = "deny"
559```
560
5612. Add exact deny paths for known credential directories, signing keys, production configuration, and neighboring repositories.
562
5633. In the system `/etc/codex/config.toml` or the user `~/.codex/config.toml`, reduce the environment variables passed to spawned commands:
564
565```toml
566 [shell_environment_policy]
567 inherit = "core"
568 ignore_default_excludes = false
569
570 [shell_environment_policy.filters]
571 "AWS_*" = "exclude"
572 "AZURE_*" = "exclude"
573 "GITHUB_*" = "exclude"
574 "*_TOKEN" = "exclude"
575 "*_SECRET" = "exclude"
576 "*_PASSWORD" = "exclude"
577```
578
579 This is a user-configurable default, not an enforced requirement. It reduces inherited variables but does not prevent access to credential files, helpers, shell startup content, or other identities. See [Advanced Configuration](https://learn.chatgpt.com/docs/config-file/config-advanced).
580
5814. In the administrator-managed `requirements.toml`, select the restricted profile and allow only that profile. Built-in profiles do not inherit its secret-file denies:
582
583```toml
584 default_permissions = "corp-workspace"
585
586 [allowed_permission_profiles]
587 "corp-workspace" = true
588 ":danger-full-access" = false
589```
590
5915. Choose the credential flow for your Cloud environment:
592 - In current Codex Cloud, use **Network secrets** for credentials sent to an allowed HTTPS service. Programs receive a placeholder, and the proxy substitutes the real value for approved destinations on port 443 during setup and tasks. The raw credential is not placed in a local process or file. Direct environment variables are available to programs. See [Environment variables and network secrets](https://learn.chatgpt.com/docs/environments/cloud-environments#configure-environment-variables-and-network-secrets).
593 - In Codex Cloud (Legacy), secrets support setup-time authentication and are removed before the agent phase; environment variables remain for the full chat. See [Legacy Cloud environments](https://learn.chatgpt.com/docs/environments/cloud-environment).
594
5956. If the agent needs authentication, use an independent broker to provide short-lived credentials scoped to the exact target and action. See [Recommended configuration](https://learn.chatgpt.com/docs/cyber-safety/recommended-configuration).
596
597#### Verify
598
599Create harmless canaries:
600
601```plain text
602.env.test
603test-key.pem
604.aws/credentials
605.kube/config
606```
607
608Then ask Codex to:
609
610- [ ] Read a normal file.
611
612- [ ] Read each canary.
613
614- [ ] Search recursively for secrets.
615
616- [ ] Print its command environment.
617
618- [ ] Read a neighboring repository.
619
620Expected: Reading a normal file succeeds; denied files and neighboring repositories remain inaccessible, and excluded variables are absent from the command environment.
621
622#### Roll back
623
624Restore the prior profile, but retain high-confidence secret-file denies while investigating workflow failures.
625
626#### Boundaries
627
628Filesystem denies do not remove secrets already pasted into prompts, committed to readable source, returned by connectors, or written into model-accessible logs.
629
630</PersistentChecklist>
631
632
633
634
635
636
637
638<a id="require-approval-for-sensitive-commands"></a>
639
640
641
642### Require approval for sensitive commands
643
644
645
646<PersistentChecklist client:load id="codex-security-cookbook-require-approval-for-sensitive-commands" itemIds={["run-git-status-expected-no-sensitiveaction-approval", "run-git-push-expected-approval-prompt", "run-terraform-apply-expected-approval-prompt", "run-kubectl-delete-pod-test-expected-approval-prompt", "run-rm-rf-testdirectory-expected-forbidden", "deny-one-approval-expected-command-does-not-run-and", "restart-the-client-and-repeat-expected-rule-remains-enforced"]} description="This browser could not save your verification progress.">
647
648Use this recipe when actions such as `git push`, deployments, database migrations, and cloud CLI commands need approval before they run. Decide which commands should prompt for review and which must be forbidden, then configure rules for those commands.
649
650> **Intended result**
651>
652> Sensitive commands consistently pause for approval or are forbidden, even when the model believes they are appropriate.
653
654#### Configure
655
6561. Decide which commands require a prompt and which are forbidden.
657
658| Command class | Decision |
659| ---------------------------- | --------- |
660| `git push`, `git commit` | Prompt |
661| `terraform apply` | Prompt |
662| `kubectl apply/delete/patch` | Prompt |
663| database migrations | Prompt |
664| destructive host commands | Forbidden |
665
6662. Add admin-enforced command rules:
667
668```toml
669 [rules]
670 prefix_rules = [
671 { pattern = [{ token = "git" }, { any_of = ["push", "commit"] }], decision = "prompt", justification = "Require review before mutating shared Git state." },
672 { pattern = [{ token = "terraform" }, { token = "apply" }], decision = "prompt", justification = "Require review before infrastructure changes." },
673 { pattern = [{ token = "kubectl" }, { any_of = ["apply", "delete", "patch"] }], decision = "prompt", justification = "Require review before Kubernetes changes." },
674 { pattern = [{ token = "rm" }], decision = "forbidden", justification = "Destructive removal is not permitted in managed Codex workflows." },
675 ]
676```
677
678 Requirements rules can require a prompt or forbid a command; they cannot allow one. See [Managed configuration](https://learn.chatgpt.com/docs/enterprise/managed-configuration).
679
6803. For high-risk profiles, add a shell-wrapper entry to the same `prefix_rules` array when wrappers could obscure a sensitive command:
681
682```toml
683 { pattern = [{ any_of = ["bash", "sh", "zsh"] }], decision = "prompt", justification = "Review shell wrappers that could obscure sensitive commands." },
684```
685
686 The shell-wrapper entry is noisy; use it selectively.
687
6884. If a person must approve commands, enforce these settings:
689
690```toml
691 allowed_approval_policies = ["on-request", "untrusted"]
692 allowed_approvals_reviewers = ["user"]
693```
694
695 Auto-review changes the reviewer; it is not a deterministic security guarantee. See [Auto-review](https://learn.chatgpt.com/docs/sandboxing/auto-review).
696
6975. Send PermissionRequest, tool-decision, and tool-result events to the security pipeline.
698
699#### Verify
700
701- [ ] Run `git status`. Expected: No approval prompt appears.
702
703- [ ] Run `git push`. Expected: An approval prompt appears.
704
705- [ ] Run `terraform apply`. Expected: An approval prompt appears.
706
707- [ ] Run `kubectl delete pod test`. Expected: An approval prompt appears.
708
709- [ ] Run `rm -rf test-directory`. Expected: The command is forbidden.
710
711- [ ] Deny one approval request. Expected: The command does not run, and the denial is logged.
712
713- [ ] Restart the client and repeat the checks. Expected: The rule remains enforced.
714
715#### Roll back
716
717Restore the prior requirements bundle. Narrow an overly broad token pattern instead of removing an entire sensitive command class.
718
719#### Boundaries
720
721Prefix rules are not a complete operating-system policy engine. Pair them with sandboxing, least-privilege OS accounts, network restrictions, cloud IAM, branch protection, deployment approvals, and database authorization.
722
723</PersistentChecklist>
724
725
726
727
728
729## Combined recipes for enterprise security goals
730
731These examples show how to combine controls for common enterprise security goals. Test the configuration on each client and environment in scope.
732
733
734
735<a id="let-developers-use-codex-while-protecting-production"></a>
736
737
738
739### Let developers use Codex while protecting production
740
741
742
743<PersistentChecklist client:load id="codex-security-cookbook-let-developers-use-codex-while-protecting-production" itemIds={["run-a-normal-workspace-task-expected-the-normal-task", "attempt-a-representative-sensitive-command-and-deny-the-approval", "verify-the-codex-identity-cannot-obtain-production-write-access", "restart-the-client-and-repeat"]} description="This browser could not save your verification progress.">
744
745**Uses these configuration recipes:** Restrict permission profiles and Full Access; Protect secrets and unrelated repositories; Require approval for sensitive commands.
746
747Developers have legitimate production access, but you want Codex to help with development without making unreviewed production changes.
748
749- Define an approved workspace profile and enforce the allowed profiles and approval policies. Deliver these settings as administrator-enforced requirements to prevent users from overriding them.
750
751- Run Codex under a separate, lower-privilege identity or in an environment that cannot access or obtain production write credentials. Developers can retain production access through a separate authorized workflow.
752
753- Minimize environment variables passed to spawned commands. If the agent needs authentication, use an independent broker to provide short-lived credentials scoped to the exact target and action.
754
755- Decide which commands require an approval prompt and which are forbidden. Require a person to approve selected high-impact commands, such as `git push`, `terraform apply`, Kubernetes changes, and database migrations; forbid commands that must never run.
756
757- Keep production IAM, branch protection, and deployment approvals enforced independently of Codex.
758
759#### Restrict permission profiles and require a human reviewer
760
761In the administrator-managed `requirements.toml`, allow the built-in workspace and read-only profiles and require user review for actions that need approval:
762
763```toml
764allowed_approval_policies = ["on-request"]
765allowed_approvals_reviewers = ["user"]
766default_permissions = ":workspace"
767
768[allowed_permission_profiles]
769":read-only" = true
770":workspace" = true
771```
772
773The workspace profile permits work in the workspace within its sandbox; the read-only profile is available for narrower work. Full-access profiles are not on the allowlist.
774
775Set `approval_policy = "on-request"` in the client configuration if the client needs an explicit compatible choice. `on-request` permits an approval prompt when required; it does not prompt for every command. Add explicit rules for the actions that need review, and retain independent production authorization.
776
777Managed profile allowlists require Codex 0.138.0 or later; use and test the documented legacy sandbox-mode requirements for older clients.
778
779Restart each pilot client, verify the approved profile works, and verify a disallowed full-access profile is rejected.
780
781#### Separate the Codex identity from production credentials
782
783In your device management or development environment provisioning system, create a dedicated Codex user, container, or development host with access only to the source and development services it needs. Do not mount the developer's production credential files, token helpers, or keychains into that environment, and prevent the identity from acquiring production write roles.
784
785If the agent needs service access, configure your organization's credential broker to issue short-lived credentials restricted to the development resource and action. The developer can use their normal identity through a separate authorized production workflow.
786
787For example, in AWS IAM, open **Roles**, select the Codex development role, and under **Permissions** add a reviewed inline policy like this one, replacing the account and role with your own:
788
789```json
790{
791 "Version": "2012-10-17",
792 "Statement": [
793 {
794 "Sid": "DenyAssumeExampleProductionRole",
795 "Effect": "Deny",
796 "Action": "sts:AssumeRole",
797 "Resource": "arn:aws:iam::111122223333:role/ExampleProductionRole"
798 }
799 ]
800}
801```
802
803This example denies attempts to assume the named role, but it does not deny attempts to assume other roles. Also remove direct production permissions, restrict other production roles and credential sources, and review trust policies and organization-level controls.
804
805Test the development role against representative development and production permissions without making a production change. See [AWS IAM role policy examples](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_policy-examples.html).
806
807#### Minimize the shell environment
808
809In `/etc/codex/config.toml` for a system-wide starting configuration, or `~/.codex/config.toml` for one user, configure which environment variables spawned commands inherit:
810
811```toml
812[shell_environment_policy]
813inherit = "core"
814ignore_default_excludes = false
815
816[shell_environment_policy.filters]
817"AWS_*" = "exclude"
818"AZURE_*" = "exclude"
819"GITHUB_*" = "exclude"
820"*_TOKEN" = "exclude"
821"*_SECRET" = "exclude"
822"*_PASSWORD" = "exclude"
823```
824
825This is a configurable default, not an enforced credential boundary. User settings, shell startup or snapshots, credential helpers, and other credential sources may still expose credentials.
826
827In `requirements.toml`, use the following optional restrictions to disable login shells and shell snapshots if they are compatible with your workflow:
828
829```toml
830allow_login_shell = false
831
832[features]
833shell_snapshot = false
834```
835
836Pilot these settings because some workflows depend on login shell initialization.
837
838Start Codex with harmless synthetic variables matching the filters and inspect a spawned command's environment; also verify a login shell request is rejected if disabled. Independently verify that the execution identity cannot access real production credentials. See [Shell environment policy](https://learn.chatgpt.com/docs/config-file/config-advanced#shell-environment-policy).
839
840#### Prompt for selected high-impact commands
841
842Add requirements rules for the commands your organization uses. For example:
843
844```toml
845[rules]
846prefix_rules = [
847 { pattern = [{ token = "git" }, { token = "push" }], decision = "prompt", justification = "Review Git pushes." },
848 { pattern = [{ token = "terraform" }, { token = "apply" }], decision = "prompt", justification = "Review infrastructure changes." },
849 { pattern = [{ token = "kubectl" }, { any_of = ["apply", "delete", "patch"] }], decision = "prompt", justification = "Review cluster changes." },
850 { pattern = [{ token = "python3" }, { token = "manage.py" }, { token = "migrate" }], decision = "prompt", justification = "Review database migrations." },
851]
852```
853
854Use `forbidden` for a command that must never run; replace or add patterns for the actual deployment and migration tools.
855
856These rules match command prefixes and do not cover every equivalent action. Test absolute paths, wrappers, alternate argument orders, other clients, and other tools separately.
857
858On a disposable target, run a normal command, request a listed action, deny the approval request, and verify the action did not execute. See [Enforcing command rules](https://learn.chatgpt.com/docs/enterprise/managed-configuration#enforce-command-rules-from-requirements).
859
860#### Enforce repository and deployment approvals outside Codex
861
862In your source-control platform, protect production branches such as `main`: require reviewed changes and the necessary approvals and status checks, and review which roles can bypass those requirements. Configure deployment approval in the relevant deployment system and database privileges in the database itself.
863
864Use a disposable repository to verify a reviewed change works and an unauthorized direct push is rejected. These controls apply regardless of whether an action was requested through Codex.
865
866#### Verify
867
868On a disposable test target:
869
870- [ ] Run a normal workspace task. Expected: The task succeeds.
871
872- [ ] Attempt a representative sensitive command and deny the approval request. Expected: The denied command does not run.
873
874- [ ] Verify the Codex identity cannot obtain production write access.
875
876- [ ] Restart the client and repeat.
877
878#### Boundaries
879
880Prefix rules are not a complete operating system policy engine. Pair them with sandboxing, least-privilege OS accounts, network restrictions, cloud IAM, branch protection, deployment approvals, and database authorization.
881
882</PersistentChecklist>
883
884
885
886
887
888
889
890<a id="reduce-the-risk-of-publishing-internal-code"></a>
891
892
893
894### Reduce the risk of publishing internal code
895
896
897
898<PersistentChecklist client:load id="codex-security-cookbook-reduce-the-risk-of-publishing-internal-code" itemIds={["create-harmless-canaries-and-confirm-denied-file-reads-fail", "if-network-access-is-enabled-check-that-an-approved", "with-a-harmless-marker-and-disposable-test-accounts-or"]} description="This browser could not save your verification progress.">
899
900**Uses these configuration recipes:** Protect secrets and unrelated repositories; Restrict network destinations; Allow only approved MCP servers; Require approval for sensitive commands.
901
902Developers use Codex with internal repositories, and you want to reduce the risk of code being sent to a public repository, public code snippet, or other unapproved destination.
903
904- Add exact deny paths for known credential directories and unrelated repositories, and minimize environment variables passed to spawned commands.
905
906- When agent internet access is required, use a narrow allowlist of approved domains. Allow only approved MCP server names and identities, and require review for selected publishing commands or deny them, such as `git push`.
907
908- Use source control hooks and permissions, policies for public repositories and code snippets, and data loss prevention controls appropriate to your organization.
909
910#### Deny access to unrelated repositories and credential files
911
912In administrator-managed `requirements.toml`, add exact paths that the local agent must not read:
913
914```toml
915[permissions.filesystem]
916deny_read = ["~/.ssh", "~/.aws/credentials", "~/.kube/config", "/work/unrelated-repository"]
917```
918
919Replace the examples with paths present on your managed devices. This managed deny applies across allowed permission profiles; a deny configured only inside one custom profile does not protect users who select a different profile. Prefer exact known paths and test any glob patterns at the depths you need.
920
921On native Windows, managed `deny_read` covers direct file tools but does not by itself prevent shell subprocess reads; use OS identities and file access control lists as well.
922
923Create harmless canary files in the denied locations. Under every allowed profile, verify that direct and shell reads of those files are denied and that an approved source file can be read. See [Managed deny-read requirements](https://learn.chatgpt.com/docs/enterprise/managed-configuration#enforce-deny-read-requirements).
924
925#### Minimize credential-bearing shell variables
926
927In `/etc/codex/config.toml` or `~/.codex/config.toml`, set a narrow inherited environment:
928
929```toml
930[shell_environment_policy]
931inherit = "core"
932ignore_default_excludes = false
933
934[shell_environment_policy.filters]
935"AWS_*" = "exclude"
936"AZURE_*" = "exclude"
937"GITHUB_*" = "exclude"
938"*_TOKEN" = "exclude"
939"*_SECRET" = "exclude"
940"*_PASSWORD" = "exclude"
941```
942
943This setting reduces inherited variables but is user-configurable and does not prevent access to credential files, helpers, shell startup content, or other identities.
944
945Where appropriate, an administrator can also add these restrictions to `requirements.toml` and pilot them to assess their effect on developer workflows:
946
947```toml
948allow_login_shell = false
949
950[features]
951shell_snapshot = false
952```
953
954Test with synthetic canary variables and a separate least-privilege execution identity. Do not use real credentials. See [Shell environment policy](https://learn.chatgpt.com/docs/config-file/config-advanced#shell-environment-policy).
955
956#### Allow only approved network destinations for local commands
957
958If local sandboxed commands need network access, merge both the profile and managed-proxy settings below into `requirements.toml`, replacing the domains with approved endpoints:
959
960```toml
961default_permissions = "corp_network"
962
963[allowed_permission_profiles]
964corp_network = true
965
966[permissions.corp_network]
967extends = ":workspace"
968
969[permissions.corp_network.network]
970enabled = true
971
972[experimental_network]
973enabled = true
974managed_allowed_domains_only = true
975
976[experimental_network.domains]
977"git.corp.example" = "allow"
978"registry.corp.example" = "allow"
979```
980
981The network-enabled profile grants the sandboxed command a network path, while the managed proxy restricts its destinations. Deploy them together; a network-enabled profile without the intended managed proxy does not apply the same destination restrictions. If internet access is unnecessary, keep network access disabled.
982
983Do not combine the canonical `domains` table with legacy `allowed_domains` or `denied_domains` settings; migrate and test the existing policy.
984
985Restart the client and verify that an approved endpoint works and an unapproved endpoint is blocked.
986
987These settings are experimental; validate OS and client support, and configure private-network access separately if an approved host resolves to a private address. An approved full sandbox escalation can bypass the command proxy.
988
989This network control for local commands does not by itself cover web search, apps, MCP servers, browser activity, or cloud environments, which need separate policies. An approved hosting domain may also contain public repositories or public code snippets, so destination allowlisting alone is not content protection. See [Network access requirements](https://learn.chatgpt.com/docs/enterprise/managed-configuration#configure-network-access-requirements).
990
991#### Configure cloud environment networking separately
992
993In the current Codex Cloud interface, open the environment configuration. Leave **Allow Codex to access internet** off. If you need internet access, turn it on and choose **Custom domains only**, add the exact required hosts under **Additional allowed domains**, then save the configuration, publish or republish the environment, and test a new task.
994
995Review service authentication and capabilities independently.
996
997Legacy Codex Cloud uses a separate per-environment agent internet setting with controls for allowed domains and HTTP methods; setup scripts have their own access behavior.
998
999Test the environment version you actually use. See [Connect to services](https://learn.chatgpt.com/docs/environments/cloud-environments#connect-to-services) and [legacy internet settings](https://learn.chatgpt.com/docs/cloud/internet-access).
1000
1001#### Restrict MCP servers and prompt before Git pushes
1002
1003Allow only approved local MCP server identities in `requirements.toml`. For example:
1004
1005```toml
1006[mcp_servers.internal]
1007identity = { url = "https://mcp.corp.example/mcp" }
1008```
1009
1010Configure the actual server separately in the user or managed-default `config.toml`, using the matching name and URL and the authentication required by your server:
1011
1012```toml
1013[mcp_servers.internal]
1014url = "https://mcp.corp.example/mcp"
1015```
1016
1017The requirement only permits a matching server and does not create it. Review that server's own access and tools.
1018
1019Verify that the approved server can connect and that a server with a different name or URL is not allowed.
1020
1021Add a managed command rule to request review before a representative publishing command:
1022
1023```toml
1024[rules]
1025prefix_rules = [
1026 { pattern = [{ token = "git" }, { token = "push" }], decision = "prompt", justification = "Review the destination before publishing." },
1027]
1028```
1029
1030Pair this with `allowed_approval_policies = ["on-request"]` and `allowed_approvals_reviewers = ["user"]` as top-level requirements if a person must review.
1031
1032Deny a test push and confirm it does not run.
1033
1034Prefix matching does not cover every wrapper, API client, app, or alternate publishing path.
1035
1036#### Restrict public publishing with source control and DLP controls
1037
1038In your source control platform, restrict public repository creation and limit who can change repository visibility, where supported. Review bypass permissions for privileged users. Separately check personal accounts, code snippets, external organizations, and token access: organization settings do not govern every destination.
1039
1040In the relevant repository, require reviewed changes and review branch protection and bypass permissions.
1041
1042In your organization's DLP console, configure a pilot policy for sensitive source-code content and the upload or repository channels your DLP product actually covers; set the action to block and test with an inert synthetic marker.
1043
1044Where the source-control platform supports server-side controls, configure and enforce reviewed hooks for repositories receiving pushes.
1045
1046Use authorized test destinations and confirm blocking before any publication.
1047
1048#### Verify
1049
1050- [ ] Create harmless canaries and confirm that attempts to read denied files fail.
1051
1052- [ ] If network access is enabled, check that an approved destination succeeds and an unapproved destination is blocked; otherwise, verify network requests are blocked.
1053
1054- [ ] With a harmless marker and disposable test accounts or destinations, verify representative attempts to publish publicly are blocked before publication.
1055
1056#### Boundaries
1057
1058An allowed domain can still host public content. Configure web search, apps, MCP servers, browser activity, and Codex Cloud separately; local sandbox rules do not control them.
1059
1060</PersistentChecklist>
1061
1062
1063
1064
1065
1066
1067
1068<a id="keep-codex-from-changing-its-own-safeguards"></a>
1069
1070
1071
1072### Keep Codex from changing its own safeguards
1073
1074
1075
1076<PersistentChecklist client:load id="codex-security-cookbook-keep-codex-from-changing-its-own-safeguards" itemIds={["confirm-a-benign-bash-command-runs", "confirm-a-command-containing-codexhooktestdeny-is-blocked", "verify-that-the-policy-and-dependencies-cannot-be-changed", "attempt-to-edit-the-managed-policy-expected-the-managed", "attempt-to-weaken-a-restriction-through-user-or-project", "restart-the-client-and-verify-the-restrictions-still-apply"]} description="This browser could not save your verification progress.">
1077
1078**Uses these configuration recipes:** Restrict permission profiles and Full Access; Allow only approved MCP servers; Restrict network destinations; Protect secrets and unrelated repositories; Require approval for sensitive commands.
1079
1080You want the restrictions governing Codex to remain in force when it works in a repository or a user changes local configurations.
1081
1082- Deliver supported permission, approval, MCP, network, and command restrictions as administrator-enforced requirements.
1083
1084- Install hook scripts in an administrator-managed directory and use managed-only hooks if user, project, and plugin hooks should be ignored.
1085
1086- Protect policy files and the configuration distribution path with operating system and device management controls. Run Codex without the privileges needed to change those protections.
1087
1088- Review the effective configuration for each client and environment in scope.
1089
1090#### Enforce the permitted profiles, approvals, server identities, and commands
1091
1092Merge reviewed settings into the administrator-managed `requirements.toml`, rather than relying on a user's or repository's editable settings. For example:
1093
1094```toml
1095allowed_approval_policies = ["on-request"]
1096allowed_approvals_reviewers = ["user"]
1097default_permissions = ":workspace"
1098
1099[allowed_permission_profiles]
1100":read-only" = true
1101":workspace" = true
1102
1103[mcp_servers.internal]
1104identity = { url = "https://mcp.corp.example/mcp" }
1105
1106[rules]
1107prefix_rules = [
1108 { pattern = [{ token = "git" }, { token = "push" }], decision = "prompt", justification = "Review Git pushes." },
1109]
1110```
1111
1112This example permits two profiles, requires a human reviewer when approval is needed, permits a matching MCP identity, and prompts for a particular command prefix.
1113
1114Configure the MCP server itself separately and add the organization's other sensitive command patterns. Add global file restrictions and network restrictions when needed; requirements constrain supported settings but cannot substitute for the identity, service, and destination controls outside Codex.
1115
1116Managed profile allowlists require Codex 0.138.0 or later.
1117
1118On a pilot client, attempt to select a disallowed profile, change a restricted user or project setting, and use a non-allowlisted MCP identity; verify each is rejected or remains constrained after restart. Deny a test command approval and verify the command does not run.
1119
1120#### Add managed filesystem and network restrictions
1121
1122To deny sensitive local files across allowed profiles, add the following table to `requirements.toml` and replace the examples with your organization's paths:
1123
1124```toml
1125[permissions.filesystem]
1126deny_read = ["~/.ssh", "~/.aws/credentials", "~/.kube/config"]
1127```
1128
1129Where sandboxed local commands need network access, define an allowed network-enabled profile and managed destinations together:
1130
1131```toml
1132default_permissions = "corp_network"
1133
1134[allowed_permission_profiles]
1135corp_network = true
1136
1137[permissions.corp_network]
1138extends = ":workspace"
1139
1140[permissions.corp_network.network]
1141enabled = true
1142
1143[experimental_network]
1144enabled = true
1145managed_allowed_domains_only = true
1146
1147[experimental_network.domains]
1148"git.corp.example" = "allow"
1149```
1150
1151Choose this profile policy in place of the earlier `:workspace` default, or merge both profile names deliberately; do not paste duplicate keys or tables. If commands do not need network, keep it disabled.
1152
1153Verify exact denied paths using harmless files, and verify approved and unapproved destinations.
1154
1155On native Windows, also use file access control lists because shell subprocess reads are not covered by managed `deny_read` alone.
1156
1157Local command networking does not govern other clients, apps, connectors, or cloud execution.
1158
1159#### Install and enforce administrator-managed hooks
1160
1161Have an administrator install a reviewed hook program in a root-managed directory, such as `/etc/codex/hooks/pre_tool_use_policy.py`, and prevent the Codex user from changing the file or its directory. In `requirements.toml`, enable hooks and ignore user, project, session, and plugin hooks:
1162
1163```toml
1164allow_managed_hooks_only = true
1165
1166[features]
1167hooks = true
1168
1169[hooks]
1170managed_dir = "/etc/codex/hooks"
1171
1172[[hooks.PreToolUse]]
1173matcher = "^Bash$"
1174
1175[[hooks.PreToolUse.hooks]]
1176type = "command"
1177command = "/usr/bin/python3 /etc/codex/hooks/pre_tool_use_policy.py"
1178timeout = 30
1179statusMessage = "Checking managed Bash command"
1180```
1181
1182The requirement registers the hook but does not install it. For a local macOS integration test, save the following as `pre_tool_use_policy.py` and have an administrator install it with the commands below.
1183
1184It blocks only the synthetic marker; replace it with a reviewed organization-specific policy before relying on it. Confirm that `/usr/bin/python3` exists on the target and adjust the interpreter path if necessary:
1185
1186```python
1187import json
1188import sys
1189
1190event = json.load(sys.stdin)
1191command = event.get("tool_input", {}).get("command", "")
1192if "CODEX_HOOK_TEST_DENY" in command:
1193 print("Synthetic test command blocked by managed hook.", file=sys.stderr)
1194 sys.exit(2)
1195```
1196
1197```bash
1198sudo install -d -o root -g wheel -m 755 /etc/codex/hooks
1199sudo install -o root -g wheel -m 644 ./pre_tool_use_policy.py /etc/codex/hooks/pre_tool_use_policy.py
1200```
1201
1202Restart the local client, then:
1203
1204- [ ] Confirm a benign Bash command runs.
1205
1206- [ ] Confirm a command containing `CODEX_HOOK_TEST_DENY` is blocked.
1207
1208- [ ] Verify that the policy and dependencies cannot be changed by the Codex user.
1209
1210`allow_managed_hooks_only` skips all non-managed user, project, session, and plugin hooks and may affect other workflows.
1211
1212The Bash matcher does not cover every tool or input supplied later to an existing process; errors, timeouts, or malformed hook responses can allow a tool call to proceed.
1213
1214Cloud-orchestrated Work uses different supported hook handlers. Keep independent OS and service protections. See [Managed hooks](https://learn.chatgpt.com/docs/enterprise/managed-configuration#enforce-managed-hooks-from-requirements) and [hook behavior](https://learn.chatgpt.com/docs/hooks#pretooluse).
1215
1216#### Protect the policy and its distribution
1217
1218On macOS, an administrator can deploy a reviewed and merged requirements file in a root-owned parent directory and prevent the Codex user from writing to the file:
1219
1220```bash
1221sudo install -d -o root -g wheel -m 755 /etc/codex
1222sudo install -o root -g wheel -m 644 ./requirements.reviewed.toml /etc/codex/requirements.toml
1223ls -ld /etc/codex
1224ls -l /etc/codex/requirements.toml
1225```
1226
1227On Linux, use the appropriate administrator group; on Windows, protect the ProgramData directory and file with administrator-managed access control lists. Restrict who can change the MDM or cloud policy, review changes, and preserve a rollback copy.
1228
1229Using a non-admin pilot account, verify that the account cannot modify the policy, parent directory, or managed hook program; try a weaker user or project setting, restart Codex, and verify the enforced behavior persists.
1230
1231Test each client and environment in scope rather than assuming all clients load the same policy.
1232
1233#### Verify
1234
1235Using a non-admin test identity:
1236
1237- [ ] Attempt to edit the managed policy. Expected: The managed file cannot be changed.
1238
1239- [ ] Attempt to weaken a restriction through user or project configuration. Expected: Weaker settings are rejected or remain constrained.
1240
1241- [ ] Restart the client and verify the restrictions still apply.
1242
1243#### Boundaries
1244
1245A user or agent with sufficient host administrator privileges may be able to change the surrounding protections. User-editable settings alone are not a substitute for administrator-managed requirements.
1246
1247</PersistentChecklist>
1248
1249
1250
1251
1252
1253## Source pages
1254
1255- [Configuration Reference](https://learn.chatgpt.com/docs/config-file/config-reference)
1256
1257- [Managed configuration](https://learn.chatgpt.com/docs/enterprise/managed-configuration)
1258
1259- [Advanced Configuration](https://learn.chatgpt.com/docs/config-file/config-advanced)
1260
1261- [Authentication](https://learn.chatgpt.com/docs/auth)
1262
1263- [Sandboxing](https://learn.chatgpt.com/docs/sandboxing)
1264
1265- [Auto-review](https://learn.chatgpt.com/docs/sandboxing/auto-review)
1266
1267- [Agent approvals and security](https://learn.chatgpt.com/docs/agent-approvals-security)
1268
1269- [Agent internet access](https://learn.chatgpt.com/docs/cloud/internet-access)
1270
1271- [Cloud environments](https://learn.chatgpt.com/docs/environments/cloud-environment)
1272
1273- [Recommended configuration](https://learn.chatgpt.com/docs/cyber-safety/recommended-configuration)
1274
1275- [Compliance API and audit events](https://learn.chatgpt.com/docs/enterprise/compliance-api)