Codex Asks What Linux It Is Running On 96 Times to Say "ok"
A security proxy for AI coding agents, enforced at the OS level. Install grith and put a real boundary around your agent.

esc to closeAsk an AI coding agent to reply with the single word "ok" and watch what happens. Not at the prompt level - at the syscall level, where processes get spawned and sockets get opened.
We did this five times each for Claude Code and Codex, cold starts, identical
trivial task, same repository, same machine. The headline finding: Codex
spawns lsb_release -a roughly 96 times per session. That is a command
whose job is to report which Linux distribution is running. The answer cannot
change between the first call and the ninety-sixth.
The setup
Both agents ran under grith supervision, which records every process spawn, file operation, DNS query and socket event to a local audit log. That log is the instrument: rather than a staged demo, these are ordinary supervised sessions read back afterwards.
- Task: "reply with the single word ok" - deliberately trivial, so everything measured is startup behaviour, not work.
- Five cold runs per agent, same repository, same machine. Figures below are medians across the five runs.
- Versions: Claude Code 2.1.250, Codex CLI 0.149.1, grith 0.3.3.
One methodology note, because it changed our results before publication: the first version of the analysis sliced the audit database by wall clock, and promptly attributed DNS traffic from a different concurrent session - including a third-party domain - to the agent under test. The harness now anchors on the session start event and follows that session id, so nothing else running on the machine can leak into the numbers. That correction is the difference between a measurement and an accusation.
One trivial session, measured
| Event kind | Claude Code | Codex |
|---|---|---|
| Process spawns | 4 | 111 |
| DNS queries | 12 | 26 |
| Socket listens | 0 | 121 |
| io_uring setup attempts | 0 | 9 |
| Directories created | 3 | 1 |
| Files deleted | 2 | 0 |
Claude Code's entire session is four process spawns. Codex's is 111, and about 88% of them are the same command.

esc to closeFinding 1: lsb_release, 96 times
Per run, Codex spawned lsb_release -a 112, 92, 104, 88 and 96 times - a
median of 96. The first arrives about 63ms after session start, before Codex
has run any shell; roughly half land inside the first second, and the rest
arrive in clumps across the remaining seconds of the session, still going
just before it exits. The count varying between runs suggests it is
per-something rather than a fixed loop, but we have not identified what. It
is not the operator's dotfiles: lsb_release appears nowhere in .bashrc,
.bash_profile or .profile on the test machine.
To be clear about what this is and is not: it is waste, not exfiltration.
lsb_release reads local release metadata and prints it. But a process tree
that asks the same immutable question ninety-six times in a ten-second
session is a process tree nobody has looked at from below. If you know what
produces this - a runtime default, a per-worker environment probe, something
in the Node ecosystem - we would genuinely like to know.
Finding 2: your login shell runs seven times
Codex spawns bash -lc seven times per session, the first around 700ms in. A
login shell sources your profile, which means whatever is in your dotfiles
executes as part of an ordinary agent session. On this machine that meant
version-manager and package-tool hooks from the operator's own configuration -
not Codex behaviour, and not secret. That is exactly the point: the mechanism
runs your environment, whatever it contains. A compromised dotfile would run
just the same, seven times, in a session that only ever needed to say ok.
Claude Code spawned no login shell at all in these sessions.
Finding 3: nine reaches for io_uring
Codex attempts io_uring_setup nine times per session; grith denies every
one. io_uring matters here because it is a documented route around
syscall-level security filtering - operations submitted through its rings do
not surface as individual syscalls. We are not implying intent: this is far
more likely a runtime or library default than anything deliberate. But an
agent runtime reaching for the one I/O mechanism that syscall filters cannot
see is worth stating plainly. In these runs, Claude Code did not attempt it.
What they talk to
Domains resolved during the session, median counts:
- Claude Code: api.anthropic.com (8), mcp-proxy.anthropic.com (2), and a Datadog log-intake host (2)2
- Codex: chatgpt.com (22), ab.chatgpt.com (2), registry.npmjs.org (2)
The npm registry resolution comes from Codex running npm exec for a
Playwright package during the session; we have not yet confirmed what
triggers it, but a package fetch during a session whose task needed no
packages is worth knowing about regardless of the reason.
Honest limits
One machine, one operator's configuration, five runs per agent, two specific versions. Counts vary between runs and will vary more between machines. None of this is hidden behaviour - all of it is visible to anyone who looks at the right layer, which is rather the point: almost nobody looks. We would welcome numbers from other machines, and the harness below makes that a five-minute job.
Run it yourself
The harness is a shell script and an analysis script: start a supervised session, run the trivial task, then slice the audit log by that session's id and count events by kind. If your numbers disagree with ours, that is interesting and we want to see them.
The broader argument - that the interesting security questions about AI agents are answered below the prompt, at the boundary where processes and sockets are real - is the same one behind our earlier traces of what these agents read and what happened when one went looking for credentials. The most trivial session you can run turns out to be just as informative as the worst one.
Footnotes
-
All twelve are binds to port 0, which indicates ephemeral port allocation rather than a durable listening service. We have not characterised these sockets further, so we are deliberately not describing this as "opening twelve listeners". ↩
-
We have not confirmed whether the Datadog resolution comes from Claude Code itself or from tooling configured on the test machine, so treat that row as unattributed until we do. ↩
Like this post? Share it.