Beta

SB-Box

Isolated sandboxes for agents — a fresh machine in under half a second, frozen and resumed with its memory intact.

Build a template once, start as many identical machines as you need, freeze them when they go quiet and pay only for their disk. One gsk token drives the CLI and the REST API beta.

Architecture

Four design choices make a sandbox feel like a machine.

Not a container with extra walls: a virtual machine whose memory, disk and lifecycle are data the platform can snapshot, fork and wake. These are the four pillars the story below stands on.

PILLAR 01 · SNAPSHOT RESTORE

A create is a page-in, not a boot

Every sandbox is a full virtual machine with its own kernel. A template is the snapshot of a machine that was already running your start command; creating from it restores that snapshot, with memory arriving on demand as the machine touches it and the hot set preloaded from the template’s own warm-up runs. A 4 GB machine is up in 0.35 s median, 0.43 s at the 90th percentile (0.70 s at the 99th) measured at the platform; the public API and your round trip add a few hundred milliseconds. A resume from frozen is the same mechanism: 0.8 s median, 1.7 s at the 90th.

  • own kernel per sandbox
  • restore, not boot
  • memory on demand
  • warm-up prefetch

PILLAR 02 · STATE AS DATA

Freeze, fork and roll back a running machine

freeze saves only what changed since the template, then releases the machine — a frozen sandbox keeps its processes, memory and disk and bills only its disk. resume continues where it stopped. Every freeze is a version: clone forks any version into an independent machine, rollback returns to one, reset wipes processes but keeps the disk. Memory snapshots expire after 14 days frozen; the disk stays.

  • incremental snapshots
  • copy-on-write forks
  • versions & rollback
  • reset keeps the disk

PILLAR 03 · SELF-DRIVING LIFECYCLE

Goes quiet by itself, wakes on the next request

A sandbox freezes on its own after 10 to 15 minutes idle, or when its lifetime deadline passes (onTimeout: freeze or kill), and exits when its init process exits. Nothing is resumed by hand: the next command, file operation or HTTP request to one of its port URLs resumes a frozen sandbox first, about four seconds end to end for that first call. A metrics sample every five seconds shows CPU, memory and disk.

  • idle auto-freeze
  • implicit resume
  • onTimeout: freeze | kill
  • metrics

PILLAR 04 · ISOLATED, CONTAINER-NATIVE

Any Docker image, its own kernel, your network rules

Templates build from any public Docker image or a Dockerfile. Hardware virtualization gives each sandbox its own kernel, so untrusted code never shares one with a neighbour. Egress is a per-sandbox policy (allow or deny lists, or no internet at all); anything listening on a port is reachable at https://<port>-<id>.sandbox.…, and allowPublicTraffic: false puts the sandbox’s own traffic token in front of every port URL. exec returns stdout, stderr and the exit code; streaming exec, background commands and a WebSocket PTY cover the rest.

  • Docker image → template
  • egress allow / deny
  • per-port URLs
  • streaming exec & PTY

In action

From one command to a machine that waits for you.

Three moments: an agent handed a skill file, an agent handed its own machine, and a training run handed one environment to fork. All of it is the same gsk sb-box commands you would type.

ACT 01

A code interpreter for your own agent: SB-Box as an agent skill

Agents write code; something has to run it. Drop one SKILL.md into your project and a coding agent such as opencode runs generated code on a sandbox instead of your laptop, with the same gsk sb-box commands as the quick start below. Two things keep it simple for the model: --session names the task's sandbox, so it never handles an id, and run-code is a stateful Python interpreter — variables persist between calls like a notebook. No code to maintain.

You
bash · in your project, once
curl -fsSL https://www.genspark.ai/sb-box/opencode/install.sh | sh
wrote  .opencode/skills/sb-box/SKILL.md

One file, nothing else. Needs gsk on PATH (npm i -g @genspark/cli && gsk login); sandboxes come from the public base image.

markdown · .opencode/skills/sb-box/SKILL.md, the steps

How to use me

  1. One sandbox per task, named by --session. Pick a short name for the task (the project folder name is a good one) and pass the same --session value to every command. The first run-code, exec or write creates the sandbox from the public base image (Python 3 with pandas, numpy, matplotlib; Node; git); later commands reuse it, also after the platform froze it. You never handle a sandbox id.

    gsk sb-box run-code --session "$TASK" --code 'print("hello")'
    
  2. Files first. The sandbox is a separate machine: nothing from the working directory is visible there. Copy in what the code needs, then refer to it under /home/user/.

    gsk sb-box write --session "$TASK" --path /home/user/data.csv --content "$(cat data.csv)"
    
  3. Run, then read the answer from data. For step-by-step analysis prefer the stateful interpreter: variables and DataFrames persist between calls like a notebook, and plots come back as chart data. The first call starts it; later cells reuse the running kernel.

    gsk sb-box run-code --session "$TASK" --code 'import pandas as pd; df = pd.read_csv("/home/user/data.csv"); df.describe()'
    
    gsk sb-box run-code --session "$TASK" --code 'df.groupby("region").margin.mean()'      # df is still there
    
  4. Bring results back with gsk sb-box read --session "$TASK" --path /home/user/out.json

  5. Kill it when the task is done, unless the user wants to keep it:

    gsk sb-box kill --session "$TASK"
    
Agent
opencode · the session
$ opencode          # SKILL.md sits in .opencode/skills/sb-box/; nothing else to wire up
> sales_q3.csv in this folder is last quarter's export. Which region is dragging
  margin down? Don't run code on this machine.

⚙ skill   sb-box                              # the model loads the instructions on demand
⚙ glob    **/sales_q3.csv  →  ./sales_q3.csv
Found the file. One sandbox for the whole task, named after it:
⚙ bash    gsk sb-box write --session sales-q3 --path /home/user/sales_q3.csv --content "$(cat sales_q3.csv)"
          → { "written": 92, "session": { "key": "sales-q3", "sandbox_id": "sbx-4321f1…", "created": true } }   # created the sandbox, then wrote
⚙ bash    gsk sb-box run-code --session sales-q3 --code 'import pandas as pd
          df = pd.read_csv("/home/user/sales_q3.csv")
          df["margin"] = (df.revenue - df.cost) / df.revenue
          df'   # first cell starts the interpreter
          → results[0].text:   region  revenue   cost  margin
                             0   EMEA    100.0   82.0    0.18
                             1   APAC    100.0   73.0    0.27
                             2  LATAM     50.0   34.5    0.31
                             3   AMER    200.0  132.0    0.34
⚙ bash    gsk sb-box run-code --session sales-q3 --code 'total = (df.revenue.sum() - df.cost.sum()) / df.revenue.sum()
          df.assign(vs_total=df.margin - total).sort_values("margin")[["region","margin","vs_total"]]'   # df still there
          → results[0].text:   region  margin  vs_total
                             0   EMEA    0.18 -0.105556
                             1   APAC    0.27 -0.015556
                             2  LATAM    0.31  0.024444
                             3   AMER    0.34  0.054444
⚙ bash    gsk sb-box run-code --session sales-q3 --code 'df.set_index("region").margin.plot.bar(title="Q3 margin by region")'
          → results[1].chart: { "type": "bar", "elements": [ { "label": "EMEA", "value": 0.18 }, … ] }   # png omitted by default
⚙ bash    gsk sb-box kill --session sales-q3      # task done, machine gone, session binding dropped
EMEA is dragging margin down: 18 % against 28.6 % overall, 10.6 points below the
blended rate and nearly half of AMER's 34 %. For every $1 of revenue, $0.82 goes to cost.
Every gsk call and output above is real, recorded against SB-Box on the public base image; the model's prose between them is condensed. Nothing of yours ran, and the model never saw a sandbox id: --session named the task, the first write created the machine and bound it to that name, and every later command found it again. It then asked the sandbox's interpreter three questions in a row — the DataFrame from the first cell was still there for the second and third, and the plot came back as chart data — and killed it. The same binding works without the flag: inside Claude Code the CLI picks up the host's session id on its own, and GSK_SB_BOX_SESSION names it anywhere else. The whole integration is one plain-text file — SKILL.md — fetched by install.sh.
ACT 02

The agent's own computer: lights off, not power off

Turn ACT 01 around: give the agent a machine of its own and let the work happen there — its copy of the repo, its dev server, its test runs — with nothing of yours in reach. The agent itself keeps running where it already does, and your key never leaves your laptop; every command it issues goes to the sandbox through gsk sb-box exec --session. Then close the laptop: the machine idles out and freezes — processes, memory and files intact — and bills only its disk until something touches it.

You
bash · the agent's machine, once
gsk sb-box exec --session dash --timeout 300 -c 'git clone https://github.com/acme/dashboard && cd dashboard && npm ci'
{
  "stdout": "added 412 packages in 18s",
  "exitCode": 0,
  "session": { "key": "dash", "sandbox_id": "sbx-a41d02…", "created": true }
}

The session has no deadline; only the idle timer applies. The repo lives on the sandbox from here on — nothing is cloned to your laptop, and no key is copied anywhere: the agent runs locally with the sb-box skill from ACT 01 and reaches this machine by name.

bash · hand it the task, go home
opencode run "In sandbox session dash, pytest fails in ~/dashboard. Find the bug, fix it, run pytest again and tell me what it was. Use gsk sb-box exec --session dash for every command — do not run anything on this machine."
⚙ bash    gsk sb-box exec --session dash -c 'cd dashboard && pytest -q'
          → 1 failed, 23 passed   # FAILED tests/test_todo.py::test_complete — assert done is True
⚙ bash    gsk sb-box read --session dash -p /home/user/dashboard/todo.py
⚙ bash    gsk sb-box write --session dash -p /home/user/dashboard/todo.py -d "$(…fixed file…)"
⚙ bash    gsk sb-box exec --session dash -c 'cd dashboard && pytest -q'
          → 24 passed
The bug was in complete() at todo.py:6 — it set done to the string "yes" instead of True. All tests pass.

Fixed. Leave the dev server running on its machine and go home:

gsk sb-box exec --session dash -c 'cd dashboard && (npm run dev -- --port 3000 >/tmp/dev.log 2>&1 &) && sleep 2'

Preview, alive while the sandbox is, at its port URL (the record's portUrl with port 3000): https://3000-sbx-a41d02.sandbox.westus2-prod.sspark.ai. Then you close the laptop.

Platform
11 pm. The sandbox froze hours ago — the dev server, the repo, the fix, all of it. You open the preview URL; the request resumes the machine in a second or two and the page loads from the still-running dev server.
You
bash · laptop, next morning
gsk sb-box get -s sbx-a41d02
{
  "id": "sbx-a41d02",
  "state": "frozen"
}

Frozen since last night: no compute billed, only the disk. A resume takes about a second, and the dev server never restarted:

gsk sb-box resume -s sbx-a41d02
{
  "id": "sbx-a41d02",
  "state": "running"
}
gsk sb-box exec -s sbx-a41d02 -c 'curl -s localhost:3000/api/health'
{
  "stdout": "ok",
  "exitCode": 0
}
Platform
Two timers: timeout is the hard deadline you choose — none by default, over REST and from gsk sb-box create alike, and the sandbox is frozen when it hits. The platform's idle timer freezes a quiet sandbox — no network traffic, CPU idle — 10 to 15 minutes after its last activity (a 10-minute timeout, reported back as idleTimeout, plus up to five minutes of spread so a fleet does not freeze in lockstep). Frozen time costs no compute — only the disk bills. Coming back to a frozen machine is a resume, not a rebuild — the agent's working tree, caches and shell are exactly where it left them.
You
Same idea for "Ops Anna": she watches the support queue and drafts replies, and may only reach our internal CRM — nothing else on the internet.
bash · the employee's machine

Her machine is the same kind of sandbox with two extras: an egress allow-list and a port the ticket webhook calls. Network rules are REST-only today.

API=https://www.genspark.ai/api/sb-box/v1; H='X-Api-Key: gsk-…'
curl -s -X POST "$API/sandboxes" -H "$H" -H 'Content-Type: application/json' -d '{
  "templateId": "tpl-4755…",
  "timeout": 0,
  "network": {
    "allowInternetAccess": false,
    "allowOut": ["10.20.0.0/16"]
  }
}'
{
  "id": "sbx-91be47",
  "state": "running"
}
gsk sb-box write -s sbx-91be47 -p /home/user/anna.py -d "$(cat anna.py)"
{
  "written": 2140,
  "path": "/home/user/anna.py"
}
gsk sb-box exec -s sbx-91be47 -c 'cd /home/user && (python3 anna.py --port 8080 &) && sleep 2'

Every ticket webhook goes to the sandbox's port URL — the record's portUrl with port 8080, https://8080-sbx-91be47.sandbox.westus2-prod.sspark.ai/tickets; a frozen Anna is resumed by the request itself.

Platform
a week later · what the platform sees
gsk sb-box get -s sbx-91be47
{
  "id": "sbx-91be47",
  "state": "frozen"
}
curl -s "$API/usage?days=7" -H "$H"
{
  "days": 7,
  "direct": { "runSec": 2460, "cpuCoreSec": 4920, "memGBSec": 9840,
              "diskGBSec": 12096000, "credits": 224.03 }
}

41 minutes of actual running on the 2 vCPU / 4 GB box is 56 credits of compute for the whole week; the 20 GB disk adds 24 credits a day (0.05 credit per GB-hour, 1 credit an hour) for as long as the sandbox exists — 168 of the 224. There is no cap on how long a sandbox may stay frozen — only its memory snapshot expires after 14 days untouched (then a reset boots it from disk).

ACT 03

The environment for agentic RL: one task, eight attempts, graded by tests

In agentic RL the sandbox is not where the policy runs — the policy is on your GPUs. The sandbox is the environment: the repo, the services, the tests. Every tool call the policy makes executes in it, and the grader runs in it to produce the reward. A freeze is a snapshot of the whole machine, memory included, so one frozen environment forks into as many attempts as a training step needs — each starting from the identical warm state, nothing to seed or reload. Two numbers to plan around: clones of one account are issued one at a time (about half a second each) and your plan caps how many sandboxes run at once, so eight attempts per task means eight running sandboxes per task in flight; and two thousand frozen environments at 20 GB each are 40 TB of disk, billed daily for as long as they exist.

You
Two thousand bug-fix tasks. Each training step samples eight attempts per task from the current policy; the reward is whether the task's tests pass.
Platform
Agent
bash · build the environment, once per task
gsk sb-box exec --session task-0417 --timeout 300 -c 'git clone https://github.com/acme/calc && cd calc && pip install -q -r requirements.txt && pytest -q; true'
gsk sb-box exec --session task-0417 -c 'cd calc && (./services/start.sh >/tmp/services.log 2>&1 &) && sleep 2'
gsk sb-box freeze --session task-0417
{
  "id": "sbx-0417a1…",
  "state": "frozen",
  "versions": [
    { "version": 1 }
  ]
}

The frozen state is the environment: dependencies installed, services up, tests already run once so every cache is warm (the freeze archived the bare template as v1; the environment itself is the live frozen snapshot). It stays frozen for as long as the task is in the training set.

bash · one training step, one task
ATTEMPTS=$(for i in $(seq 8); do gsk sb-box clone -s sbx-0417a1… | jq -r .data.id & done; wait)

Eight identical machines. The policy on your GPUs now acts in each one; every action is an exec, its output the next observation.

gsk sb-box exec -s $A -c 'cat calc/calc.py'                                   # the policy reads
gsk sb-box exec -s $A -c 'sed -i "s/return a - b/return a + b/" calc/calc.py'   # the policy edits

When the attempt ends, the grader runs in the same machine. Tests pass → 1.0, anything else → 0.0.

while read -r A; do gsk sb-box exec -s $A -c 'cd calc && pytest -q' | jq -c '{reward: (if .data.exitCode == 0 then 1.0 else 0.0 end)}' & done <<<"$ATTEMPTS"; wait
{"reward": 1.0}
{"reward": 0.0}
{"reward": 1.0}
{"reward": 1.0}
{"reward": 0.0}
{"reward": 1.0}
{"reward": 0.0}
{"reward": 1.0}

Rewards and trajectories go to the trainer; the eight machines go away. The environment is still frozen, untouched, for the next step.

while read -r A; do gsk sb-box kill -s $A & done <<<"$ATTEMPTS"; wait
The trainer — verifiers, OpenEnv, prime-rl, your own loop — runs on your side and drives the sandboxes through the API: one clone per attempt, one exec per policy action, one exec for the grader. Two thousand tasks are two thousand frozen environments; attempts are forks, and a fork carries the running services and the warm caches with it, so no attempt pays for setup. Rules: a freeze archives the snapshot it replaces (the first freeze files the template baseline as v1); clone --version N never touches the source; rollback --version N archives "now" first, so it is itself reversible.

Plans & limits

Limits follow your membership.

How many sandboxes you can run at once, how big each one can be and how many templates you can build in parallel depend on your plan. Run time is never capped — a frozen sandbox costs only its disk, and your credit balance is the only limit. The table is on the plans & billing page; the console shows your own plan and usage live.

Quickstart

Four steps to a machine your agent can trust.

Run these from your laptop, a CI job, or anywhere the gsk CLI is installed. Every step has a REST equivalent in the docs.

STEP 01

Get your API token

Install the CLI and sign in once. The same token works for the CLI and the REST API beta.

bash
$ npm i -g @genspark/cli
$ gsk login
# browser opens, you authorize, token is saved to ~/.genspark-tool-cli/config.json
STEP 02

Pick a template — or don't

create with no --template starts from the Genspark public base template: Ubuntu 24.04, Python 3 with pandas, Node 22, git. Build your own only when you need more — pick an image, say what should be running when the snapshot is taken, ready in a few minutes.

bash
gsk sb-box templates
{
  "templates": [
    { "id": "tpl-4755…", "name": "sb-box-base-…", "namespace": "public", "status": "ready" }
  ]
}

The public base is already there. Your own image, snapshotted once (optional):

gsk sb-box build-template --template py312 --image python:3.12-slim
{
  "templateId": "tpl-2c9e…",
  "status": "building"
}
STEP 03

Start a sandbox, run things

Lifetime in seconds. One call per action; output comes back as JSON, files stream in and out by path.

bash
gsk sb-box create --timeout 600
{
  "id": "sbx-7f3a9c",
  "state": "running",
  "idleTimeout": 600
}

Public base by default; add --template py312 for your own (exec, files and ports work the same there, with one difference: on a custom image commands run as root with HOME=/root and there is no /home/user unless your image creates it; run-code's stateful interpreter ships only in the public base). Then run, write, read:

gsk sb-box exec -s sbx-7f3a9c -c 'python3 -c "print(6*7)"'
{
  "stdout": "42",
  "exitCode": 0
}
gsk sb-box write -s sbx-7f3a9c -p /home/user/hi.txt -d 'hello'
{
  "written": 5,
  "path": "/home/user/hi.txt"
}
gsk sb-box read -s sbx-7f3a9c -p /home/user/hi.txt
{
  "content": "hello",
  "path": "/home/user/hi.txt"
}
STEP 04

Freeze when you step away, kill when you're done

A freeze keeps everything, memory included, and stops the compute bill (the disk keeps billing); a resume takes about a second. Or just let the idle timer freeze it for you, 10 to 15 quiet minutes after the last command.

bash
gsk sb-box freeze -s sbx-7f3a9c
{
  "id": "sbx-7f3a9c",
  "state": "frozen"
}

Everything kept, memory included; only the disk bills while frozen. Back in about a second:

gsk sb-box resume -s sbx-7f3a9c
{
  "id": "sbx-7f3a9c",
  "state": "running"
}
gsk sb-box kill -s sbx-7f3a9c
{
  "status": "killed",
  "sandbox_id": "sbx-7f3a9c"
}