One line of code, one fresh VM:

```bash
echo 'print(6*7)' | kedge eval python3
```

`kedge eval <runtime>` runs stdin in a fresh VM, streams stdout and stderr, and
exits with the code's status. The VM is destroyed afterward; nothing is deployed
or persisted.

Each run has the same [hardware isolation](/docs/security) as an app instance.
See [instant sandboxes & scale-out](/docs/performance) for restore latency.

## runtimes

| runtime | interpreter |
|---|---|
| `python3` | Python 3.12 |
| `node20` | Node.js 20 |
| `ruby` | Ruby 3.3 |
| `bash` | Bash |
| `deno` | Deno, TypeScript |

Every image also carries the [handler runtime](/docs/handlers) tools: `sqlite3`,
`jq`, `curl`, and `uptime`. A run gets 256 MiB of memory and 30 seconds; the
[REST eval operation](/docs/api#sandboxes) takes a `timeout` override.

## network access

Sandboxes have outbound network access by default. `-no-network` on `kedge eval`
or `kedge sandbox` leaves the VM with only a loopback interface. Anonymous runs,
including the front-page console, are always networkless regardless of what the
request asks for.

## interactive sandboxes

`kedge sandbox python3` drops you into a shell inside a fresh VM, and
`kedge shell -` does the same without naming a runtime. The VM is destroyed when
you exit.

## held sandboxes

To run several commands against one VM, open a sandbox and address it by ID:

```bash
id=$(curl -sX POST https://kedge.dev/api/sandboxes \
  -H "Authorization: Bearer $KEDGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"runtime":"node20"}' | jq -r .id)

curl -sX POST "https://kedge.dev/api/sandboxes/$id/exec" \
  -H "Authorization: Bearer $KEDGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cmd":["node","-v"]}'

curl -sX DELETE "https://kedge.dev/api/sandboxes/$id" \
  -H "Authorization: Bearer $KEDGE_TOKEN"
```

A sandbox expires 5 minutes after its last command; `ttl_seconds` at creation
sets a different deadline, up to an hour. One command is capped at 10 minutes,
and an account can hold only so many at once: past the cap a create fails with
a limit error rather than queuing. Expiry destroys the VM, so a forgotten ID
costs nothing.

An ID names the host holding the sandbox, so any node in the fleet answers a
call for it and `GET /api/sandboxes` lists your sandboxes fleet-wide. The VM
lives in that host's memory, so it does not survive that host's restart.

`POST /api/eval` and `POST /api/sandbox/exec` stay the shorthand when one command
is the whole job: they create, run, and destroy in a single call and leave no
identifier behind.

A sandbox has no ingress, and its disk is gone when it expires. Persistent
interactive work belongs in a [workspace](/docs/agents), which keeps its full
writable filesystem. [GitHub Actions runners](/docs/github-runners) apply the
same one-run boundary to CI jobs.
