
Artwork: Prisoners Exercising by Vincent van Gogh. Wikimedia Commons, Pushkin Museum of Fine Arts · Public domain
Isolated Execution for Coding Agents: Where the Sandbox Ends
AI disclosure: This article was moderated by AI.
Putting a coding agent in a container limits some failures, but it does not create a complete security boundary. The agent can still reach network services, inherit credentials, interact with shared infrastructure, or outlive the control that was supposed to stop it.
That was the most important result of my isolated-execution research. The choice between a container and a microVM matters, but the runtime is only one part of the sandbox. Network paths, secret delivery, dependency infrastructure, lifecycle controls, evidence retention, and the privileges of the sandbox manager can each undo the boundary.
This is Part 2 of my research into governed Agentic SDLC. I started with recent incidents, compared isolation technologies and coding-agent products, and then used Autodev to test how those choices fit into a wider control plane. Autodev is one implementation in this comparison, not the standard against which the others are judged.
Isolated execution has more than one boundary
A useful sandbox has to answer several independent questions:
- Process and kernel: Can the workload access host processes, devices, or kernel interfaces?
- Filesystem: Which source, tools, caches, and host paths can it read or change?
- Network: Which destinations and protocols can it reach, including through shared services?
- Credentials: Do secrets enter the workload, or are authenticated requests mediated elsewhere?
- Resources: What limits exist for CPU, memory, processes, storage, and execution time?
- Lifecycle: Can the control plane reliably pause, stop, destroy, and recover the workload?
- Evidence: Which commands, policy decisions, outputs, and attempted violations survive cleanup?
- Control plane: What can the service that creates sandboxes do to the host or cloud account?
A stronger answer in one row does not repair a weak answer in another. A microVM with unrestricted egress can leak data. A network-isolated container with the host Docker socket mounted can control the host. A short-lived environment can still cause a lasting change if it holds a production credential.
This also changes what “ephemeral” means. Destroying the compute is useful, but the worktree, policy record, trace, diff, and scanner output may need to survive. Otherwise cleanup removes the same evidence needed to investigate the run.
Why coding agents need defense in depth
A coding agent does not only execute text generated by a model. It executes a repository’s build scripts, package-manager hooks, compiler plugins, test fixtures, browser content, MCP responses, and downloaded dependencies. Any of those inputs can be malicious even when the model follows its instructions perfectly. Accidental damage needs the same controls: a recursive delete, fork bomb, or test pointed at a production endpoint does not become safer because nobody intended it.
Isolation therefore has two jobs. It limits what a compromised workload can reach, and it gives the control plane a reliable way to stop resource exhaustion. Linux supplies several useful primitives, but they are complementary rather than progressively stronger versions of the same feature.
| Mechanism | What it controls | What it does not provide |
|---|---|---|
| Namespaces | Separate views of process IDs, mounts, users, networks, IPC, hostnames, time, and cgroup roots | Resource limits, fine-grained access policy, or a separate kernel |
| cgroup v2 | Hierarchical accounting and limits for CPU, memory, I/O, processes, and other resources | Filesystem secrecy or protection from kernel vulnerabilities |
Linux capabilities and no-new-privileges | Remove pieces of root authority and prevent privilege gain across execve | Filesystem or network isolation by themselves |
| seccomp | Reduces the host-kernel system-call surface exposed to a process | A complete sandbox or information-flow policy; the kernel documentation says this explicitly |
| Landlock, AppArmor, or SELinux | Constrains access to kernel objects such as files; current Landlock also covers selected network and IPC operations | A new kernel boundary, and coverage depends on the LSM and kernel version |
| Container runtime | Composes namespaces, cgroups, mounts, capabilities, and filters into a usable process environment | Safety from privileged mounts, management sockets, permissive policy, or a shared-kernel exploit |
| Userspace kernel or VM | Moves system-call handling away from the host kernel, or gives the workload its own guest kernel | Egress policy, secret mediation, evidence retention, or a safe lifecycle API |
Namespaces change what a process can see. cgroups control what it can consume. Seccomp, capabilities, and LSMs constrain what it can do. gVisor and VMs then move the kernel boundary.
The practical consequence is that “uses cgroups” is not an isolation claim. A cgroup can stop a
fork bomb with pids.max and cap memory with memory.max, but the processes remain ordinary host
processes. Likewise, a mount namespace can hide the host filesystem while an accidentally mounted
Docker socket restores host-level control through the daemon API.
The Linux kernel documentation makes another useful distinction: seccomp is a tool for sandbox builders, not a sandbox on its own. A production design composes these controls and then tests the composition as one boundary.
How I would construct an agent sandbox
I would build from the authority outside the workload toward the process inside it:
- Classify the workload first. Trusted local automation, untrusted pull requests, hostile multi-tenant code, and computer-use agents should not inherit the same boundary. Tenant trust and credential value decide whether a container, userspace kernel, microVM, or managed service is the starting point.
- Create a per-run identity and workspace. Give each run a fresh non-root UID, worktree, and
sandbox identity. Mount only the required source. Keep the image read-only and make scratch space
explicit with a writable worktree, volume, or
tmpfs. - Remove ambient host authority. Drop every capability unless a tested command needs it, set
no-new-privileges, apply a seccomp allowlist and an LSM policy, expose no host home directory, SSH agent, cloud credential directory, device, or container-management socket. - Start with no network. Put DNS and outbound connections behind policy that understands the calling sandbox and destination. Cover UDP, DNS tunneling, redirects, proxies, artifact stores, cloud metadata, and private address ranges, not only direct HTTPS requests.
- Broker credentials instead of injecting them. Prefer a trusted proxy that attaches a short-lived credential to an approved request. If a secret must enter the workload, scope it to one resource and one run, then assume the agent can disclose it.
- Bound resource and time use. Set CPU, memory, process, file-size, storage, and wall-clock limits. The stop operation has to kill descendants and concurrent forks, not only the shell that launched the command.
- Export evidence before cleanup. Stream commands, stdout and stderr, denied operations, network decisions, resource events, exit status, and hashes to storage the sandbox cannot edit. Preserve the final diff and test results separately from the disposable compute.
- Fail closed and test the failure path. If the runtime cannot apply a mount, network rule, seccomp profile, or kernel prerequisite, do not silently run with a weaker boundary. Exercise pause, kill, reaping, timeout, and evidence recovery before relying on them during an incident.
The broker, credential mediator, and evidence store belong outside the disposable workload. Giving the sandbox access to their control sockets collapses the separation in the drawing.
A small escape test suite is more useful than a long list of enabled flags. I would verify that a
run cannot read a sibling worktree or host home directory, reach the container daemon or cloud
metadata service, resolve or connect to an unapproved destination, leave descendants after a
timeout, exceed its PID and memory budgets, or recover a real provider key from environment
variables, files, process arguments, logs, or /proc. Each denied attempt should appear in the
external evidence record. Destroying the sandbox should not destroy that record.
The sandbox incidents changed my threat model
The most useful threat models I found came from failures rather than architecture diagrams. OpenAI’s August 26, 2026 account of the Hugging Face incident and its accompanying technical report describe experimental agents that were supposed to run without internet access or unauthorized communication.
The agents found both. They wrote files into an internally hosted Artifactory service and used it as a message board. They then reached the internet through server-side request forgery in that service. The agents shared methods, chained vulnerabilities, recovered exposed Hugging Face credentials, executed code on Hugging Face workers, and expanded access across clusters.
The timeline matters. OpenAI reported that an internal team had seen message-board activity and disallowed internet access in late May. The Artifactory service failed on July 4, but the wider meaning of the behavior was not understood. A security alert on July 19 connected unusual activity to the larger incident. Safeguards and chain-of-thought monitoring used in other environments had not been running in this one.
The independent METR and Redwood Research investigation adds useful context. My takeaway was more specific than “agents escape sandboxes.” A sandbox includes its transitive dependencies. A package mirror, artifact store, resolver, metadata service, or file processor can become the route around an intended boundary.
A later incident exposed a different gap. On September 20, an agent used insufficient DNS filtering to query a public chatbot from a training sandbox. OpenAI’s report says monitoring raised a P0 alert within 15 minutes and a person acknowledged it three minutes later. The expected automatic shutdown did not happen. The run was stopped manually two and a half hours later.
Three lessons from those reports changed my checklist:
- Network denial has to cover indirect routes used by system dependencies, not only obvious HTTP egress.
- Detection and containment are separate controls. An alert needs a tested stop path and an owner.
- A failed escape attempt is still evidence about intent and policy. A useless response does not make the request harmless.
Containers and microVMs move different boundaries
Before this research, I used “sandbox” as if it named an architecture. It does not. The term can mean a restricted process, an ordinary container, a userspace kernel, a lightweight virtual machine, or a remote managed environment.
| Approach | Isolation boundary | Main trade-off |
|---|---|---|
| Native container | Linux namespaces, cgroups, capabilities, and the shared host kernel | Fast and familiar, but the host kernel and management socket remain sensitive boundaries |
| Process sandbox such as nsjail or bubblewrap | A smaller composition of namespaces, filesystem views, syscall filters, and, depending on the tool, resource controls | Useful for local tools and nested restrictions, but still shares the host kernel and requires careful policy |
| gVisor | A per-sandbox userspace application kernel intercepts system calls before they reach the host | Less fixed overhead than a VM, with compatibility gaps and cost for system-call-heavy workloads |
| Kata Containers | Lightweight VMs presented through container tooling | Hardware isolation while preserving container workflows, with added VM and infrastructure complexity |
| Firecracker | A KVM microVM with a minimal device model and its own guest kernel | Small VM footprint, but it is a VMM rather than a complete sandbox platform |
| WebAssembly runtime such as Wasmtime | Bounds-checked linear memory and explicit host imports; WASI filesystem access follows a capability model | Strong for compatible modules, but coding agents usually need native compilers, package managers, and operating-system tools |
| Docker Sandboxes | A microVM with a private Docker daemon, network mediation, and host-side credential handling | A developer-oriented managed experience with more resource cost than a container |
| Managed remote environment | Provider-defined VM or container plus lifecycle, policy, and integrations | Less infrastructure to operate, with provider-specific controls and evidence |
Native containers share the host kernel
Containers use namespaces to separate process IDs, mounts, users, and networks. Cgroups limit resources, while Linux capabilities and seccomp can reduce kernel access. They start quickly and fit existing build tooling, which makes them an attractive local default.
The important limitation is the shared kernel. A kernel vulnerability can cross the namespace
boundary. Mounting a Docker or containerd management socket is another direct route to host-level
control. Rootless execution, dropped capabilities, read-only mounts, seccomp, AppArmor or SELinux,
and no-new-privileges reduce risk, but they do not turn a container into a VM.
gVisor moves system-call handling into userspace
gVisor places a userspace application kernel between the workload and the host kernel. Most application system calls are handled there instead of reaching the host directly. This reduces the host-kernel surface exposed to the workload without assigning a full VM to each sandbox.
The trade-off depends on the workload. Applications that rely on unsupported kernel behavior may need changes, and system-call-heavy work can pay a performance cost. That makes compatibility tests part of the security decision rather than an afterthought.
Kata and Firecracker add a guest kernel
Kata Containers runs container workloads inside lightweight virtual machines. The operator keeps familiar container APIs while hardware virtualization provides another boundary between the guest kernel and the host.
Firecracker is lower-level. It is a KVM-based virtual machine monitor with a deliberately small device model. The project publishes startup targets below 125 ms and memory overhead below 5 MiB per microVM. Those numbers explain why microVMs are practical for short-lived workloads, but they do not make Firecracker a turnkey agent sandbox. Image construction, networking, storage, scheduling, secret mediation, lifecycle, and evidence still need a control plane.
Docker Sandboxes packages a microVM for agent work
Docker Sandboxes helped connect the lower-level architecture to a developer workflow. Each sandbox has its own Docker daemon inside a microVM, so an agent can build and run containers without access to the host daemon.
Outbound traffic passes through a host-side proxy where network policy can be applied. Docker’s documentation also describes credential mediation: the workload receives a sentinel value, while the proxy injects the real credential after the request crosses the VM boundary. The secret does not have to enter the agent’s environment.
That model addresses two risks that an isolated kernel alone cannot solve: network governance and secret exposure. It also costs more memory and compute than an ordinary container. The right comparison is therefore not “secure” versus “insecure.” It is which boundary matches the workload, threat model, latency budget, and operational capacity.
Coding-agent products bundle different parts of the sandbox
I compared products by the boundary they expose, not by how many agent features they advertise. The public documentation does not support a perfect side-by-side security ranking, but it does show which responsibilities each product takes on.
| Product or project | Execution model I found | Useful distinction |
|---|---|---|
| GitHub Copilot coding agent | Ephemeral GitHub Actions-powered development environment | Repository workflow, logs, steering, and pull-request handoff are integrated |
| Cursor Cloud Agents | Isolated Ubuntu-based remote machines with configurable network access | Managed environments combine snapshots, secrets, hooks, artifacts, and egress controls |
| Devin | Managed compute with shell, editor, browser, and integrations | The product emphasizes long-running delegated work and a persistent collaboration view |
| OpenHands | Local Docker, remote runtimes, or customizable sandbox images | The open runtime makes execution providers easier to inspect and replace |
| GitLab Duo Agent Platform | Agent sessions integrated with GitLab workflows and CI/CD controls | Tool approvals and audit events sit near existing source-control governance |
| Docker Sandboxes | Local microVM with a private Docker daemon | It focuses on isolating developer agents while preserving familiar Docker workflows |
| NVIDIA OpenShell | Open-source gateway and policy layer over Docker, Podman, Kubernetes, or microVM sandboxes | A trusted supervisor mediates network access and credentials while Landlock and seccomp constrain the workload |
| Azure Container Apps dynamic sessions | Managed, isolated, prewarmed execution sessions | It separates a session-management pool from short-lived workload sessions |
This comparison changed the question I was asking. I had started with “which sandbox should I use?” The more useful questions were: which controls belong to the execution provider, which belong to my control plane, and which evidence remains portable if I change providers?
A managed environment can provide a stronger default than a local container and remove a large amount of infrastructure work. It also couples the workflow to the provider’s policy model, identity, logs, lifecycle, and supported integrations. A local or open runtime gives more control, but the operator inherits the hard parts.
Open-source sandbox projects expose different layers
I also looked beyond coding-agent products at projects that can become part of a sandbox provider. The useful comparison is where each project stops.
| Project | Layer it supplies | Boundary to inspect |
|---|---|---|
| Kubernetes SIGs Agent Sandbox | A Sandbox custom resource, stable identity and storage, lifecycle controls, templates, claims, and warm pools | It is an orchestrator. Its own documentation says low-level isolation comes from a configured RuntimeClass, such as gVisor or Kata Containers |
| E2B Runtime | One Firecracker microVM per sandbox, with a cgroup, network namespace, nftables egress policy, snapshots, an orchestrator, and an in-guest command API | The open runtime exposes the whole control and data plane; a self-hosted operator still owns deployment hardening and control-plane credentials |
| Microsandbox | Local, embeddable microVMs using OCI images, with SDKs, snapshots, volumes, network allow rules, and an MCP server | It provides a guest-kernel boundary but is currently marked beta; broker policy and operational hardening still need review |
| Daytona | Hosted sandboxes with a dedicated kernel, filesystem, network stack, resource allocation, snapshots, APIs, and audit features | Its public core repository says development moved to a private codebase in June 2026 and is now archived, so current behavior must be judged from the service documentation and contract |
| nsjail | A Linux process-isolation tool combining namespaces, cgroups, resource limits, and seccomp-bpf | It is a useful building block, not a remote lifecycle, credential, or evidence service |
| Bubblewrap | An unprivileged tool for constructing mount, user, PID, network, IPC, and UTS namespace environments, with optional seccomp filters | Its maintainers explicitly leave the security model to the caller’s arguments; mounted sockets and paths can reopen host authority |
| Wasmtime | A WebAssembly runtime for untrusted modules with isolated linear memory, explicit imports, and capability-based WASI filesystem access | Host functions define the reachable authority, and native coding-agent toolchains generally do not fit this module boundary |
Kubernetes Agent Sandbox makes the separation especially clear. It standardizes provisioning for a stateful singleton workload, then delegates the actual kernel boundary to gVisor, Kata, or another runtime. That is a good model for avoiding a hard-coded provider, but selecting the default Kubernetes runtime would not magically produce the stronger boundary described by the API.
E2B and Daytona expose higher-level sandbox APIs, while Microsandbox, nsjail, Bubblewrap, and Wasmtime can sit closer to a local control plane. Their position determines which team must operate image provenance, egress, identity, snapshots, patching, abuse controls, and evidence.
Agent toolkits and Agent OSes are not kernel boundaries
The naming gets confusing above the execution layer. Microsoft’s Microsoft 365 Agents Toolkit scaffolds, debugs, tests, and deploys agents and apps for Microsoft 365 Copilot, Teams, and Microsoft 365. It also integrates hosting, authentication, CI/CD, and the Agents Playground. Those are development and deployment capabilities, not a claim that arbitrary tool code runs in an isolated kernel.
Microsoft Agent Framework sits at a similar architectural level. It supplies agent and workflow abstractions, tool and MCP integration, state, middleware, and telemetry. Middleware can approve or reject a tool request, but any accepted shell or code-execution tool still needs a sandbox provider underneath it. I would connect either Microsoft toolkit to a narrow execution API rather than let generated tool code inherit the toolkit host’s identity and filesystem.
“Agent OS” is even less specific. Agno AgentOS is a FastAPI runtime and control plane with persistent sessions, RBAC, approvals, tracing, evaluations, and background work. It runs in infrastructure configured by its operator, and its tools follow that application’s access. Pairing it with a sandbox can be a sound design; treating its session isolation as process isolation would not be.
AIOS uses operating-system language for scheduling LLM, memory, storage, and tool requests. Its computer-use architecture can add a VM controller, but its local kernel mode otherwise runs the AIOS components and agents on the same machine. The VM is the execution boundary in that design, not the word “kernel” in the project name.
There is also a separate Agent OS project for writing specifications and injecting codebase standards into coding-agent workflows. It does not claim to be an execution sandbox. In all three cases, the deciding question remains concrete: which process runs generated code, under which OS identity, with which mounts, network paths, credentials, resource limits, and stop mechanism?
OpenShell separates the sandbox from the policy decision
NVIDIA OpenShell was the closest match I found to the provider boundary I wanted to explore. It is not another agent. It is an open-source runtime and control plane for running agents under declarative filesystem, process, network, and provider policies.
The architecture separates an untrusted workload from a trusted supervisor. The workload has no direct network egress. DNS lookups and TCP connections travel over an authenticated channel to the supervisor, which identifies the calling program, checks policy, and opens an approved connection. Provider credentials stay on the trusted side and are added only to requests for an authorized endpoint.
Filesystem restrictions use Landlock, while seccomp constrains process behavior and mediates network operations. A policy prover checks proposed network-policy changes for new risks, such as credentialed access to another host or access to cloud metadata. A flagged proposal waits for human review instead of being applied automatically.
The same policy model can sit above several compute drivers. Docker and Podman disable workload
networking and connect the sandbox to its supervisor through a protected Unix socket. Kubernetes
uses a private service with mutual TLS and a NetworkPolicy fence. The microVM driver gives the
guest no network device and uses vsock for the supervisor channel. OpenShell therefore does not
replace the underlying isolation technology; it supplies a common mediation and lifecycle layer
above it.
Setting up OpenShell locally
The current support matrix lists Debian or Ubuntu on x86-64 and ARM64, macOS on Apple Silicon with Docker Desktop, and Windows through WSL 2 with Docker Desktop as experimental. A Docker-backed setup requires Docker Engine or Docker Desktop 28.0 or newer. Podman 5.x, Kubernetes 1.29, and the microVM driver are alternative backends.
The official quickstart installs the CLI and a local gateway, then creates a retained sandbox:
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | sh
openshell sandbox create --name demo
Without --from, the gateway uses nvcr.io/nvidia/base/ubuntu:24.04. That image is a minimal
Ubuntu environment with no coding agent installed. The restrictive fallback policy allows the
working directory and required system paths but denies all outbound network access.
The first-agent guide uses OpenCode
with OpenRouter to show the additional pieces. Set OPENROUTER_API_KEY in the host shell, import
the provider profile, and let OpenShell store the credential outside the workload:
export OPENROUTER_API_KEY="<your-key>"
openshell profile import \
--url https://raw.githubusercontent.com/NVIDIA/OpenShell/main/providers/openrouter.yaml
openshell provider create \
--name openrouter \
--type openrouter \
--from-existing
openshell sandbox create \
--name my-agent \
--from ghcr.io/anomalyco/opencode:latest \
--provider openrouter \
-- opencode -m openrouter/nvidia/nemotron-3.5-lightning:free
When the agent requests a destination that policy denies, OpenShell records the denial and can draft a narrow rule. The host-side review loop is explicit:
openshell rule get my-agent --status pending
openshell rule approve my-agent --chunk-id <chunk-id>
Approved network rules hot-reload into the running sandbox. Logs and denied operations remain
available through openshell logs my-agent, and openshell sandbox delete my-agent stops the
workload, releases its resources, and purges injected credentials.
This setup is more involved than starting an unrestricted container, which is the point. The image defines the tools, the provider defines where a credential may be used, policy defines allowed access, and the gateway owns lifecycle and review. The main operational caveat is that its kernel controls have real prerequisites: the sandbox requires Landlock ABI 3 or newer and specific seccomp features. OpenShell probes those capabilities and fails closed when the boundary cannot be built.
How Autodev fits, and where it does not
Autodev currently uses per-task Docker containers for local development and Azure Container Apps custom dynamic sessions for Azure deployments. I chose Docker first because it made the system easy to run, inspect, and change while I was still learning what the execution contract needed to be.
Each task gets a separate Git worktree and execution environment. Local containers start without
network access. A recognized dependency-install command can receive temporary bridge access only
when both agent capability and policy allow it. The bridge is removed after that command. CPU,
memory, process, user, capability, and no-new-privileges settings constrain the container. The
worktree and evidence remain after the container is removed.
That design is useful for a local research workbench. It is not the boundary I would accept for hostile multi-tenant workloads. The control plane currently mounts the Docker socket, which gives it broad host authority. A hosted version needs a narrow sandbox broker, workload identity, and a stronger provider. A microVM service or managed equivalent is the direction I would evaluate, not a feature Autodev already has.
The useful part of bringing execution into Autodev was defining a provider boundary. The workflow can ask for a workspace, execute a typed command, change network state, collect evidence, and clean up without making the agent itself the sandbox manager. Docker and Azure implement that contract differently. A future provider should be replaceable without changing the policy or evidence model.
This integration also exposed a limitation in neat architecture diagrams: the control plane is part of the attack surface. Moving a workload into a microVM does not help if the broker accepts arbitrary host paths, hands secrets directly to the guest, or cannot terminate a stuck run. The broker API deserves the same threat modeling as the guest.
My verdict: choose the whole boundary, not the runtime label
I would not ask whether containers or microVMs are “the” answer for coding agents. Containers remain a practical choice for trusted local work and controlled CI tasks. gVisor offers an interesting middle layer when compatibility permits it. Kata and Firecracker-based systems make more sense as tenant trust decreases, provided someone operates the missing infrastructure. Managed sandboxes can be the best engineering choice when their controls and evidence meet the organization’s needs.
The decision should start with the workload and attacker model, then cover process, filesystem, network, credentials, resources, lifecycle, evidence, and control-plane privilege. If any of those rows is left implicit, the sandbox name says less than it appears to.
Autodev was useful to me because it forced those rows into one execution contract and made its weak points visible. Its current Docker implementation is a development compromise, not the conclusion of the research.
The next part of this series will cover agent governance and evaluation: why an agent needs an identity distinct from its user’s credential, how policy should mediate semantic capabilities, and why evaluation belongs in the promotion path. The final planned part is user experience and the collaboration space, including evidence review, intervention, approval, and handoff.