How to Run Untrusted Agent Code Safely: Sandboxes Explained

·

6 min read

·

Summarize with AI
ON THIS PAGE

Solari runs untrusted agent code in a microVM of its own per task, and Solari sandboxes are ready in 90 ms. That matters because your agent writes code, runs it, and nobody reads it first: in production that code can pull in a bad package, read your API keys or hang a server, and a prompt injection is all it takes to point it there.

To run untrusted agent code safely, give each task its own sandbox: a disposable machine with its own kernel behind a hypervisor, outbound network limited to what the task needs, and no long-lived credentials inside. When the task ends, kill the machine or rewind it to a clean snapshot. On Solari that is one create call: the default isolation tier is a dedicated microVM, and the hardened tier also blocks all outbound network.

Why Agent Code Needs a Sandbox

Agent-written code is code nobody reviewed. It can install packages, read environment variables, open sockets and loop forever, and a prompt injection in a web page or a file can steer it. Running it on your app server, or in a container that shares the host kernel, leaves one kernel bug between that code and your production systems.

A sandbox moves the code to a machine that exists only for that task. A Solari sandbox is "a machine for running code and automation, with no screen": it runs commands, keeps code state between calls, watches files, and can be snapshotted and forked. Solari runs its own marketing system in a Solari sandbox for the same reason.

Which Isolation Boundary to Pick

Pick a hypervisor boundary for code you did not write. A microVM gives each sandbox its own kernel, so an exploit has to escape a virtual machine, not just a namespace. Containers and userspace kernels are lighter, and they are a fair choice for code you trust more.

Here is how the main sandbox vendors describe their own boundaries:

Provider

Default isolation

Other options

Source

Solari

A dedicated microVM per sandbox

"hardened" blocks outbound network; "shared" uses namespaces and a cgroup

Solari sandboxes docs

E2B

A separate Firecracker microVM per sandbox

BYOC and self-hosted deployment

E2B's sandbox guide

Modal

gVisor or a VM runtime, Modal's choice if unset

GPU sandboxes run on gVisor

Modal sandboxes docs

Daytona

Linux containers

Linux VM, Windows, macOS and GPU sandboxes

Daytona sandboxes docs

Solari's dedicated tier is a Cloud Hypervisor microVM with a dedicated kernel per session. The shared tier is a different trade: it isolates sandboxes "by namespaces and a cgroup rather than by a hypervisor", and only from your own organization's other shared sandboxes. Across organizations nothing changes: you never share a VM with another org.

How to Set Up a Safe Sandbox on Solari

Create the sandbox on the hardened tier, write the agent's code into it, run it with a deadline, and set the idle action to kill. With @solarisdk/sandbox it is a few lines:

import { SandboxClient } from "@solarisdk/sandbox"

const sandboxes = new SandboxClient({ apiKey: process.env.SOLARI_API_KEY! })

const sbx = await sandboxes.create({
  template: "base",
  isolation: "hardened",            // own microVM, outbound network blocked
  cpu: 2,
  memMb: 2048,
  lifecycle: { onTimeout: "kill" }, // discard, don't pause, when idle
})
await sbx.connect()

await sbx.files.write("/work/task.py", agentCode)
const res = await sbx.commands.run("python3", { args: ["/work/task.py"] })
if (res.timedOut) throw new Error("agent code hit its deadline")
import { SandboxClient } from "@solarisdk/sandbox"

const sandboxes = new SandboxClient({ apiKey: process.env.SOLARI_API_KEY! })

const sbx = await sandboxes.create({
  template: "base",
  isolation: "hardened",            // own microVM, outbound network blocked
  cpu: 2,
  memMb: 2048,
  lifecycle: { onTimeout: "kill" }, // discard, don't pause, when idle
})
await sbx.connect()

await sbx.files.write("/work/task.py", agentCode)
const res = await sbx.commands.run("python3", { args: ["/work/task.py"] })
if (res.timedOut) throw new Error("agent code hit its deadline")
import { SandboxClient } from "@solarisdk/sandbox"

const sandboxes = new SandboxClient({ apiKey: process.env.SOLARI_API_KEY! })

const sbx = await sandboxes.create({
  template: "base",
  isolation: "hardened",            // own microVM, outbound network blocked
  cpu: 2,
  memMb: 2048,
  lifecycle: { onTimeout: "kill" }, // discard, don't pause, when idle
})
await sbx.connect()

await sbx.files.write("/work/task.py", agentCode)
const res = await sbx.commands.run("python3", { args: ["/work/task.py"] })
if (res.timedOut) throw new Error("agent code hit its deadline")

Four settings do the work. The hardened tier closes outbound network; switch to "dedicated" only when the task must reach an API, and pass only the keys that task needs. The size is a hard bound: a dedicated Solari sandbox takes 1 to 8 CPUs, and a larger request gets a 400 rather than being quietly reduced. A command that passes its deadline returns timedOut instead of throwing, so check it before trusting the exit code. And onTimeout "kill" ends an idle sandbox instead of pausing it, which suits code you plan to discard.

Keeping Credentials Out of Reach

Store keys as account secrets and hand them to the sandbox by name; never paste them into the prompt or the code. Solari secrets "are stored encrypted, belong to your organization, and are write-only: no call ever returns a value."

Design the run around where the value lives. A secret sits in the sandbox's environment while it runs and travels with any pause or snapshot, so "anyone who can restore that snapshot can read it." Give an untrusted run scoped, short-lived keys, or none at all. E2B takes another route: E2B says its secret injection keeps credential values outside the sandbox.

Exposed ports need the same care. previewUrl() returns a signed URL with a one-hour token, and that URL is "a bearer credential": anyone holding it reaches the port until it expires.

Reset, Pause or Throw Away

Throw the machine away when the task is done, or rewind it to a clean snapshot when setup is slow. Solari snapshots follow a simple pattern: prepare a machine once, take a snapshot, then start "as many ready-to-go copies as you need" with fromSnapshot. revert() rewinds the same machine to a snapshot under the same id, so each agent starts without leftovers from the last one.

Pause is for work you want to keep, not code you want to forget. Sandboxes share the Solari VM lifecycle: a paused machine saves its state, the next connect picks up where it stopped, and "A paused VM doesn't count against your plan's limit on running VMs."

What Sandboxes Cost

Solari bills sandboxes by vCPU-hour and GB-hour while they run. Solari's Professional plan is $0.0245 per vCPU-hour and $0.0077 per GB-hour, and Team is $0.0175 and $0.0055. Snapshot storage is $0.05 per GB-month, with the first 10 GB free.

A 4 vCPU, 8 GB sandbox hour costs $0.114 on Solari's Team plan. Solari's pricing page lists that size at $0.331 on E2B, $0.333 on Daytona and $0.761 on Modal at list price, which makes Solari the lowest sandbox hour in that comparison. On their own pages, E2B says its Pro plan is $150 a month plus usage, with sessions up to 24 hours, and Daytona says it bills $0.0504 per vCPU-hour, with $200 in free compute to start.

Limits to Know Before You Ship

Run hostile code on the hardened or dedicated tier. The shared tier is built for trusted, lightweight work: it runs commands.run and runCode with system directories read-only. On that tier, size processes from the sandbox's configured memory rather than /proc/meminfo.

Treat every open port as public. A port opened with no token is reachable by anyone who guesses the hostname, with no expiry, so keep that for pages with nothing behind them.

Speed is part of safety, because a fast sandbox makes one machine per task cheap to adopt. A full Solari sandbox lifecycle takes 8.2 s, and Solari's browsers average 199.9 ms end to end on Steel's open-source benchmark, across 5,000 runs.

Where Each Fits

Safe agent code comes down to four habits: a microVM per task, a closed network, scoped secrets and a disposable machine. E2B, Modal and Daytona are the more established sandbox vendors with larger ecosystems; Solari is the younger company and moves faster on speed and price. Solari gives every task a dedicated microVM by default, a hardened tier with the network closed, and the lowest sandbox hour in its price comparison. Create a hardened sandbox from the Solari sandboxes guide on Solari's Free plan today, and when the same agent needs to operate a website, Solari's Cloud Browsers run from the same account and API key, as set out in Introducing Solari. The companion piece, What Is the Best Sandbox for Running AI Agent Code Securely, compares the vendors in more depth.

FAQ QUESTIONS:

  1. Is a container enough to isolate AI-generated code?

  2. How do I stop sandboxed agent code from reaching the internet?

  3. Can code inside a sandbox read my API keys?

Share this article

Give every agent
a place to work

Browsers, sandboxes and desktops from one API. Start free and scale when you need to.