> ## Documentation Index
> Fetch the complete documentation index at: https://gateway.consus.io/llms.txt
> Use this file to discover all available pages before exploring further.

# ChatGPT Desktop

> Use the ChatGPT desktop app (ChatGPT Work and Codex) with ITAR and FedRAMP High data

<Warning>
  The gateway enforces the compliance boundary for the models it serves. It does **not** control the app itself: local tool execution, file access, or the app's own background connections to OpenAI. See [What this does not control](#what-this-does-not-control). Client-side behavior is the customer's responsibility.
</Warning>

The ChatGPT desktop app has two agent modes, **ChatGPT Work** and **Codex**. Both read one file, `~/.codex/config.toml`. Point that file at the Consus Gateway and both modes run on compliant GPT models. No OpenAI account, no sign-in.

## Before you start

* **The ChatGPT desktop app.** [Download it](https://openai.com/chatgpt/download/), but do not sign in.
* **A Consus API key** from your Consus admin.

Keep the app closed (**Cmd+Q**) until Step 2. It rewrites its config file while running.

## Step 1: Add the configuration

Create `~/.codex/config.toml` (Windows: `%USERPROFILE%\.codex\config.toml`) and replace `<CONSUS_API_KEY>` with your key. For **FedRAMP High**, also change `:itar` to `:fedramp-high` on the first line.

<Accordion title="config.toml">
  ```toml theme={null}
  model = "gpt-5.6-sol:itar"
  model_provider = "consus"
  model_reasoning_effort = "medium"
  model_reasoning_summary = "auto"
  web_search = "disabled"
  approval_policy = "on-request"
  approvals_reviewer = "user"
  sandbox_mode = "workspace-write"

  [sandbox_workspace_write]
  network_access = false

  [model_providers.consus]
  name = "Consus Gateway"
  base_url = "https://api.consus.io/v1"
  wire_api = "responses"
  http_headers = { "x-api-key" = "<CONSUS_API_KEY>" }

  [shell_environment_policy]
  inherit = "all"
  exclude = ["AWS_*", "AZURE_*", "GOOGLE_*", "GCP_*", "CONSUS_*", "OPENAI_*", "ANTHROPIC_*", "*_KEY", "*_TOKEN", "*_SECRET", "*PASSWORD*"]

  [features]
  computer_use = false
  browser_use = false
  browser_use_external = false
  browser_use_full_cdp_access = false
  in_app_browser = false
  image_generation = false
  realtime_conversation = false
  in_app_dictation = false
  apps = false
  remote_plugin = false
  plugin_sharing = false
  recommended_plugins = false
  tool_suggest = false
  skill_mcp_dependency_install = false
  memories = false

  [computer_use]
  default_app_access = "deny"

  [browser_use]
  allow_history_access = false

  [browser_use.default_origin_policy]
  access = "deny"
  uploads = "deny"
  downloads = "deny"
  full_cdp_access = "deny"

  [memories]
  use_memories = false
  generate_memories = false

  [analytics]
  enabled = false

  [feedback]
  enabled = false

  [otel]
  exporter = "none"
  trace_exporter = "none"
  metrics_exporter = "none"
  log_user_prompt = false
  ```
</Accordion>

The file holds your key, so restrict it to your user:

```bash theme={null}
chmod 600 ~/.codex/config.toml
```

<Warning>
  **Do not reorder the file.** In TOML, every line after a `[table]` header belongs to that table. The eight lines at the top must stay above the first `[...]` header. Move `web_search = "disabled"` below `[model_providers.consus]` and Codex silently ignores it.
</Warning>

<Accordion title="What each setting does">
  | Setting                                                                   | What it does                                                                                                                                                                                      |
  | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `model`, `model_provider`                                                 | The model ID sent to the gateway, and the provider block to use. IDs must carry a `:level` suffix or the gateway returns 400.                                                                     |
  | `model_reasoning_summary = "auto"`                                        | Keeps reasoning summaries consistent across models. GPT-5.6 Terra, GPT-5.6 Luna, and GPT-5.4 only produce `auto` summaries; the gateway serves other values as `auto` on those models.            |
  | `web_search = "disabled"`                                                 | Stops the app declaring OpenAI's hosted web search. The gateway strips that tool regardless.                                                                                                      |
  | `approval_policy`, `approvals_reviewer`, `sandbox_mode`, `network_access` | The agent asks a human before leaving its sandbox, writes only inside the project folder, and its commands have no network.                                                                       |
  | `[model_providers.consus]`                                                | Routes requests to the gateway over the Responses API and sends your key as `x-api-key`. The key is inline because the app is launched from the Dock and cannot read shell environment variables. |
  | `[shell_environment_policy]`                                              | Strips cloud and API credentials from the environment of commands the agent runs.                                                                                                                 |
  | `[features]`, `[computer_use]`, `[browser_use]`, `[memories]`             | Turns off computer use, the agent-driven browser, hosted image and voice features, connectors, remote plugins, and memory, with deny-by-default policies behind each switch.                      |
  | `[analytics]`, `[feedback]`, `[otel]`                                     | Turns off Codex analytics, the feedback upload (it sends session logs to OpenAI), and metrics export, which otherwise goes to an OpenAI-hosted service.                                           |
</Accordion>

## Step 2: Open the app and verify

Open ChatGPT and pick **ChatGPT Work** or **Codex** from the dropdown at the top left. Three things should be true:

1. The bottom-left corner says **Consus Gateway**.
2. The model picker shows a model and a reasoning effort. It reads **Custom** until you do Step 3.
3. **Settings → Configuration** shows Web search **Disabled**, Sandbox **Workspace write**, and network access **off**.

Then click **Choose project**, pick a folder, and ask for something real:

```
create a file hello.py with a fibonacci function and run it
```

The file appears on disk and the output is the actual numbers. You're done.

## Step 3 (optional, recommended): Name your models

Everything works without this step. Doing it gets you three things: the picker shows real names like **5.6 Sol (ITAR)** instead of **Custom**, you can switch models from the picker, and the GPT-5.6 models get their full context window (up to 1M tokens) instead of a small default.

Quit the app, then save this script as `~/.codex/make-catalog.py`:

<Accordion title="make-catalog.py">
  ```python theme={null}
  import copy, json, os, shutil, subprocess, sys

  LEVEL = sys.argv[1] if len(sys.argv) > 1 else "itar"  # or: fedramp-high

  # base model -> (bundled template to clone, display name, context window, reasoning efforts)
  MODELS = {
      "gpt-5.6-sol":   ("gpt-5.6-sol",   "GPT-5.6 Sol",   922000,  ["low", "medium", "high", "xhigh"]),
      "gpt-5.6-terra": ("gpt-5.6-terra", "GPT-5.6 Terra", 1000000, ["low", "medium", "high", "xhigh"]),
      "gpt-5.6-luna":  ("gpt-5.6-luna",  "GPT-5.6 Luna",  1000000, ["low", "medium", "high", "xhigh"]),
      "gpt-5.4":       ("gpt-5.4",       "GPT-5.4",       272000,  ["low", "medium", "high", "xhigh"]),
      "gpt-5.1":       ("gpt-5.4",       "GPT-5.1",       272000,  ["low", "medium", "high"]),
  }

  # The desktop app ships its own Codex binary; fall back to a CLI on PATH.
  CANDIDATES = ["/Applications/ChatGPT.app/Contents/Resources/codex", shutil.which("codex")]
  codex = next((c for c in CANDIDATES if c and os.path.exists(c)), None)
  if not codex:
      sys.exit("Could not find a Codex binary. Install the ChatGPT desktop app, or put the Codex CLI on your PATH.")

  bundled = json.loads(subprocess.check_output([codex, "debug", "models", "--bundled"]))["models"]
  by_slug = {m["slug"]: m for m in bundled}
  label = LEVEL.upper().replace("FEDRAMP-", "FedRAMP ").replace("HIGH", "High")

  out = []
  for i, (base, (tmpl, name, ctx, efforts)) in enumerate(MODELS.items()):
      m = copy.deepcopy(by_slug.get(tmpl) or by_slug.get("gpt-5.4") or bundled[0])
      m.update({
          "slug": f"{base}:{LEVEL}", "display_name": f"{name} ({label})", "description": "Consus Gateway",
          "priority": i + 1, "visibility": "list",
          "context_window": ctx, "max_context_window": ctx,
          "supported_reasoning_levels": [l for l in m["supported_reasoning_levels"] if l["effort"] in efforts],
          "default_reasoning_level": "medium",
          "additional_speed_tiers": [], "service_tiers": [], "availability_nux": None, "upgrade": None,
          "supports_search_tool": False,
      })
      out.append(m)

  path = os.path.expanduser("~/.codex/consus-models.json")
  os.makedirs(os.path.dirname(path), exist_ok=True)
  with open(path, "w") as f:
      json.dump({"models": out}, f, indent=2)
  print(f"wrote {len(out)} models to {path}")
  ```
</Accordion>

Run it once, and again whenever the app updates:

```bash theme={null}
python3 ~/.codex/make-catalog.py                # ITAR
python3 ~/.codex/make-catalog.py fedramp-high   # FedRAMP High
```

Then add this line to `config.toml`, directly under `model_reasoning_summary` (it must sit above the first `[table]` header), and reopen the app:

```toml theme={null}
model_catalog_json = "~/.codex/consus-models.json"
```

Add the line only after running the script. Codex will not start if the file it points to is missing.

## Good to know

* **Start a new chat to change models.** Changing reasoning effort mid-chat is fine. Changing the model mid-chat can fail with `The request was invalid`, because Codex replays encrypted reasoning that only the original cloud provider can read. Sol and GPT-5.1 are served from Azure Government; Terra, Luna, and GPT-5.4 from AWS GovCloud.
* **GPT models only.** The app speaks the [Responses API](/endpoints/responses), which serves GPT. For Claude, use [Claude Desktop](/integrations/claude-desktop) or [Claude Code](/integrations/claude-code).
* **No web search, image generation, voice, or connectors.** Those run on OpenAI's infrastructure outside the compliance boundary. The model has no live information, so treat any "current" facts as unverified.
* **Do not sign in to ChatGPT** on a machine that handles controlled data. Signing in enables the OpenAI-hosted features this setup turns off.

## What this does not control

* **The app's own connections.** The configuration governs the agent: where prompts go, which tools exist, what its commands can reach. The application around it is OpenAI's and keeps its own connections to OpenAI and Google for things like updates and product analytics. Consus cannot see or route those.
* **The provider.** A user who edits `config.toml` can point the app somewhere else. No Codex setting prevents that.
* **Local data.** Chats and transcripts are stored on the workstation under `~/.codex/`.

If your data classification requires the workstation to reach only authorized endpoints, enforce it on the network: an egress allowlist that permits `api.consus.io`. That one control closes the first two gaps.

## For admins: lock the settings

Users can edit `config.toml`. To make the safety settings permanent, install a second file, `requirements.toml`, where users cannot write. Codex treats it as a hard limit: anything `config.toml` requests beyond it is overridden at launch, and the app shows those settings as locked under **Settings → Configuration → Admin config**.

<Accordion title="requirements.toml">
  ```toml theme={null}
  allowed_approval_policies = ["untrusted", "on-request"]   # "never" is not allowed
  allowed_approvals_reviewers = ["user"]
  allowed_sandbox_modes = ["read-only", "workspace-write"]  # no full access
  allowed_web_search_modes = []                             # only "disabled" remains
  allow_appshots = false
  allow_remote_control = false

  default_permissions = ":workspace"

  [allowed_permission_profiles]
  ":read-only" = true
  ":workspace" = true

  [features]
  computer_use = false
  browser_use = false
  browser_use_external = false
  browser_use_full_cdp_access = false
  in_app_browser = false
  image_generation = false
  realtime_conversation = false
  in_app_dictation = false
  apps = false
  remote_plugin = false
  plugin_sharing = false
  memories = false

  [computer_use]
  default_app_access = "deny"
  allow_persistent_approval = false
  allow_locked_computer_use = false

  [browser_use]
  allow_history_access = false
  allow_global_persistent_approval = false

  [browser_use.default_origin_policy]
  access = "deny"

  # Only locally installed plugin sources.
  [marketplaces]
  restrict_to_allowed_sources = true

  # Files the agent may never read, whatever the user approves.
  [permissions.filesystem]
  deny_read = ["/**/*.env", "~/.ssh", "~/.aws", "~/.azure", "~/.config/gcloud", "~/.kube"]

  # Commands the agent runs may reach only the gateway.
  [experimental_network]
  enabled = true
  managed_allowed_domains_only = true

  [experimental_network.domains]
  "api.consus.io" = "allow"
  ```
</Accordion>

Install it as an administrator:

```bash theme={null}
sudo mkdir -p /etc/codex
sudo cp requirements.toml /etc/codex/requirements.toml
sudo chmod 644 /etc/codex/requirements.toml
```

| Platform      | Location                                                  |
| ------------- | --------------------------------------------------------- |
| macOS / Linux | `/etc/codex/requirements.toml`                            |
| Windows       | `%ProgramData%\OpenAI\Codex\requirements.toml`            |
| macOS MDM     | domain `com.openai.codex`, key `requirements_toml_base64` |

To push the Step 1 file to a fleet as well, use `/etc/codex/managed_config.toml` or the MDM key `config_toml_base64`.

<Note>
  The file is machine-wide, so it also governs the Codex CLI and editor extensions. Headless `codex exec` can no longer run commands or edit files, because it always requests the `never` approval policy, which this file forbids.
</Note>

## Troubleshooting

**`The request was invalid` right after switching models** — Start a new chat. See [Good to know](#good-to-know).

**The picker says `Custom`, or lists the wrong models** — Do Step 3, and re-run the script after every app update.

**The app will not start** — `model_catalog_json` points at a file that does not exist. Run the script in Step 3, or remove the line.

**`unexpected status 401`** — The key in `http_headers` is missing or wrong. Do not switch to `env_key` or `env_http_headers`: they read shell environment variables, which an app launched from the Dock does not have.

**A setting has no effect** — A top-level key is below a `[table]` header. **Settings → Configuration** shows a banner naming the ignored key. A banner about `session-flags` is the app's own and can be ignored.

**My edits disappeared** — The app rewrote the file while it was running. Quit it before editing, and leave the sections it adds (`[desktop]`, `[projects]`, `[plugins]`, `[mcp_servers]`) in place.

**`git fetch` or `pip install` fails inside the agent** — Agent commands have no network, by design. Run those yourself in a terminal.

## Codex CLI

Prefer the terminal? See the [Codex CLI guide](/integrations/codex).
