Somewhere for your agents to run code.
Sandy gives every agent an isolated Linux sandbox with a real, persistent shell. Start one in under two seconds, run anything in it, throw it away.
$sandy newsb-a1b2c3d4e5f60718 $sandy run "pip install pandas"Successfully installed pandas-2.3.4 $sandy run "python train.py"epoch 4/4 loss 0.0132 $sandy rm --id=sb-a1b2c3d4e5f60718Persistent shells
It behaves like a shell because it is one.
Most execution APIs spawn a fresh subprocess per call. It looks like a shell, so a model uses it like a shell — and then cd silently does nothing and the next command fails for reasons nothing in the transcript explains.
A Sandy sandbox holds one bash process open for its whole life. Your working directory, your environment, your installed packages, your background jobs — all still there on the next call. Pipes, redirects and && work because there is a real shell parsing them.
Subprocess per call
# a fresh subprocess for every call$cd /workspace$lsls: cannot access: no such file $export TOKEN=abc$echo $TOKEN # every command lands in a new worldSandy
# one bash process, kept alive$cd /workspace$lstrain.py data.csv $export TOKEN=abc$echo $TOKENabc # state accumulates, like a shell shouldPublic URLs
Every port is already on the internet.
Start a server on any port and it is reachable, immediately, at a URL derived from the port and the sandbox id. There is no expose call to make — because the thing writing the code is an agent, and an agent should not have to discover that a second API exists.
This is how an agent shows its work. It builds the app, and the URL is the artifact it hands back to you.
https://<port>-<sandbox-id>.sandy.host
$sandy run --id=sb-a1b2c3d4e5f60718 \> "python -m http.server 8080 &"https://8080-sb-a1b2c3d4e5f60718.sandy.host # no tunnel, no expose call, no config# another tenant, holding a valid API key$sandy run --id=sb-a1b2c3d4e5f60718 "cat /etc/passwd"error: sandbox not found (404) # the sandbox is not in their namespace,# so as far as Kubernetes is concerned# it does not existIsolation
A boundary you can point at.
Every customer gets their own Kubernetes namespace. Sandboxes are created in it, and reached through it. Nothing crosses.
That is a structural boundary, not a permission check somewhere in our code that could be written wrong on a Friday afternoon. For one tenant to reach another's sandbox, Kubernetes itself would have to be wrong.
Cold starts
Under two seconds to a live shell.
Sandboxes are handed out from a pool that is already warm. You are not waiting on an image pull, a scheduler, or a kubelet — the box exists before you ask for it.
Fast enough that creating a throwaway sandbox per task is a reasonable default, rather than something you architect around.
< 2s
to an allocated sandbox
1
bash process, for its whole life
1
namespace per customer
0
infrastructure you operate
Interfaces
A CLI for you. A JavaScript SDK for your code. An HTTP API for everything else.
The sandy binary is a thin client over the same public API you would call yourself. One static binary, no runtime to install — which also makes it something an agent can invoke directly as a bash command.
The @sandy-computer/sdk npm package is that same API typed for Node and Bun — no dependencies, one npm install @sandy-computer/sdk away. Reach for it when a program, not a person, is creating the sandboxes.
import { Sandbox } from '@sandy-computer/sdk'
const sbx = await Sandbox.create()
await sbx.files.write('/app/train.py', src)
const { stdout } = await sbx.commands.run('python train.py')
await sbx.kill()
// 'epoch 4/4 loss 0.0132\n'curl -X POST \
https://api.sandy.computer/sandboxes/sb-a1b2c3d4e5f60718/exec \
-H "Authorization: Bearer $SANDY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command": "python train.py"}'
{
"stdout": "epoch 4/4 loss 0.0132\n",
"exit_code": 0
}Real filesystem
A full Linux userland. apt, pip, npm, git — anything that runs on a box runs here.
File transfer
Push inputs in and pull artifacts out with one command, in either direction.
Honest exit codes
Exit status propagates unchanged, so an agent can actually tell whether it worked.
Dies when you say
One call and the sandbox, its filesystem, and its URLs are gone. No cleanup step.
Give your agent somewhere to work.
Create an account, make an API key, and run your first sandbox in about a minute.