> ## Documentation Index
> Fetch the complete documentation index at: https://gateway.forceaisecurity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Run an agent in an isolated sandbox

> Execute an agent's code inside a locked-down OpenSandbox box while its model calls stay governed by the gateway, and pick that isolation per agent from the dashboard.

An agent that only reasons and calls models needs no sandbox: the gateway already governs every call. The moment an agent **runs code** (a generated script, a shell command, a build, browser automation) that code needs somewhere safe to run. The isolated sandbox substrate gives it one: each agent gets its own [OpenSandbox](https://github.com/opensandbox-group/OpenSandbox) box with its own filesystem and processes, its network egress pinned so the only thing it can reach is the ForceAI gateway, and its scoped key held outside the box so the workload never reads it

<Note>
  This is compute isolation, which is different from validating a model. The gateway keeps doing guardrails, spend, and RBAC; OpenSandbox only contains the compute. Agent Runtime is admin-only
</Note>

## Two independent choices

When you spawn an instance you pick two things that do not affect each other, so any profile pairs with any isolation:

| Choice            | What it controls                                     | Options                               |
| ----------------- | ---------------------------------------------------- | ------------------------------------- |
| Runtime profile   | how long the instance lives and whether it is reused | `ephemeral-task`, `session`, `pooled` |
| Compute isolation | where the agent's code runs                          | Governed only, Isolated sandbox       |

Practical pairings: `ephemeral-task` with an isolated sandbox gives a fresh box per task that is hard-killed on completion, which is the safest choice for untrusted code. `session` with an isolated sandbox keeps one box across a user's session, which suits a coding agent iterating on the same workspace. `pooled` with an isolated sandbox keeps boxes warm for latency at the cost of reusing a box across tasks

<Warning>
  The profile's own "isolation" column (per task, per session, multiplexed) describes instance **reuse**, not compute isolation. A `pooled` profile still runs in a real sandbox when you select Isolated sandbox; it just reuses that box
</Warning>

## Turn the substrate on

The OpenSandbox server ships off by default. In the local stack it is an opt-in compose profile:

```bash theme={null}
docker compose --profile opensandbox up -d opensandbox-server
```

The substrate creates each sandbox **from a container image**, so the backend needs one it can pull. The default `forceai/runtime-runner:local` is a placeholder, so point it at a real base until you build your own runner:

```bash theme={null}
FORCEAI_RUNTIME_RUNNER_IMAGE=python:3.11 \
  docker compose up -d --force-recreate --no-deps forceai-backend
```

On-prem, enable the chart's opt-in sidecar instead (it uses the in-cluster Kubernetes runtime and gets its own ServiceAccount and Role to spawn sandbox pods):

```yaml theme={null}
sidecars:
  openSandbox:
    enabled: true
```

Confirm the deployment now offers both substrates. If this returns only `gateway`, the sandbox server is not reachable and the dashboard will not show the choice:

```bash theme={null}
curl -s https://YOUR_GATEWAY_DOMAIN/v1/runtime/substrates \
  -H "Authorization: Bearer $ADMIN_KEY"
# {"substrates":["gateway","opensandbox"]}
```

## Create one from the dashboard

<Steps>
  <Step title="Open Agent Runtime">
    Go to **Agentic -> Agent Runtime** and click **Spawn instance**
  </Step>

  <Step title="Pick the profile">
    **Runtime profile** sets the lifecycle. `ephemeral-task` is the safest default for code you do not trust: one task, then hard-killed
  </Step>

  <Step title="Choose Isolated sandbox">
    Set **Isolation** to `Isolated sandbox (opensandbox)`. This is the field that puts the agent's code in a box; leaving it on `Governed only` means the agent makes governed model calls but executes nothing
  </Step>

  <Step title="Set the model and task">
    Pick the model the agent runs its tasks against, optionally give it a first task, and click **Spawn**
  </Step>
</Steps>

The new row shows a purple **Isolated sandbox** tag and goes to `ready` once the box is up. **Run** executes a task inside it, **Details** shows the endpoint and task history, and **Kill** destroys the box and revokes the key

## Create one over the API

The same thing for automation. `substrate` is what makes it isolated; omit it and you get the deployment default:

```bash theme={null}
curl -X POST https://YOUR_GATEWAY_DOMAIN/v1/runtime/agents \
  -H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
  -d '{
    "profile": "ephemeral-task",
    "agent_type": "chat",
    "substrate": "opensandbox",
    "task": "echo hello"
  }'
# -> {"instance_id":"...","substrate":"opensandbox","state":"ready","endpoint_url":"..."}
```

Then run a task in the box and kill it when done:

```bash theme={null}
curl -X POST https://YOUR_GATEWAY_DOMAIN/v1/runtime/agents/$INSTANCE_ID/tasks \
  -H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
  -d '{"task": "python3 -c \"print(6*7)\"", "capability": "chat"}'

curl -X DELETE "https://YOUR_GATEWAY_DOMAIN/v1/runtime/agents/$INSTANCE_ID?force=true" \
  -H "Authorization: Bearer $ADMIN_KEY"
```

## What ForceAI does for you

Spawning an isolated agent is a governed operation, not just a container start. ForceAI mints a **scoped, budget-capped key** for that one instance, then creates the sandbox with a **deny-by-default network policy whose only allow rule is the gateway**, so a prompt-injected or buggy agent cannot reach an arbitrary endpoint. When the Credential Vault is enabled the scoped key is registered **outside** the box and injected into the agent's outbound calls by the egress sidecar, so the model calls are authenticated without the workload ever being able to read the secret. Every model call still passes the guardrail, spend, and budget checks at the gateway, and killing the instance tears down the box and revokes the key

## Check it is really isolated

The dashboard tag is the quick check; the containers are the proof. Each instance runs as its own box plus an egress sidecar:

```bash theme={null}
docker ps --format '{{.Names}}' | grep sandbox-
# sandbox-<id>          the isolated box
# sandbox-egress-<id>   its egress sidecar (network policy + Credential Vault)
```

## Before you rely on it

Two things are worth knowing up front. The sandbox is created from whatever runner image you configured, so an agent that should reason and call models from inside the box needs a runner image that does that; a plain base image like `python:3.11` only executes commands you send it. And Credential Vault injection needs the gateway reachable by **FQDN on a transparent-intercept port** (443 by default) plus the egress sidecar in `dns+nft` mode on a privileged or secure runtime, so on a local stack where the gateway is `forceai-gateway:4000` you get the egress pin but not the key injection

<Tip>
  Not every agent needs this. If an agent only reasons and calls approved tools, leave it on **Governed only**: it is cheaper and needs no extra infrastructure. Reach for the isolated sandbox when the agent executes code
</Tip>
