---
type: research
title: ChatGPT Desktop — Hybrid Architecture and Mode Hierarchy
description: RE of the desktop app — Swift shell + Chromium renderer + embedded Codex app-server, the three conversation surfaces and their transports
tags: [openai, chatgpt, desktop, electron, reverse-engineering, architecture]
timestamp: 2026-08-24
---

# ChatGPT Desktop — Hybrid Architecture and Mode Hierarchy

Part of [openai-research](openai-research.md).

## Binary composition

`/Applications/ChatGPT.app` — bundle id **`com.openai.codex`** (the desktop app is the Codex app).

| Layer | Technology | Role |
|---|---|---|
| Shell | Native Swift (SDK macosx15.5, Sparkle updater, provisionprofile) | Window, menu, macOS integration |
| `Codex Framework.framework` | **Renamed Electron** (Chromium 151.0.7922.170 — Chromium `locale.pak` in the bundle) | UI runtime ("Codex Framework" is branding of the Electron framework, not Rust) |
| `Resources/app.asar` (271MB) | Electron (`.vite/build/main-*.js` main + `/webview/` Vite renderer, 7609 files) | The whole app UI — works offline |
| `Resources/codex` | **Rust — compiled Codex app-server** (Mach-O arm64, 220MB, 3669 rustc/cargo strings; same base as the codex-rs open source, desktop build) | App-server: auth/keychain, threads, realtime, HTTP to the codex backend (`models` polling ~3min, `responses`, `images/generations`) carrying `x-oai-attestation`; NOTE (2026-08-27): `X-OAI-IS` **not observed in flow A / A1 / the manual runs** (0 occurrences in the captured main headers of those flows); **flow B: unknown** (not executed) — the rust client request headers of any flow were never captured. The earlier "chain" claim came from the web-flow/vendor analysis |

`~/.codex/` is the desktop app's CODEX_HOME (auth.json, `models_cache.json`, `logs_2.sqlite` with `codex_http_client` Rust logs, bundled plugins). Electron ↔ app-server communication: **stdio JSON-RPC** (`app_server.request{rpc.transport="stdio"}`).

**ChatGPT Desktop ≠ Codex CLI**: distinct products. The desktop embeds the Codex **app-server** (same codebase as the codex-rs open source, own binary and protocol) — it does not depend on the Codex CLI being installed, and the desktop binary has code the open source does not (e.g. the DeviceCheck attestation integration — `x-oai-attestation`; the public codex-rs has the `AttestationProvider` trait, the desktop supplies the Apple implementation). `X-OAI-IS`: not observed in the executed flows (A/A1/manual; 0 hits in the captured main headers); unknown for flow B (not executed); rust request headers never captured. The `~/workdir/codex` source is a reference for the shared protocol, not the absolute source of the closed binary.

## Mode hierarchy

```
MODO CHATGPT
├─ submodo work → chatgpt.com/backend-api + handoff tool (Codex quota, /compact)
└─ submodo chat → chatgpt.com/backend-api (chat quota, no compact)  ← RESEARCH FOCUS
MODO CODEX     → app-server direct → /backend-api/codex
```

## Submode marking in the payload (`/f/conversation`)

The body builder is `r3r()` in the renderer. Chat vs Work is **not a mode boolean**:
- The distinguishing mechanism is `local_function_signatures` — the `handoff` tool with the server-provided description that is always offered in the ChatGPT mode
- `conversation_mode: {kind: 'gizmo_interaction', gizmo_id}` + `gizmo_id` = Projects
- `conversation_execution_target` — execution target (tpp/local tasks)

## Renderer → network transport

1. Renderer calls the typed API client (`safeGet`/`safePost`, OpenAPI paths — 163 mapped)
2. Transport `Hp` sends via **IPC** (`fetch-response` events) — the renderer never fetches directly
3. Control headers renderer→main: `X-OpenAI-Attach-Auth: 1` (main injects `Authorization: Bearer` + `ChatGPT-Account-Id` from the app-server), `X-OpenAI-Attach-Integrity-State: 1` (main resolves the session integrity marker — see the `oai_is` field of the OAuth refresh; NOT observed as an `X-OAI-IS` request header in the real f/* traffic), `X-OpenAI-Attach-Desktop-Surface`, `X-OpenAI-Attach-DeviceCheck-Token` (DeviceCheck)
4. Main cleans the User-Agent (`excludedUserAgentProducts: ['Electron', app name]`)
5. Client defaults: `OAI-Language: en`, `originator: Codex Desktop`, `oai-did` (persisted device uuid)

## Authenticated webviews (the only exception)

Webviews that render site pages (checkout `/purchase/*`, workspace settings, Sites) use `SR()`/`OR()` in the main: clear cookies, **`POST /api/auth/link-session`** with `{auth_token, expires_in}` + `x-i-am-a-browser: true` (see [auth-and-link-session](auth-and-link-session.md)), then set routing cookies (`_account`, `_account_residency_region`, `_account_is_fedramp`). The chat does not use this path.

## Plugin primary runtime (on-demand download)

The app downloads a bundled "primary runtime" on demand: `GET persistent.oaistatic.com/codex-primary-runtime/<version>/codex-primary-runtime-darwin-arm64-<version>.tar.xz` with UA `codex-primary-runtime-installer` (main process). Version **26.826.11250** (newer than the app 26.820.60940 — the runtime tracks its own release line). Extracts into `~/.codex/plugins/cache/openai-primary-runtime/{template-creator,spreadsheets,pdf,presentations,documents}/26.826.11250/` — the plugin runtime surfaces (docs, sheets, PDF, slides, template creator) used by the agent mode.

## Cross-references

- Chat submode protocol: [chat-submode-protocol](chat-submode-protocol.md)
- Codex backend: [codex-flow-protocol](codex-flow-protocol.md)
- Auth and tokens: [auth-and-link-session](auth-and-link-session.md)
