CLI Tools Shouldn't Install Background Daemons by Default
AI CLI tools now install persistent, self-updating background daemons by default. Why that breaks the developer-tool trust model and how to do better.

A developer runs npm install -g @openai/codex to try an AI coding assistant, and ends up with a persistent, self-updating background daemon they never asked for. That's the situation that blew up recently when users of OpenAI's Codex CLI discovered the tool auto-starts a background process, writes pid and lock files into ~/.codex, keeps old binary versions on disk (one user reported roughly 12 GB of them), and updates itself without prompting. This isn't a story about one company's bug. It's a story about a trust model that developer tooling has relied on for decades — and how quietly it's being abandoned.
I've spent years doing infrastructure and incident response, and the pattern here is one I've seen before — in adware, in compromised build agents, in 'helpful' updater services shipped by hardware vendors. So let's take the common defenses of this design and test each one, because most of them don't survive contact with how production environments actually work.
Myth 1: "It's just an updater — browsers do the same thing"
This is the most common defense, and it sounds reasonable. Chrome and Firefox update themselves constantly. So do VS Code and Slack. Why shouldn't a CLI harness?
Because the trust context is completely different. A browser is a GUI application with a first-run experience, a visible settings page, a signed installer, and — critically — an update channel that has been hardened and audited for over a decade. Chrome's updater is one of the most attacked pieces of software on Earth, and Google staffs teams to keep it safe. When you install Chrome, updating is part of the deal you visibly opted into.
A CLI installed via npm carries a different implicit contract. Developers expect it to run when invoked and stop when it's done. That expectation isn't nostalgia — it's load-bearing. CI pipelines, containers, shared build servers, and security reviews all assume that npm i -g some-tool gives you a binary that executes on demand. A background daemon that survives logout, respawns itself, and swaps its own binary violates every one of those assumptions. I've personally been on incident calls where 'why is there a process we can't account for' turned a routine alert into a three-hour forensic exercise. Unaccounted-for persistence is indistinguishable from compromise until you prove otherwise.
There's also a practical difference in blast radius. Your browser renders web pages in a sandbox. An AI coding harness runs shell commands, reads your source tree, and often holds API keys and cloud credentials in its environment. Giving that thing a self-modifying, always-on component is a categorically bigger risk than a browser update.
Myth 2: "Users can just turn it off with a config flag"
Technically true, and practically meaningless. Yes, there is a feature flag:
# ~/.codex/config.toml
[features]
daemon_auto_start = false
Here's the problem: it was undocumented when users went looking for it. An opt-out that nobody knows about is not consent — it's a gotcha. And notice what the community's fallback plan was: delete the pid and lock files, symlink them to /dev/null, and mark them immutable with chattr +i so the daemon crashes on startup if the flag ever disappears. When your users are booby-trapping their own home directories to defend against your software, you have not built a configuration system. You've built an adversarial relationship.
I've written about this dynamic before in the context of fitness apps leaking location data: the pattern is always the same. The feature ships on-by-default, the toggle is buried or undocumented, and the burden of vigilance lands on the user. Defaults are policy. A default that installs persistence is a statement about who the software is really serving.
The fix is trivially easy and always has been: ask once, on first run. Codex runs a background service to keep itself updated. Enable it? [y/N]. Tools like apt, Homebrew, and most Linux daemons have operated on explicit enablement for decades. systemctl enable exists precisely because starting something at boot is supposed to be a deliberate act.
Myth 3: "Moving fast requires silent self-updating"
This one has some merit, and I want to steelman it. AI harnesses genuinely do move fast. A CLI that's three weeks stale might misbehave against a changed API, and a buggy client in the wild is a real support burden. One team building a legal-tech IDE described considering a daemon, then choosing a hard update check at every launch instead: if the app is out of date, it tells you and updates at startup, and if you don't like it, you stop running it. That's a legitimate design.
But 'we need updates' does not imply 'we need a persistent daemon that updates itself while you're not looking.' There is a wide middle ground:
- Check on invocation. Compare versions when the CLI starts, print a notice, update next run. Homebrew, gh, and countless others do this.
- Update through the package manager. You were installed via npm — update via npm. The user already has a mechanism they understand and can pin, audit, and lock down.
- Opt-in daemon, off by default. For users who want zero-friction freshness, offer the daemon as a documented choice.
- Prune old versions. Keeping every previous binary until you've eaten 12 GB of someone's disk is not an update strategy; it's a leak.
The real reason teams reach for silent self-updaters isn't speed — it's control. A package-manager update respects the user's lockfile, mirrors, and approval workflow. A self-updater routes around all of that. When a vendor insists on the self-updating path, they're telling you they value their deployment pipeline over your change management. In an enterprise with logging blind spots already, that's how you end up with unreviewed code executing under developer credentials.

Myth 4: "It's open source — you can read the code"
Open source helps, but it doesn't solve the actual problem, which is drift between what you reviewed and what's running. The whole point of a self-updating daemon is that the binary on your machine today is not the binary you installed. Reading the source of version 0.157.0 tells you nothing about the build that gets pulled down next Tuesday. Unless you're verifying signatures and reproducible builds on every silent update — and nobody is — 'it's open source' is a comfort blanket.
There's a second, subtler issue: state and configuration entanglement. Users trying to manage Codex declaratively (one described failing to do it under nix-darwin) found that the tool mixes config with mutable runtime state in the same directory. That makes it impossible to pin, diff, or audit what changed. Good Unix tools have known this forever: config in ~/.config, state in ~/.local/state, cache in ~/.cache, and never let the tool mutate its own configuration. If you want to understand why the audited path matters, the gap between specification and implementation is exactly where these surprises live.
If you want to check what's actually running on your machine right now, it takes thirty seconds:
# What AI-tool daemons are alive right now?
ps auxww | grep -Ei 'codex|claude' | grep -v grep
# What's been accumulating on disk?
du -sh ~/.codex ~/.local/share/claude 2>/dev/null
# What launchd/systemd units did tools register?
ls ~/Library/LaunchAgents 2>/dev/null # macOS
systemctl --user list-units --type=service | grep -Ei 'codex|claude' # Linux
Run that on a few developer laptops in your org. I guarantee at least one person will be surprised by the output.
Myth 5: "Everyone does it, so it must be fine"
Claude Code keeps prior binary versions around and runs background tasks too — users in the same discussion confirmed it. So yes, this is an industry pattern, not a single vendor's lapse. But 'everyone does it' has never been a security argument; it's how bad norms calcify. The supply-chain incidents of the last few years should have taught us that developer machines are the softest high-value target in most organizations: they hold cloud credentials, signing keys, and push access, and they run an astonishing amount of unaudited code with those privileges.
Every persistent, network-connected, self-modifying component you add to that machine is attack surface with a beak into your most sensitive credentials. A compromised update channel for a popular dev tool is a nightmare scenario precisely because the update mechanism is trusted implicitly. The more vendors normalize silent daemons, the more valuable each one becomes as a target.
And the quality bar matters. Users reported the Codex daemon corrupting permissions during auto-update — leaving .git directories read-only and wedging working repos. A buggy daemon doesn't just sit there; it actively breaks things with privileges you never consciously granted. "Everyone does it" plus "it's buggy" is not reassurance. It's a forecast.
What a consent-respecting update mechanism looks like
This is a solved problem. We don't need new technology; we need vendors to choose the boring answer. If I were writing the requirements doc for any AI CLI shipping today, it would look like this:
- No persistence by default. The tool runs when invoked and exits when done. Any background component is opt-in, requested explicitly on first run.
- Documented, stable kill switches. A config flag and an environment variable (e.g.
CODEX_NO_DAEMON=1) that are documented, tested, and won't silently disappear. - Updates via the install channel. npm-installed tools update via npm. Notify on invocation; never mutate your own binary out from under the user.
- Signed, versioned releases with changelogs. If you do ship an updater, sign artifacts and let users pin versions and audit what changed.
- Bounded disk and clean state separation. Keep at most one previous version, separate config from state, and respect XDG conventions.
- Honor the distro model. As one commenter put it, distro packaging exists so that maintainers test software and strip out shenanigans before it reaches users. Don't fight that — publish to it.
None of this slows a fast-moving team down in any meaningful way. It just moves the consent to the front of the interaction, where it belongs.
The bottom line
The uncomfortable truth is that AI coding tools are asking for more trust than any developer tool in history — shell access, your credentials, your entire source tree — while adopting the update hygiene of 2008-era adware. Those two facts cannot coexist safely. A CLI that installs a persistent, self-updating daemon by default has made a decision on your behalf, and the decision is that the vendor's convenience outranks your control over your own machine.
Push back on it. Set the flags, audit your machines, file the issues, and prefer tools whose defaults respect you — the same instinct that makes people reach for sideloading freedom over locked-down platforms applies here. The tools we let run unattended on developer laptops today become the supply-chain incident reports of tomorrow. Consent isn't a nice-to-have in update design. It's the whole mechanism.


