Chapter Twenty-Three

ChatGPT and Codex on Linux

OpenAI's tools reach Linux users along two quite different paths, and it helps to separate them before going further.

Learning Objectives
  1. Distinguish ChatGPT the assistant from Codex the command-line coding agent
  2. Install and configure Codex CLI on a Linux system using TOML configuration files
  3. Explain how Codex combines bubblewrap, namespaces, seccomp, and Landlock to sandbox commands
  4. Choose appropriate sandbox and approval modes for a given task
  5. Compare the sandboxing approaches taken by different command-line agents

ChatGPT is the conversational assistant, used through a browser or a desktop application. On Linux there is no official desktop client, so the browser is the normal route. It is a useful thing to have open while you work, but it is not the subject of this chapter, because it does not touch your filesystem or run your commands.

Codex is the coding agent, and it does both. It is distributed as a command-line program, written in Rust, developed in the open on GitHub, and it follows the same agent loop described in Chapter 22: you state a goal, it reads files and runs commands until the goal is met. Codex is also available inside ChatGPT and as an IDE extension, but the CLI is the version that matters for a Linux system, and it is where the interesting engineering is.

That engineering is mostly about confinement. Codex takes a noticeably more aggressive stance than Claude Code on sandboxing: where Claude Code treats the sandbox as an opt-in layer, Codex sandboxes by default and asks you to opt out. Understanding how it does that is a short tour of modern Linux security primitives.

Installing Codex CLI

Codex ships as a native binary. Three routes are supported:

npm install -g @openai/codex
brew install --cask codex

Or download a release archive directly. The Linux builds are statically linked against musl, which means one binary works across distributions regardless of glibc version:

Platform Archive
Linux x86_64 codex-x86_64-unknown-linux-musl.tar.gz
Linux arm64 codex-aarch64-unknown-linux-musl.tar.gz

Table 23.1: Codex CLI release archives for Linux

Extract the archive, put the binary somewhere on your $PATH, and make it executable. This is the plainest possible installation story, and on a system where you would rather not add a package repository or a Node.js toolchain it is the cleanest option:

tar xzf codex-x86_64-unknown-linux-musl.tar.gz
install -m 755 codex ~/.local/bin/codex
codex --version

Start a session in a project directory by running codex with no arguments, or pass a prompt directly.

Configuration in TOML

Where Claude Code uses JSON, Codex uses TOML, and where Claude Code layers instructions in CLAUDE.md, Codex reads the vendor-neutral AGENTS.md convention. Configuration is resolved through a precedence chain that will look familiar from every other Unix program in this book: the most specific source wins.

Table 23.2: Codex configuration precedence, highest first

Priority Source
1 CLI flags and --config overrides
2 Project config: .codex/config.toml (trusted projects only)
3 Named profile: ~/.codex/<profile>.config.toml
4 User config: ~/.codex/config.toml
5 System config: /etc/codex/config.toml
6 Built-in defaults

Note the presence of /etc/codex/config.toml. A system-wide configuration file under /etc is precisely what Chapter 4 said that directory is for, and it is the hook an administrator would use to set organisation-wide policy on a shared machine.

A minimal user configuration:

approval_policy = "on-request"
sandbox_mode = "workspace-write"

Approval and Sandbox Modes

Codex separates two questions that are easy to conflate: will you be asked before a command runs, and what can that command reach once it does. They are configured independently.

approval_policy governs the first:

  • untrusted is the cautious baseline, approving only a known-safe set and asking about everything else.
  • on-request lets the model run commands but pause and ask when it judges that it needs to.
  • never runs without prompting, and can be restricted by administrator policy.

sandbox_mode governs the second:

  • read-only allows the agent to read the filesystem but write nothing.
  • workspace-write allows writes within the working directory, which is the sensible default for ordinary development.
  • danger-full-access disables the sandbox entirely, and is named to discourage you.

The combination that suits most work is workspace-write with on-request: the agent can edit the project it is working on, cannot touch anything else, and asks before doing something it considers consequential. Regardless of mode, .git/ and .codex/ are protected, so an agent cannot rewrite your repository history or silently widen its own configuration.

How the Linux Sandbox Actually Works

This is the part worth reading closely, because it uses four separate kernel facilities in combination, and each does a job the others cannot.

bubblewrap builds the filesystem view. It mounts the entire root filesystem read-only with --ro-bind / /, then layers the specific writable directories back in with --bind. The default is therefore total: nothing is writable until something explicitly says it is. Protected subpaths such as .git are re-applied as read-only inside otherwise-writable roots, so a writable workspace does not imply a writable repository.

Namespaces provide the isolation boundaries, and Codex uses three of the mechanisms Chapter 17 introduced:

Flag Namespace Effect
--unshare-user User Enables unprivileged sandboxing without root
--unshare-pid PID The sandboxed process cannot see or signal host processes
--unshare-net Network No network access at all, when networking is restricted

Table 23.3: Namespaces used by the Codex Linux sandbox

seccomp filters system calls. Once bubblewrap has established the filesystem view, a seccomp filter is applied in-process to block networking at the syscall level, along with PR_SET_NO_NEW_PRIVS, which prevents the process or any child from gaining privileges through a setuid binary. That flag is a small thing with a large effect: it closes the classic escalation route where a confined process executes something setuid to escape.

Landlock is retained as a fallback. Landlock is a Linux security module, merged in kernel 5.13, that lets an unprivileged process restrict its own filesystem access. Codex uses it on the legacy path where bubblewrap is unavailable or explicitly disabled.

The layering here is the interesting part, and it is a good model for thinking about confinement generally. bubblewrap decides what exists. Namespaces decide what is visible. seccomp decides what is permitted. PR_SET_NO_NEW_PRIVS decides that none of it can be escalated away. No single mechanism would be sufficient; together they compose into something reasonably tight.

The one platform note: WSL1 is unsupported, because it cannot create the user namespaces bubblewrap requires. WSL2 works.

Comparing the Approaches

It is instructive to line up the three agents this part of the book covers, because they solve an identical problem in three different ways.

Table 23.4: Sandboxing strategies compared

Tool Linux mechanism Default posture
Claude Code bubblewrap, plus optional seccomp filter Opt-in, enabled with /sandbox
Codex CLI bubblewrap, namespaces, seccomp, Landlock On by default, workspace-write
Gemini CLI Containers: Docker, Podman, gVisor, LXC Opt-in, via --sandbox

Claude Code and Codex both reach directly for kernel primitives; Gemini CLI, covered in the next chapter, delegates the whole problem to a container runtime. Each has a defensible rationale. Kernel primitives are lighter and need no daemon, but the code doing the confining is the agent's own. Containers are heavier and require Docker or Podman to be installed and working, but they reuse an isolation boundary that has been attacked and hardened for a decade.

None of the three is a substitute for a virtual machine when you genuinely need a hard boundary. All three are a substantial improvement over running an agent unconfined, which remains the most common configuration in practice and the one worth arguing people out of.

Using Codex in Scripts

Like Claude Code, Codex is usable non-interactively, which makes it composable with the rest of your shell. The general shape is the same: pass a prompt, get output, exit. This makes it a candidate for the sort of automation Chapter 14 covered, running from a script or a CI job rather than from a keyboard.

The caution from the previous chapter applies with more force in automation, not less. A prompt running unattended in CI has no one to answer its questions, which means it is running with approvals effectively disabled. That is exactly the situation where the sandbox mode is doing all the work, and where a container or an ephemeral VM around the whole job is worth the setup cost.

Where This Leaves ChatGPT

Returning to where the chapter started: for a Linux user, ChatGPT and Codex occupy genuinely different roles. ChatGPT is for thinking, explaining, and drafting, with your input arriving by copy and paste. Codex is for acting, with its own access to your files. The security considerations that dominate this chapter apply entirely to the second and not at all to the first.

Some tasks are better suited to the first. Asking an assistant to explain an unfamiliar iptables rule does not require giving it your filesystem, and there is a reasonable argument for keeping such questions in a browser tab. The agent is a more powerful tool, but "more powerful" and "correct for this task" are not the same claim.

Linux Simulator: learn Linux on iPhone. Download on the App Store.

Frequently Asked Questions

  1. What is the difference between ChatGPT and Codex?
  2. How does Codex sandbox commands on Linux?
  3. What is AGENTS.md?
  4. Which sandbox and approval mode should I use with Codex?