Security model
September 4, 2026 ยท View on GitHub
Trust boundaries
The user trusts the local Codex app, this loopback daemon, the launcher's private Electron browser profile, the selected ChatGPT workspace, OpenAI's tunnel service, and the exact MCP connector they created. Repository contents, tool output, websites, and prompt text are untrusted data.
Full-mode capability flow
- The daemon accepts a Codex Responses turn on
127.0.0.1. - It extracts
cwd, workspace roots, and sandbox policy from the native Codex envelope. When a resumed root task or subagent omits that envelope, its canonical local rollout must prove the exact thread and current turn (or latest source turn for standalone compaction). Request metadata can only constrain that authority. Tools always come from the current request; user-authored<environment_context>text is never a source of recovered authority. A context-only continuation after completed compaction additionally binds the exact checkpoint and source instruction to its native thread, turn, model and effort. A freshly emitted environment claim without a new human message must match that turn's canonical rollout in cwd, roots and sandbox policy; the checkpoint alone does not grant filesystem authority. - It creates a random, turn-scoped token and embeds it in that one ChatGPT browser prompt.
- Every Codex Native action presents that same turn token. The MCP handler idempotently claims an internal binding plus a request-scoped activity lease and immediately dispatches the requested action; neither internal handle is exposed to the model. The lease is settled only after the MCP handler finishes, including inventory calls that need no outer Codex tool.
- MCP can request only a callable tool advertised by the active outer Codex turn. The unrestricted
raw orchestration
execgateway remains available in Full mode. Before caller-authored JavaScript runs, the bridge wraps its tool registry with a transparent proxy that enforces the exact 10-secondwait_agentpolling contract and prevents recursive rawexec. The generic inventory/call pair also provides a structured exact-name path. Codex remains responsible for its sandbox, approval, UI, command sessions, and tool result. - Before a Codex tool batch is dispatched, the browser records and acknowledges the current answer projection. Completion stays blocked while the tool is unresolved and then requires a new stable final-answer projection after that causal boundary. A two-phase broker fence then rereads the DOM and commits completion only if the activity revision stayed unchanged with no active invocation; a concurrent claim makes the candidate lose, while a claim after commit receives an explicit terminal rejection. Recent MCP activity may suppress a false DOM-health failure but never adds an idle delay to a successful completion.
The bridge transports decisions; it does not add a second planner, semantic router, or fallback model. Every available effort uses the same MCP contract. An unavailable account route, missing connector, or missing outer tool fails explicitly instead of becoming an effort-specific exception.
The direct turn-token MCP schema is attached only through the Codex Native2 connector identity.
The pre-v4 Codex Native connector is treated as legacy and is never selected as a fallback. This
prevents a cached legacy schema from being mistaken for the current capability contract.
Principal risks
Prompt injection and destructive tool use
ChatGPT sees repository content and tool results that may contain hostile instructions. Full mode can invoke write and command tools. Use a trusted workspace, keep Codex sandbox/approval settings appropriate, and grant only intended connector actions. Automatic per-call approval is off by default.
Browser session theft
The launcher's persistent Electron partition can authorize ChatGPT access. It remains in the current OS user's private application-data directory and is never copied into a daemon prompt or runtime descriptor. Never sync, upload, attach, or commit it. On suspected exposure, sign out or revoke the ChatGPT session from the launcher.
Tunnel credential theft
The runtime key needs only Tunnels Read + Use. It is accepted through a hidden prompt or copied from a file, stored with user-only permissions, referenced by file, and never placed in a command argument or generated profile. Rotate it after suspected exposure.
Same-user local process
The Responses endpoint is loopback-only, but it has no independent bearer secret because the built-in Codex OpenAI provider cannot be configured with a bridge-specific credential while preserving the native provider/task identity. Another process under the same OS user can reach the port. Run on a trusted single-user account and treat local code execution as inside the trust boundary.
The lifecycle endpoints are separate from the Responses surface. /admin/drain, /admin/resume,
/admin/cancel-turn, /admin/cancel-turns, and /admin/shutdown require a random bearer token stored in the
user-only application config. The launcher uses them to reject new work, prove that both the HTTP
request and long-lived browser/tool loop are idle, flush response state, and stop a process. The
token does not turn loopback into a hostile-local-process security boundary; it prevents accidental
or unauthenticated lifecycle control through ordinary requests.
Browser/UI drift
ChatGPT DOM and labels are not a stable API. Selectors are narrow; Full-mode completion requires stable completed-turn evidence and, after tools, a new final-answer projection. UI drift fails the turn; it never chooses another model, starts another transport, or returns a fabricated success.
Login-state isolation
The launcher keeps ChatGPT login, identity-provider navigation, and model turns in one private
Electron partition. Allowed login popups are adopted into an in-launcher WebContentsView that
shares that partition; unrelated external links remain outside it. A visible composer alone is not
authentication evidence: the launcher also requires a valid server session and an exact Temporary
Chat URL before setup can continue. No cookies, local storage, or browser profile are copied from an
external browser.
Cross-turn data leakage
Browser turns use at most five independent task-bound tabs in one private login partition. Every
outer Codex task owns an exact launcher surface lease and retains its Temporary Chat only across
sequential messages in the same model/effort/compaction epoch; chats are never reused across tasks.
Closing a running tab destroys its page and terminates that turn. The five-tab limit bounds parallel
account traffic. Tool calls remain in the same ChatGPT response. The
bounded local continuation cache is private, expires, and exists only to implement Codex
previous_response_id replay. Full-mode context compaction accepts a checkpoint only through its
one-shot MCP control capability in the exact retained source chat. If that chat no longer exists, a
fresh tool-free Temporary Chat receives the canonical Codex history; the bridge never parses ordinary
assistant prose as a structured handoff.
Network exposure
- Responses and health listeners bind to
127.0.0.1only. - Full mode uses OpenAI's outbound HTTPS Secure MCP Tunnel; it opens no public listener or inbound firewall rule.
- The embedded browser connects to ChatGPT, the selected identity provider during explicit sign-in, and user-authorized attachment URLs through normal browser networking.
Non-goals
- Defending against a compromised local OS user or compromised Codex/Electron binary.
- Bypassing ChatGPT plan, workspace, usage, action-control, or model restrictions.
- Making consumer browser automation equivalent to a supported OpenAI API contract.