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
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. First, connect your agent.
Run this project's tests in a sandbox on briq, not on my machine, and tell me what fails.
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" })briq_runstarts a microVM with the image and a 2 GB volume at/work, and answers once a shell does.briq_execruns each command and returns its exit code and output. A command that can take over a minute runs withasync: true(Run a long command).briq_destroyremoves 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:
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:
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:
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.
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":
briq_fork({ briq: "bq_7kq2m9x1p4ab", name: "attempt-1" })See Fork.
A server it starts
Publish the sandbox when you run it, and whatever listens on $PORT (8080) serves at URL:
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, 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:
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 anotherimage.- The command stopped after a minute:
briq_execwaits a minute at most. Run it again withasync: true. the briq is stopped: the agent's daily cap or the team's credits stopped it;briq_quotasays which. Otherwisebriq_startit.- A build is slow on
small: one shared CPU, slowed down once a long build used its burst. Run the sandbox withsize: "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.
- Billed by the second while awake. A running command keeps it awake; otherwise it sleeps
after its
sleepwindow (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_destroydeletes all of it. - Output:
briq_execreturns up to 32 KB; a long command's whole output stays in the briq, at the pathbriq_exec_resultnames.