Security Trust

Codex sandboxing is the agent story I care about

7 min read

Codex sandboxing is the agent story I care about because coding agents touch real working state.

A model producing a diff is interesting. A model producing a diff inside a clear filesystem, command, network, and credential boundary is useful. The second version has a chance to survive review.

The repo is not one permission. Reading source is different from reading secrets. Editing a blog draft is different from editing a migration. Running tests is different from opening the network. Installing a dependency is different from formatting a file. A sandbox should name those differences directly.

Agent safety gets vague when it stays at the level of personality. Is the agent careful? Is it aligned? Does it understand the task? Fine questions, but too soft for the part where a process is about to mutate a checkout.

The sandbox is where the claims become concrete.

filesystem scope is the first line

The first sandbox question is what the agent can read and write.

Read access and write access should be separate. A coding agent may need to read a broad part of the repo to understand the system, but only write to a narrow set of files. It may need to inspect package metadata but should not edit lockfiles unless the task explicitly involves dependencies. It may need to read tests but only change source, or the reverse.

A useful grant is explicit:

{
  "paths": {
    "read": ["src", "scripts", "package.json"],
    "write": ["src/content/blog"]
  }
}

That shape lets the runtime catch boundary crossings. If the agent tries to edit src/content.config.ts during a blog rewrite, the system can stop and ask. Maybe the edit is justified. Maybe the agent drifted. The point is that the crossing becomes visible.

The worst version is a workspace-wide write grant for a narrow task. That turns every agent mistake into a repo-wide review problem.

commands need their own policy

Shell access is not one permission either.

Running pnpm markdown:check is different from running a migration. Running tests is different from installing packages. Running a formatter is different from deleting generated output. Commands deserve the same scoping as files.

I like command allowlists for routine work:

{
  "commands": {
    "allow": [
      "pnpm markdown:check",
      "pnpm check",
      "pnpm astro check"
    ],
    "ask": [
      "pnpm build",
      "pnpm install"
    ],
    "deny": [
      "rm -rf",
      "git push"
    ]
  }
}

This is not about pretending an allowlist solves every shell risk. Commands can be composed. Scripts can do surprising things. But a command policy gives the runtime a starting point and gives the user something to review.

If the agent needs a new command, that is useful information. It means the task has changed shape or the environment is missing something.

network access should be deliberate

Network access is where coding agents can quietly leave the local problem.

Sometimes network is necessary. The agent may need documentation, package metadata, an issue tracker, or a remote API. Sometimes network is exactly what should be blocked, especially when the workspace might contain private code or credentials.

The sandbox should separate local work from external calls:

network:
  localhost: allow
  package registry: ask
  arbitrary web: ask
  internal services: deny unless explicitly granted

Localhost access is useful for testing web apps. Arbitrary web access is a different decision. Internal services are different again.

The agent should not get to decide that a failing test probably requires searching the web if the user asked for an offline repo cleanup. It can ask. The sandbox should enforce the answer.

secrets should be absent by default

The best secret available to an agent is the one it cannot read.

That sounds obvious until a local development environment leaks credentials through .env, shell history, config files, package manager auth, cloud CLI state, or browser sessions. A coding agent operating near a real workspace can stumble into sensitive material without malicious intent.

The sandbox should make secrets unavailable by default. If a task genuinely requires a credential, the credential should be resolved through a controlled tool with a narrow purpose, not exposed as raw text to the model.

For most coding tasks, the correct secret policy is simple:

secrets:
  environment: stripped
  dotfiles: read blocked
  credential helpers: unavailable
  secret lookup: tool-specific approval required

That policy may feel strict. Good. The agent can still do a surprising amount of useful work without seeing secrets.

recovery is part of the sandbox

A sandbox is not only about preventing bad actions. It is also about making recovery cheap.

If an agent edits inside a worktree, the user can inspect or discard the diff. If the agent writes only inside scoped paths, cleanup is smaller. If commands are logged, failures are easier to replay. If network calls are recorded, unexpected external dependencies can be investigated.

Recovery should be designed into the run:

before:
  capture git status

during:
  log file writes and commands

after:
  show diff, checks, denied actions, and cleanup steps

That record makes the sandbox practical. It is not enough to say the agent was contained. The user needs to know what happened inside the container.

authority drift is the real failure

The failure mode I watch for is authority drift.

A task starts small. The agent reads a file. It finds a failing check. It edits a helper. The helper depends on another module. A test needs a fixture update. A dependency is stale. Suddenly the agent wants broader write access, a new command, and network access to check documentation.

Some of that may be legitimate. Some of it may be the agent turning a focused task into a wandering repair session.

The sandbox should make each escalation explicit:

  • new write path requested
  • new command requested
  • network requested
  • secret lookup requested
  • external write requested

Those are not errors by default. They are decision points.

This is where sandboxing supports productivity. It lets the agent continue when the expansion is justified and stops it when the expansion is just momentum.

measure the boring things

The metrics I want for sandboxed agents are not flashy.

Count files touched. Count denied writes. Count permission escalations. Count commands run. Count failed commands. Count network requests. Count runs discarded. Count runs accepted after review. Count cases where the receipt did not match the diff.

Those numbers tell you whether the sandbox is helping or getting in the way.

If every useful task needs three escalations, the default scope is too tight or the task slicer is bad. If agents rarely hit boundaries but produce huge diffs, the scope may be too broad. If reviewers discard many runs after command failures, verification is happening too late.

Sandboxing should be tuned from evidence, not from fear.

the point is reviewable autonomy

I do not care about sandboxing because I want agents to be timid.

I care because autonomy without reviewable boundaries is a mess. A coding agent should be able to do real work. It should read code, make edits, run checks, and report back. But that work should happen inside a shape the user can understand.

Filesystem scope. Command policy. Network rules. Secret isolation. Escalation. Recovery. Receipts.

That is the agent story I care about because it turns capability into something a team can operate.

Jeremy London

About Jeremy London

Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.