4423| `codex-security scans` | List, inspect, compare, and retrieve saved scan logs. |4423| `codex-security scans` | List, inspect, compare, and retrieve saved scan logs. |
4424| `codex-security findings` | Review and update saved security findings. |4424| `codex-security findings` | Review and update saved security findings. |
4425| `codex-security export` | Export completed findings as CSV, JSON, or SARIF. |4425| `codex-security export` | Export completed findings as CSV, JSON, or SARIF. |
4426| `codex-security publish` | Publish completed scan findings to Linear. |
4426| `codex-security validate` | Check one or more candidate security findings. |4427| `codex-security validate` | Check one or more candidate security findings. |
4427| `codex-security patch` | Patch one or more security issues. |4428| `codex-security patch` | Patch one or more security issues. |
4428| `codex-security login` | Sign in, store credentials, or check sign-in status. |4429| `codex-security login` | Sign in, store credentials, or check sign-in status. |
5099 --output /path/outside/repository/exports/findings.csv5100 --output /path/outside/repository/exports/findings.csv
5100```5101```
5101 5102
5103#### `codex-security publish scan`
5104
5105Publish every finding from a completed scan to Linear:
5106
5107```text
5108usage: codex-security publish scan [SCAN_DIR] --to linear
5109 [--linear-team TEAM_ID]
5110 [--project PROJECT_ID]
5111 [--linear-api-key KEY]
5112 [--linear-assignee EMAIL_OR_USER_ID]
5113 [--dry-run] [--json]
5114```
5115
5116`SCAN_DIR` must contain a completed, sealed scan. Omit it in an interactive
5117terminal to select a completed scan from local scan history. Creating issues
5118also requires the scan and its findings to exist in local scan history. A dry
5119run validates the sealed artifacts without this persistence check.
5120
5121| Argument | Description |
5122| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5123| `--to linear` | Publish to Linear. This argument is required. |
5124| `--linear-team TEAM_ID` | Select the Linear team. Uses `CODEX_SECURITY_LINEAR_TEAM` when omitted; one of them is required. |
5125| `--project PROJECT_ID` | Select a Linear project. Uses `CODEX_SECURITY_LINEAR_PROJECT` when omitted. If neither is set, issues are created directly in the team. |
5126| `--linear-api-key KEY` | Use a Linear personal API key for direct publication. Uses `CODEX_SECURITY_LINEAR_API_KEY` when omitted. |
5127| `--linear-assignee EMAIL_OR_USER_ID` | Assign created issues by email address or Linear user ID. Requires `--linear-api-key` or `CODEX_SECURITY_LINEAR_API_KEY`. Issues remain unassigned when omitted. |
5128| `--dry-run` | Prepare issue payloads without starting Codex, contacting Linear, creating issues, or writing publication state. |
5129| `--json` | Write structured publication results to stdout. Progress remains on stderr. |
5130
5131Linear issue descriptions and dry-run output can include source code snippets
5132and vulnerability details. Publish only to an authorized Linear team or
5133project, and treat saved output as sensitive.
5134
5135Each non-dry-run invocation attempts to create a new issue for every finding.
5136Publishing the same scan again doesn't match, update, or reuse existing issues.
5137If some findings fail, the command preserves successfully created issues and
5138returns exit code `2`.
5139With `--json`, review the `created` and `failed` results before retrying to
5140avoid duplicates.
5141
5142Preview the issue payloads before publishing:
5143
5144```bash
5145npx @openai/codex-security publish scan /path/to/completed-scan \
5146 --to linear \
5147 --linear-team TEAM_ID \
5148 --dry-run \
5149 --json
5150```
5151
5152#### Publish with the connected Linear app
5153
5154Without a Linear API key, the command starts Codex using your existing
5155configuration and connected Linear app. Sign in and connect Linear to your
5156Codex account before publishing:
5157
5158```bash
5159npx @openai/codex-security login
5160npx @openai/codex-security publish scan /path/to/completed-scan \
5161 --to linear \
5162 --linear-team TEAM_ID \
5163 --project PROJECT_ID
5164```
5165
5166#### Publish with a Linear API key
5167
5168Supplying `--linear-api-key` or `CODEX_SECURITY_LINEAR_API_KEY` publishes
5169directly through the Linear API and doesn't start Codex. Direct publication
5170leaves issues unassigned unless you select an assignee:
5171
5172```bash
5173export CODEX_SECURITY_LINEAR_API_KEY=YOUR_LINEAR_PERSONAL_API_KEY
5174npx @openai/codex-security publish scan /path/to/completed-scan \
5175 --to linear \
5176 --linear-team TEAM_ID \
5177 --linear-assignee teammate@example.com
5178```
5179
5180Command-line values override their matching environment variables. For API
5181keys, prefer `CODEX_SECURITY_LINEAR_API_KEY` over `--linear-api-key` because
5182command-line arguments can appear in shell history and process listings.
5183
5102#### `codex-security validate` and `codex-security patch`5184#### `codex-security validate` and `codex-security patch`
5103 5185
5104Check whether a candidate finding is valid:5186Check whether a candidate finding is valid:
5172```5254```
5173 5255
5174When you expose the CLI as an MCP server, `info` is the only available command.5256When you expose the CLI as an MCP server, `info` is the only available command.
5175Scans, exports, sign-in, validation, and patching remain CLI-only.5257Scans, exports, publication, sign-in, validation, and patching remain CLI-only.
5176 5258
5177#### Read scan output5259#### Read scan output
5178 5260
5294The CLI uses these exit codes:5376The CLI uses these exit codes:
5295 5377
5296| Exit | Condition |5378| Exit | Condition |
5297| ----- | --------------------------------------------------------------------------------------------------------------------------------------------- |5379| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5298| `0` | A scan completed with complete coverage and passed its severity policy, a bulk scan completed without failures, or another command succeeded. |5380| `0` | A scan completed with complete coverage and passed its severity policy, a bulk scan or publication completed without failures, or another command succeeded. |
5299| `1` | A completed scan reports a finding at or above the configured severity. |5381| `1` | A completed scan reports a finding at or above the configured severity. |
5300| `2` | The CLI found an input, runtime, or export error, a scan has incomplete coverage, or a bulk scan has repositories with errors. |5382| `2` | The CLI found an input, runtime, or export error, a scan has incomplete coverage, a bulk scan has repositories with errors, or a publication has one or more failed findings. |
5301| `130` | Ctrl-C interrupted a scan. |5383| `130` | Ctrl-C interrupted a scan or publication. |
5302| `143` | SIGTERM terminated a scan. |5384| `143` | SIGTERM terminated a scan or publication. |
5303 5385
5304Any scan with `partial` or `unknown` coverage returns `2`, even without a5386Any scan with `partial` or `unknown` coverage returns `2`, even without a
5305severity policy. When you request structured output, completed scans still5387severity policy. When you request structured output, completed scans and
5306write the available results to stdout. The CLI prints the location of any5388partial publications still write the available results to stdout. The CLI
5307partial output after an interruption or runtime error.5389prints the location of any partial output after an interruption or runtime
5390error.
5308 5391
5309#### Local scan permissions5392#### Local scan permissions
5310 5393
11567 11650
11568To change the base URL for the built-in OpenAI provider, use `openai_base_url`; don't create `[model_providers.openai]`, because you can't override built-in provider IDs.11651To change the base URL for the built-in OpenAI provider, use `openai_base_url`; don't create `[model_providers.openai]`, because you can't override built-in provider IDs.
11569 11652
11570#### ChatGPT customers using data residency11653#### API organizations using data residency
11571 11654
11572Projects created with [data residency](https://help.openai.com/en/articles/9903489-data-residency-and-inference-residency-for-chatgpt) enabled can create a model provider to update the base_url with the [correct prefix](https://platform.openai.com/docs/guides/your-data#which-models-and-features-are-eligible-for-data-residency).11655Projects created with [data residency](https://help.openai.com/en/articles/9903489-data-residency-and-inference-residency-for-chatgpt) enabled can create a model provider to update the `base_url` with the [correct prefix](https://developers.openai.com/api/docs/guides/your-data#which-models-and-features-are-eligible-for-data-residency). For ChatGPT workspaces with data residency, a custom provider isn't required; Codex respects workspace residency settings when you sign in with ChatGPT.
11573 11656
11574```toml11657```toml
11575model_provider = "openaidr"11658model_provider = "openaidr"
17213 17296
17214Expected: Codex prints layer diagnostics plus policy details such as17297Expected: Codex prints layer diagnostics plus policy details such as
17215`allowed_approval_policies`, `allowed_sandbox_modes`, `mcp_servers`, `rules`,17298`allowed_approval_policies`, `allowed_sandbox_modes`, `mcp_servers`, `rules`,
17216`enforce_residency`, and `experimental_network` when configured.17299and `experimental_network` when configured.
17217 17300
17218Use this output to debug why an effective setting differs from `config.toml`.17301Use this output to debug why an effective setting differs from `config.toml`.
17219 17302
19890 - `resource`: the canonical HTTPS identifier for your MCP server. ChatGPT sends this exact value as the `resource` query parameter during OAuth.19973 - `resource`: the canonical HTTPS identifier for your MCP server. ChatGPT sends this exact value as the `resource` query parameter during OAuth.
19891 - `authorization_servers`: one or more issuer base URLs that point to your identity provider. ChatGPT will try each to find OAuth metadata.19974 - `authorization_servers`: one or more issuer base URLs that point to your identity provider. ChatGPT will try each to find OAuth metadata.
19892 - `scopes_supported`: optional list that helps ChatGPT explain the permissions it is going to ask the user for.19975 - `scopes_supported`: optional list that helps ChatGPT explain the permissions it is going to ask the user for.
19893 - Optional extras from [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) such as `resource_documentation`, `token_endpoint_auth_methods_supported`, or `introspection_endpoint` make it easier for clients and admins to understand your setup.19976 - Optional extras from [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) such as `resource_documentation`, `resource_policy_uri`, or `resource_tos_uri` make it easier for clients and admins to understand your setup.
19894 19977
19895When you block a request because it is unauthenticated, return a challenge like:19978When you block a request because it is unauthenticated, return a challenge like:
19896 19979
19914```json19997```json
19915{19998{
19916 "issuer": "https://auth.yourcompany.com",19999 "issuer": "https://auth.yourcompany.com",
20000 "authorization_response_iss_parameter_supported": true,
19917 "authorization_endpoint": "https://auth.yourcompany.com/oauth2/v1/authorize",20001 "authorization_endpoint": "https://auth.yourcompany.com/oauth2/v1/authorize",
19918 "token_endpoint": "https://auth.yourcompany.com/oauth2/v1/token",20002 "token_endpoint": "https://auth.yourcompany.com/oauth2/v1/token",
19919 "client_id_metadata_document_supported": true,20003 "client_id_metadata_document_supported": true,
19925```20009```
19926 20010
19927- Fields that must be correct:20011- Fields that must be correct:
20012 - `issuer`: the canonical authorization server identifier. Use this exact
20013 value in the protected resource metadata `authorization_servers` list.
20014 - `authorization_response_iss_parameter_supported`: set this to `true`
20015 only when your authorization server returns an `iss` parameter in every
20016 authorization response, including error responses.
19928 - `authorization_endpoint`, `token_endpoint`: the URLs ChatGPT needs to run the OAuth authorization-code + PKCE flow end to end.20017 - `authorization_endpoint`, `token_endpoint`: the URLs ChatGPT needs to run the OAuth authorization-code + PKCE flow end to end.
19929 - `client_id_metadata_document_supported`: set to `true` when you want ChatGPT to use CIMD for client registration. ChatGPT prioritizes CIMD when it is available, but the plugin builder can choose DCR when both CIMD and DCR are available.20018 - `client_id_metadata_document_supported`: set to `true` when you want ChatGPT to use CIMD for client registration. ChatGPT prioritizes CIMD when it is available, but the plugin builder can choose DCR when both CIMD and DCR are available.
19930 - `token_endpoint_auth_methods_supported`: include the token endpoint authentication methods your authorization server accepts. This applies to CIMD, DCR, and predefined OAuth clients. For CIMD, ChatGPT supports `none` for public-client token exchange and `private_key_jwt` for signed client assertion token exchange. Other OAuth clients commonly use `none`, `client_secret_post`, or `client_secret_basic`.20019 - `token_endpoint_auth_methods_supported`: include the token endpoint authentication methods your authorization server accepts. This applies to CIMD, DCR, and predefined OAuth clients. For CIMD, ChatGPT supports `none` for public-client token exchange and `private_key_jwt` for signed client assertion token exchange. Other OAuth clients commonly use `none`, `client_secret_post`, or `client_secret_basic`.
19931 - `registration_endpoint`: include this when you support dynamic client registration (DCR), which lets ChatGPT create and reuse a dedicated `client_id` for the connector instance.20020 - `registration_endpoint`: include this when you support dynamic client registration (DCR), which lets ChatGPT create and reuse a dedicated `client_id` for the connector instance.
19932 - `code_challenge_methods_supported`: include `S256` if your authorization server advertises PKCE support.20021 - `code_challenge_methods_supported`: must include `S256`. MCP servers are
20022 unsupported when their authorization server metadata omits this field or
20023 does not advertise `S256`, as required by the
20024 [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#authorization-code-protection).
19933 - Optional fields follow [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) / [OpenID Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html); include whatever helps your administrators configure policies.20025 - Optional fields follow [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) / [OpenID Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html); include whatever helps your administrators configure policies.
19934 20026
19935#### OIDC scopes20027#### OIDC scopes
19965 20057
19966This optimization is optional. Reauthorization still works when an ID token is unavailable or your authorization server does not use the hint.20058This optimization is optional. Reauthorization still works when an ID token is unavailable or your authorization server does not use the hint.
19967 20059
20060#### Protect callbacks with issuer identification
20061
20062OpenAI hosts use [RFC 9207 issuer
20063identification](https://www.rfc-editor.org/rfc/rfc9207#section-2.4) to protect
20064OAuth callbacks against authorization server mix-up attacks. To let ChatGPT
20065and Codex use a stable redirect URI when creating an eligible OAuth client:
20066
20067- Set `authorization_response_iss_parameter_supported: true` in your
20068 [authorization server
20069 metadata](https://www.rfc-editor.org/rfc/rfc9207#section-3).
20070- Use the same exact issuer identifier in the metadata `issuer` field and
20071 the protected resource metadata `authorization_servers` list.
20072- Return `iss` in every successful and error authorization response. Its value
20073 must exactly match the metadata `issuer`; clients use exact string
20074 comparison and do not normalize trailing slashes, paths, ports, or casing.
20075
20076ChatGPT and Codex record the selected metadata `issuer` before redirecting
20077the user and check the returned `iss` before exchanging the authorization
20078code. If the server advertises issuer identification but omits `iss` or
20079returns a mismatch, ChatGPT and Codex reject the response. These requirements
20080follow the [MCP authorization response validation
20081rules](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization#authorization-response-validation).
20082
19968#### Redirect URL20083#### Redirect URL
19969 20084
19970ChatGPT completes the OAuth flow by redirecting to `https://chatgpt.com/connector/oauth/{callback_id}` and the URL will be shown in the app management page. Add that production redirect URI to your authorization server's allowlist so the authorization code can be returned successfully.20085Copy the exact production redirect URI shown in the app management page into
20086your authorization server's allowlist.
20087
20088- If your authorization server does not meet the issuer identification
20089 requirements above, ChatGPT uses the callback-ID-specific redirect URI
20090 `https://chatgpt.com/connector/oauth/{callback_id}`.
20091- If your authorization server meets those requirements, ChatGPT uses the
20092 stable redirect URI
20093 `https://chatgpt.com/connector_platform_oauth_redirect`.
19971 20094
19972- For apps that are already published, the previous legacy redirect URI `https://chatgpt.com/connector_platform_oauth_redirect` continues to work.20095Apps published before ChatGPT introduced callback-ID-specific redirects also
20096continue to use the stable redirect URI.
19973 20097
19974#### Echo the `resource` parameter throughout the OAuth flow20098#### Echo the `resource` parameter throughout the OAuth flow
19975 20099
19980#### Support the authorization-code flow20104#### Support the authorization-code flow
19981 20105
19982- ChatGPT, acting as the MCP client, performs the authorization-code flow with PKCE using the `S256` code challenge so intercepted authorization codes cannot be replayed by an attacker.20106- ChatGPT, acting as the MCP client, performs the authorization-code flow with PKCE using the `S256` code challenge so intercepted authorization codes cannot be replayed by an attacker.
19983- If your authorization server publishes `code_challenge_methods_supported`, include `S256` so clients can confirm PKCE support from metadata.20107- Your authorization server must publish `code_challenge_methods_supported` with `S256` so clients can confirm PKCE support from metadata.
19984 20108
19985#### OAuth flow20109#### OAuth flow
19986 20110
19988 20112
199891. ChatGPT queries your MCP server for protected resource metadata.201131. ChatGPT queries your MCP server for protected resource metadata.
19990 20114
199912. ChatGPT identifies itself as the OAuth client. When the connector uses CIMD, ChatGPT skips dynamic client registration and sends a CIMD document URL as the `client_id`, such as `https://chatgpt.com/oauth/.../client.json` (the exact URL is specific to the MCP server because the redirect URI is MCP-specific). When the connector uses DCR, ChatGPT calls your authorization server's `registration_endpoint` once for the connector instance, receives a generated `client_id`, and reuses that client for the instance.201152. ChatGPT identifies itself as the OAuth client. When the connector uses CIMD, ChatGPT skips dynamic client registration and sends a CIMD document URL as the `client_id`. For authorization servers that meet the issuer identification requirements above, ChatGPT uses the stable `https://chatgpt.com/oauth/client.json`; for other servers, it uses the callback-ID-specific `https://chatgpt.com/oauth/{callback_id}/client.json`. The app management page shows the exact client metadata document and redirect URI for the connector's callback mode. When the connector uses DCR, ChatGPT calls your authorization server's `registration_endpoint` once for the connector instance, receives a generated `client_id`, and reuses that client for the instance.
19992 20116
19993When using CIMD, there is no client registration step. The following screen shows the DCR path:20117When using CIMD, there is no client registration step. The following screen shows the DCR path:
19994 20118
29174- `externalAgentConfig/import` - apply selected external-agent migration items by passing explicit `migrationItems` with `cwd` (`null` for home). Supported item types include config, skills, `AGENTS.md`, plugins, MCP server config, subagents, hooks, commands, and sessions; non-empty imports emit `externalAgentConfig/import/progress` and `externalAgentConfig/import/completed` as work finishes. Plugin and session imports can complete asynchronously.29298- `externalAgentConfig/import` - apply selected external-agent migration items by passing explicit `migrationItems` with `cwd` (`null` for home). Supported item types include config, skills, `AGENTS.md`, plugins, MCP server config, subagents, hooks, commands, and sessions; non-empty imports emit `externalAgentConfig/import/progress` and `externalAgentConfig/import/completed` as work finishes. Plugin and session imports can complete asynchronously.
29175- `config/value/write` - write a single configuration key/value to the user's `config.toml` on disk.29299- `config/value/write` - write a single configuration key/value to the user's `config.toml` on disk.
29176- `config/batchWrite` - apply configuration edits atomically to the user's `config.toml` on disk.29300- `config/batchWrite` - apply configuration edits atomically to the user's `config.toml` on disk.
29177- `configRequirements/read` - fetch requirements from `requirements.toml` and/or MDM, including exact managed configuration, allowlists, pinned `featureRequirements`, and residency/network requirements (or `null` if you haven't set any up).29301- `configRequirements/read` - fetch requirements from `requirements.toml` and/or MDM, including exact managed configuration, allowlists, pinned `featureRequirements`, and network requirements (or `null` if you haven't set any up).
29178- `fs/readFile`, `fs/writeFile`, `fs/createDirectory`, `fs/getMetadata`, `fs/readDirectory`, `fs/remove`, `fs/copy`, `fs/watch`, `fs/unwatch`, and `fs/changed` (notify) - operate on absolute filesystem paths through the app-server v2 filesystem API.29302- `fs/readFile`, `fs/writeFile`, `fs/createDirectory`, `fs/getMetadata`, `fs/readDirectory`, `fs/remove`, `fs/copy`, `fs/watch`, `fs/unwatch`, and `fs/changed` (notify) - operate on absolute filesystem paths through the app-server v2 filesystem API.
29179 29303
29180Plugin summaries include a `source` union. Local plugins return29304Plugin summaries include a `source` union. Local plugins return
34034workspace access or replace workspace RBAC.34158workspace access or replace workspace RBAC.
34035 34159
34036Open [Managed configuration](https://chatgpt.com/codex/settings/managed-configs)34160Open [Managed configuration](https://chatgpt.com/codex/settings/managed-configs)
34037to create and assign cloud-managed requirements. For example, this policy34161to create and assign cloud-managed requirements. For example, this policy limits
34038requires supported clients to use United States data residency, limits approval34162approval and sandbox choices and prompts before a supported shell entry point
34039and sandbox choices, and prompts before a supported shell entry point runs:34163runs:
34040 34164
34041```toml34165```toml
34042enforce_residency = "us"
34043allowed_approval_policies = ["on-request"]34166allowed_approval_policies = ["on-request"]
34044allowed_sandbox_modes = ["read-only", "workspace-write"]34167allowed_sandbox_modes = ["read-only", "workspace-write"]
34045 34168