# Run code in a sandbox

URL: https://briq.cloud/docs/guides/run-code-in-a-sandbox

> Give the agent a microVM of its own for scripts, tests and builds, so nothing it runs touches your machine.

Recipe:

- [Sandbox](https://briq.cloud/cookbook/sandbox.md): A machine for the agent's own code: scripts, tests, builds, anything that should not run on your computer. Ubuntu with Python, Node, git and a C toolchain, plus the psql, redis-cli and sqlite3 clients. Commands run with briq_exec. The :full tag adds pandoc, ffmpeg, the Python data libraries and Playwright with Chromium.

An agent writes a script, wants to try a library, or has to run a test suite it does not fully
trust. A sandbox is a briq with nothing to serve: a microVM with Python, Node, git and a shell,
where the agent runs commands and reads their output. What is in it, tool by tool:
[Sandbox reference](https://briq.cloud/docs/sandbox). First, [connect your agent](https://briq.cloud/docs/getting-started).

> Run this project's tests in a sandbox on briq, not on my machine, and tell me what fails.

```text
briq_run({ recipe: "sandbox" })
→ bq_7kq2m9x1p4ab sandbox (ghcr.io/briq-cloud/sandbox:latest) running · cost so far … · sleeps at 18:42 UTC unless used
  outputs:
    WORKDIR=/work

briq_exec({ briq: "bq_7kq2m9x1p4ab", cmd: ["sh", "-c", "cd /work && git clone --depth 1 https://github.com/acme/shop . && npm ci"], async: true })
→ ex_4n8w2k5p1qzt running since 10:02 UTC
briq_exec_result({ briq: "bq_7kq2m9x1p4ab", exec: "ex_4n8w2k5p1qzt" })
→ ex_4n8w2k5p1qzt exited 0 after 48s

briq_exec({ briq: "bq_7kq2m9x1p4ab", cmd: ["sh", "-c", "cd /work && npm test"], async: true })
→ ex_9p2kd7mq1x4r running since 10:03 UTC
briq_exec_result({ briq: "bq_7kq2m9x1p4ab", exec: "ex_9p2kd7mq1x4r" })
→ ex_9p2kd7mq1x4r exited 1 after 2m
  stdout: … 2 failed, 36 passed …

briq_destroy({ briq: "bq_7kq2m9x1p4ab" })
```

1. `briq_run` starts a microVM with the image and a 2 GB volume at `/work`, and answers once a
   shell does.
2. `briq_exec` runs each command and returns its exit code and output. A command that can take
   over a minute runs with `async: true` ([Run a long command](https://briq.cloud/docs/guides/run-a-long-command)).
3. `briq_destroy` removes it. Forgotten, it sleeps after an hour unused and costs nothing until
   the next call.

## Files in and out

A script the agent wrote goes in with `briq_files_put`, and what the code produced comes back with
`briq_files_get`, 1 MB a file each way:

```text
briq_files_put({ briq: "bq_…", path: "/work/check.py", contentBase64: "…" })
briq_exec({ briq: "bq_…", cmd: ["python", "/work/check.py"] })
briq_files_get({ briq: "bq_…", path: "/work/report.csv" })
```

Bigger: fetch it from inside (`git clone`, `curl`), or pass a few small files at run as boot files,
`files: { "/work/check.py": "…" }`.

## Credentials

A token the code needs (a private repo, an API the tests call) goes in as a secret. The commands
read it from the environment; status, logs and audit show its name only:

```text
briq_run({ recipe: "sandbox", env: { GITHUB_TOKEN: { secret: "GITHUB_TOKEN", value: "github_pat_…" } } })
briq_exec({ briq: "bq_…", cmd: ["sh", "-c", "cd /work && git clone https://x:$GITHUB_TOKEN@github.com/acme/shop ."] })
```

Make the token for the task: a fine-grained GitHub token on one repo, read-only, that expires. Never
a key to your cloud or your production: the code in a sandbox runs with an open internet, and it
is the code you did not want on your machine.

## A service next to it

Tests that need a database get a real one, in the same stack. Its connection string holds a
password, so it goes in as a secret too:

```text
briq_run({ recipe: "postgres", stack: "shop" })
→ … DATABASE_URL=postgres://postgres:…@…internal:5432/app
briq_run({ recipe: "sandbox", stack: "shop", env: { DATABASE_URL: { secret: "DATABASE_URL", value: "<from postgres>" } } })
```

Every briq of the stack wakes and sleeps together. See
[Run tests against real services](https://briq.cloud/docs/guides/run-tests-against-real-services).

## Prepare once, run many

Install the dependencies in one sandbox, then fork it for each attempt. A fork starts with
`/work` as it was, so each run begins at "installed", not at "empty":

```text
briq_fork({ briq: "bq_7kq2m9x1p4ab", name: "attempt-1" })
```

See [Fork](https://briq.cloud/docs/concepts/fork).

## A server it starts

Publish the sandbox when you run it, and whatever listens on `$PORT` (8080) serves at `URL`:

```text
briq_run({ recipe: "sandbox", expose: "private" })
→ … URL=https://sandbox-x2kd.briq.run/?briq_token=…
briq_exec({ briq: "bq_…", cmd: ["sh", "-c", "cd /work && npm run dev -- --host :: --port $PORT"], async: true })
```

The URL answers once the server does, and only to whoever has it.

## Another image

The default image is [ours](https://github.com/briq-cloud/sandbox), under a gigabyte so a sandbox
starts in seconds. Its `full` tag adds document and media tools, the Python data libraries and
Playwright with a Chromium. Or pass any image that has `sh`:

```text
briq_run({ recipe: "sandbox", image: "ghcr.io/briq-cloud/sandbox:full" })
briq_run({ recipe: "sandbox", image: "golang:1.25-bookworm" })
briq_run({ recipe: "sandbox", image: "ubuntu:24.04" })
```

The recipe's command, volume and port stay.

## When it does not work

* **`exit 127`, `not found`**: the tool is not in the image. Install it (`apt-get install -y …`,
  `pip install …`) or run another `image`.
* **The command stopped after a minute**: `briq_exec` waits a minute at most. Run it again with
  `async: true`.
* **`the briq is stopped`**: the agent's daily cap or the team's credits stopped it; `briq_quota`
  says which. Otherwise `briq_start` it.
* **A build is slow** on `small`: one shared CPU, slowed down once a long build used its burst.
  Run the sandbox with `size: "medium"` or `"large"`.
* **`an async exec needs sh in the image`**: a distroless image. Use one with a shell.

## Good to know

* **One microVM per sandbox**, on the team's private network: the code reaches the team's other
  briqs and the internet, except outbound mail. See [Security](https://briq.cloud/docs/security).
* **Billed by the second while awake.** A running command keeps it awake; otherwise it sleeps
  after its `sleep` window (`sleep: "30m"` for a shorter one) and the next call wakes it.
* **Ten commands at once** per sandbox.
* **Files survive sleep**; a fork copies `/work`. `briq_destroy` deletes all of it.
* **Output**: `briq_exec` returns up to 32 KB; a long command's whole output stays in the briq,
  at the path `briq_exec_result` names.
