Giving an agent a code interpreter changes what it can learn. Instead of guessing whether a transformation works, it can run it. It can inspect a repository, compile a program, query a dataset, and test an idea against the machine.
The same capability changes the security model. Generated code can read a credential, exhaust memory, contact an unexpected server, or alter work that belongs to another run. It does not need malicious intent. The model can be mistaken, a dependency can be compromised, or a document can contain instructions designed to redirect the agent.
Treat agent-controlled code as untrusted code. A sandbox enforces limits on what that code can access and consume.
A sandbox is a boundary, not a temporary folder
Teams often begin by running code in a child process with a fresh working directory. This is convenient and may prevent accidental edits to the current project. It is not a security boundary.
The process may still inherit environment variables, open file descriptors, network access, local services, and a view of the host filesystem. It shares the host kernel and may run with the same identity as the application. Changing the current directory changes where relative paths begin. It does not change what the process is allowed to reach.
A complete sandbox answers a broader set of questions:
- Which files can the workload read, write, or execute?
- Which processes, devices, and kernel interfaces can it reach?
- Which network destinations are allowed?
- Which credentials are available, for what operation, and for how long?
- How much CPU, memory, storage, and wall-clock time can it consume?
- Which state persists after the run?
- How are artifacts inspected before they leave the boundary?
These controls work together. A read-only filesystem does not help if the process can send sensitive input over the network. Network isolation does not prevent one tenant from reading another tenant's mounted files. A memory limit does not stop an agent from invoking a privileged local socket. Sandboxing is the composition of isolation, resource governance, lifecycle, and data policy.
It is also workload-specific. A browser agent needs network access but may not need a host filesystem. A coding agent needs a writable workspace and package access but should not see production credentials. A data agent may need one governed dataset and no outbound internet. “Run safely” only becomes meaningful when expressed as concrete capabilities.
Choose isolation for the consequence of escape
Linux provides useful primitives for restricting processes. Namespaces separate views of processes, mounts, users, and networks. cgroups account for and limit resources. Capabilities divide some of the authority traditionally attached to root. Security modules such as AppArmor, SELinux, and Landlock can restrict filesystem and kernel access.
Seccomp filters system calls, but the Linux kernel documentation states that seccomp is not a sandbox by itself. It reduces the kernel surface available to a process. It must be combined with other controls. Cgroups also do not isolate data or processes. They govern and account for resources.
Containers combine several of these primitives, but they share the host kernel. Their isolation depends on kernel correctness, runtime configuration, mounted resources, daemon access, and the privileges granted to the container. A hardened container may be appropriate for trusted internal work or workloads with limited consequences. Public multi-tenant code execution often requires a stronger boundary.
Two common approaches add another isolation layer. Firecracker is a virtual machine monitor built on KVM with a minimal device model. Its jailer process adds host-side restrictions. The full boundary still depends on correct storage, metadata, network, credential, and control-plane configuration.
gVisor provides a userspace application kernel. It intercepts application system calls and page faults. It implements much of the Linux interface in userspace and makes a limited set of calls to the host kernel. This reduces the kernel surface exposed to the workload. It does not protect against every class of attack, including Spectre-style CPU side channels.
Neither option is universally better. MicroVMs provide a hardware virtualization boundary but require more infrastructure. A userspace kernel can fit container workflows but may have compatibility or performance costs for some workloads.
The right question is not which technology has the strongest marketing claim. It is what happens if the boundary fails. A private, single-user scratchpad and a public service executing arbitrary code from thousands of users do not carry the same consequence. Isolation should match the trust level of the input, sensitivity of reachable data, tenant model, and value of the host.
Use defense in depth even with a strong isolation layer. Run without unnecessary privilege. Prefer rootless operation where it fits. Avoid host mounts and access to container runtime sockets. Use minimal images. Patch the kernel and runtime. Separate workloads across nodes or accounts when their risk differs.
Restrict network access and credentials
Compute isolation is only part of the problem. Useful agents need data and services, and those connections are often more consequential than local code execution.
Unrestricted outbound network access allows a workload to send out anything it can read. It also lets generated code download arbitrary dependencies, scan internal addresses, or call services outside the user's intent. For arbitrary untrusted code, begin with no network access and grant the destinations the task requires.
This can mean an allowlist of package registries or a controlled HTTP proxy. Another option is tool-mediated access with no arbitrary sockets. A proxy can enforce destination policy, attach task identity, limit request methods, record metadata, and remove sensitive headers. DNS needs the same attention because a blocked IP policy with unrestricted name resolution can be incomplete or easy to misconfigure.
Credentials should not be placed in the prompt, image, or general environment. If code needs to read a repository, the harness can issue a short-lived token scoped to that repository and operation. Better still, the sandbox can call a broker that performs the authorized request without revealing the underlying secret. The credential then belongs to the control plane, not to generated code.
Design egress policy and secret policy together. A secret that cannot leave the environment can still be abused against its intended service. A network allowlist does not prevent an over-scoped token from deleting data at an allowed destination. A sandbox limits which capabilities the workload can reach. It cannot prevent abuse of a capability the system intentionally granted. The broker must check the user, tenant, task, action, and resource for each request.
Package installation deserves explicit treatment. Installing from the public internet runs third-party code during the build or install process. Reproducible base images, lockfiles, internal mirrors, signature verification, and dependency caches reduce both risk and latency. For high-risk workloads, build dependencies in a separate restricted phase and run the resulting artifact in a networkless phase.
Control environment lifetime and persistent state
A strong default is an ephemeral environment created for one run and destroyed afterward. Ephemerality limits the lifetime of malicious files, leaked tokens, and accidental configuration. It also makes failures easier to reproduce because each run starts from a known base. It does not undo external side effects or protect a shared cache from contamination.
Purely fresh environments can be slow. Agents repeatedly download dependencies, index repositories, or build the same artifacts. The solution is not undefined persistence. It is named persistence.
A practical system can combine a versioned read-only base image, a run-scoped writable workspace, and explicit caches with controlled ownership and expiry. User files can live in a durable store outside the sandbox and be mounted or copied into a task-specific view. Each persistent layer has a purpose, an owner, and an invalidation rule.
Cleanup must occur even when the agent fails, times out, or is cancelled. The control plane should revoke credentials, terminate processes, detach storage, release leases, and mark artifacts according to policy. Background reapers can handle abandoned environments, but the run record should show whether cleanup completed.
Resource limits are part of this lifecycle. CPU, memory, process count, disk, output size, and wall-clock time all need ceilings. Without them, an accidental fork bomb or endless compilation becomes a denial-of-service attack. Limits should produce understandable observations so the agent can reduce its approach or explain that it reached a boundary.
Artifacts crossing out of the sandbox need review. A generated archive can contain unexpected files or symlinks. A rendered document can carry active content. A binary may be executable even if the user expected data. Export rules can restrict file types, scan content, normalize archives, and require approval for higher-risk outputs.
Observe the boundary without recording the user's work
Operators need the image, policy, resource use, network destinations, and final environment status. They do not automatically need every line of source code, command output, or user data processed inside it.
Useful sandbox telemetry includes opaque run and tenant identifiers, image digest, isolation profile, and stop reason. It can also include resource peaks, network destinations, policy denials, tool operation IDs, and artifact hashes. Do not use raw personal information as an identifier. Full command or file capture should be purpose-limited, access-controlled, and retained according to a clear data policy.
This distinction improves both privacy and debugging. Structured events can reveal that a workload hit its memory limit or attempted a blocked destination without copying the private dataset that triggered it. When deeper inspection is necessary, temporary elevated capture can be enabled for a specific run with authorization and an expiry.
Sandbox testing should be adversarial. Try path traversal, symlink escapes, fork bombs, oversized output, access to metadata services, internal network scans, stale credential reuse, archive tricks, and cleanup failure. Test the control plane as well as the guest. Access to a container daemon or orchestration API can bypass guest isolation by mounting host or tenant resources.
Sandboxing does not fix a vulnerable application inside the guest. It also does not correct authorization errors in the control plane. The design must protect the workload boundary, management interface, and capabilities granted to each run.
A sandbox does not make generated code trustworthy. It limits what untrusted code can reach, measures what it consumes, and gives the system a reliable way to end its authority. That is the right objective. The agent can experiment freely inside a boundary precisely because the boundary is real.