| Runtime | Package |
|---|---|
| TypeScript / JavaScript | @brimble/sandbox |
| Python | brimble-sandbox |
| Go | github.com/brimblehq/brimble-sdks/sandbox-go |
Install
Authenticate
All three SDKs read the same environment variable,BRIMBLE_SANDBOX_KEY, set it to your account-level API key from the profile drawer (avatar → API key) in the dashboard. You can also pass the key explicitly to the client constructor. See API keys for how to generate, rotate, and rate-limit.
baseUrl/base_url/BaseURLsets the API root. Defaults tohttps://sandbox.brimble.io.timeoutMs/timeout_ms/Timeoutsets the per-request HTTP timeout. Defaults to 30 seconds.retry/retry/Retrylets you override the retry policy (see Retries and idempotency).- TypeScript-only:
fetchImplsupplies a customfetchfor tests. - Python-only:
sessionsupplies arequests.Sessionfor connection pooling. - Go-only:
HTTPClientsupplies a custom*http.Client.
The client surface
Every client exposes three resource groups:| Resource | What it covers |
|---|---|
client.sandboxes | Lifecycle: create, list, get, destroy, pause, resume. Discovery: list templates, list regions. Plus use(id) for scoped runtime ops. |
client.snapshots | Account-wide snapshot listing and deletion. (Per-sandbox snapshot ops live on the handle.) |
client.volumes | Volume lifecycle: create, list, get, delete. |
client.Ping(ctx), a one-shot connectivity and auth check.
Quickstart presets
The shortest path to a running sandbox is a quickstart helper. They preconfigure the template, persistent disk, and ready-wait so you can get to work in a single call.createReady (next section).
Create-and-wait helpers
createReady is the workhorse: provision a sandbox and block until it’s ready in one call. You don’t have to think about the starting → ready transition.
region is optional, omit it (or pass "auto") to let the server pick. Pass a specific region ID from listRegions() to pin one.
getReady(id) does the same for an existing sandbox: fetch + wait, so you can pick a sandbox back up after a process restart without coordinating the state check yourself.
If you want manual control over the wait (longer timeout, custom poll interval), call create() then handle.waitUntilReady() separately:
Create a sandbox alongside a fresh volume
withVolume provisions both in a single call. Use it when you want a per-sandbox persistent workspace and don’t already have a volume.
region and the sandbox’s region must resolve to the same region. Passing "auto" on both is the simplest path.
The sandbox handle
create(), createReady(), get(), getReady(), and withVolume() all return a handle, an object that bundles the sandbox ID with runtime helpers. Most of what you do with a sandbox happens through the handle, not the client.
You can also grab a handle for any sandbox by ID:
| Surface | TypeScript | Python | Go |
|---|---|---|---|
| Sandbox ID | handle.id | sandbox.id | handle.ID() |
| Cached status | handle.status | sandbox.status | handle.Status() |
| Full record | handle.data | sandbox.data | handle.Latest() |
| Refresh from API | handle.refresh() | sandbox.refresh() | handle.Refresh(ctx) |
| Destroy | handle.destroy() | sandbox.destroy() | handle.Destroy(ctx) |
| Pause / resume | handle.pause() / handle.resume() | sandbox.pause() / sandbox.resume() | handle.Pause(ctx) / handle.Resume(ctx) |
| Wait until ready | handle.waitUntilReady() | sandbox.wait_until_ready() | handle.WaitUntilReady(ctx) |
| Snapshots namespace | handle.snapshots.create/list | sandbox.snapshots.create/list/iterate | handle.Snapshots.Create/List |
Auto-wait on runtime ops
Every runtime method (exec, runCode, putFile, getFile, stats, createSnapshot, listSnapshots) accepts an optional waitUntilReady flag. When set, the SDK polls the sandbox until it’s ready before sending the actual call, so you don’t have to coordinate the wait yourself.
ready. The fast path for one-off scripts is therefore create() followed by exec(..., { waitUntilReady: true }). The fast path for a long-lived workflow is createReady() so the handle is hot the moment you reach for it.
Sandboxes resource (full surface)
Create
region is required (use "auto" to let the server pick); everything else is optional.
cpu 1 to 2000 (MHz units), memory 1 to 2048 MB, disk 1 to 5 GB ephemeral.
List, get, destroy
destroy is idempotent: calling it on an already-destroyed sandbox is a no-op.
Iterate across pages
For walking every sandbox without hand-rolling pagination:Pause and resume
handle.status reflects the new value immediately.
Runtime operations
Once the sandbox isready (or you’ve passed waitUntilReady):
Exec
exec buffers, you get stdout, stderr, exit_code, and duration_ms once the command finishes. For long-running commands where you want output as it arrives, see Stream exec output below.
Run code
Per-call environment variables
Bothexec and runCode accept an env object that’s layered on top of the sandbox’s existing environment for a single call. Same-named keys override the sandbox-level value for that invocation only; the next call starts from the sandbox defaults again. Cleaner than shelling out export FOO=bar && in front of every command, and stops secrets from showing up in argv / shell history.
Stream exec output
Bothexec and runCode can stream output as it’s produced, useful for long-running commands, AI-agent runs, or anywhere you want to surface partial output to a user instead of waiting for the whole command to finish. The server emits one NDJSON frame per chunk (stdout, stderr, done, or error); each SDK gives you back a stream you read with native primitives.
run_code_stream / RunCodeStream for streaming the language-aware variant. In TypeScript, the same stream: true opt-in works on runCode as well, the return type narrows to a readable stream.
Frame shapes (typed as ExecStreamFrame in TS, returned as JSON in all three):
{ "type": "stdout", "data": "..." }, a chunk of stdout text.{ "type": "stderr", "data": "..." }, a chunk of stderr text.{ "type": "done", "exit_code": 0, "duration_ms": 142 }, the final frame on a clean run.{ "type": "error", "message": "..." }, a transport-level error after streaming began.
Upload and download files
400.
Batch file uploads
For seeding many small files in one round trip (a fresh repo checkout, a set of config files, a directory of fixtures), use the batch upload. It sends the files as base64-encoded JSON in a single POST and returns a per-file success/failure summary.putFile instead.
Stats
Snapshots
Per-sandbox snapshot ops live on the handle. Account-wide ops live onclient.snapshots.
^[a-z0-9-]{1,40}$. Creation is async: the response returns status: "creating" and the snapshot flips to ready (or failed) a few minutes later. Poll list to see the transition.
Restore from a snapshot
PassfromSnapshot at sandbox create time to seed the new sandbox with the snapshot’s filesystem:
Volumes
The SDKs only create sandbox-type volumes (type: "sandbox"); the web type is reserved for the dashboard’s persistent-disk toggle on a project. All three SDKs enforce this client-side and reject any other value before the HTTP call.
volumeId on create. See the Volumes doc for the full attach model.
Discovery
Both templates and regions are first-class on the client. No need to hit a separate API.listTemplates() is the authoritative way to see what’s currently available.
Pagination
Alllist endpoints accept { page, limit } (plus teamId on team-scoped calls). Defaults are page = 1, limit = 15, max limit = 100. Responses include totalCount, currentPage, totalPages, limit, and a data array.
For walking every result, prefer the iterate / iterate_all / Iterate helpers shown above; they handle pagination internally.
Errors
Every SDK exposes a typed error hierarchy. All subclasses extend the base type so a singlecatch / except / errors.As block can still handle “anything from the API,” but you can narrow when you want to.
| Class | When |
|---|---|
SandboxApiError / APIError | Base type. Any non-2xx response. |
AuthError | 401 / 403. Missing key, revoked key, insufficient permission. |
ValidationError | 400 / 422. Bad input shape, invalid state transition. |
NotFoundError | 404. Sandbox / volume / snapshot doesn’t exist or isn’t yours. |
RateLimitError | 429. Carries retryAfterSeconds / retry_after_seconds / RetryAfterSeconds when the server provides it. |
status, message, endpoint, responseBody, and requestId.
- Python:
wait_until_ready()raisesTimeoutErroron deadline.Sandbox(api_key=...)raisesValueErrorif no key is found.volumes.create()raisesValueErrorfor size or type violations before the HTTP call. - TypeScript: the constructor throws a plain
Errorif no key is found. - Go:
context.DeadlineExceeded/context.Canceledfor caller-driven cancellation; check witherrors.Is.
Retries and idempotency
Each SDK ships a built-in retry policy you can configure at the client or per call. Defaults are conservative: one attempt (no retries) by default, base delay 300 ms, max delay 3 s, retry on408, 429, 500, 502, 503, 504. Bump maxAttempts to opt in.
idempotencyKey within a short window: create, createReady, destroy, pause, resume, createSnapshot, deleteSnapshot, and volume create / delete all accept the option. Pass a stable, unique-per-operation value (a UUID, a job ID, the SHA of the request body) and you can retry network failures without spinning up a duplicate sandbox.
Timeouts and cancellation
| Operation | TypeScript | Python | Go |
|---|---|---|---|
| Default HTTP timeout | 30s | 30s | 30s |
| Per-call override | timeoutMs in options | timeout_ms=<n> | ctx deadline / RequestOptions.Timeout |
| Cancel mid-flight | signal: AbortSignal | not directly cancellable | ctx.Done() |
| Wait-until-ready default | 60s timeout, 2s poll | 60s timeout, 2s poll | 60s timeout, 2s poll |
| Wait-until-ready cancel | signal: AbortSignal | raises TimeoutError | ctx.Done() |
context.Context with a deadline matched to the operation. The SDK respects ctx.Done() everywhere, including the wait-until-ready polling loop.
Cleanup
None of the SDKs auto-destroy sandboxes when your process exits. Either setautoDestroy: true with a destroyTimeout, use oneShot: true so the sandbox terminates when its main process exits, or wrap your work in a cleanup block:
Versioning and stability
All three SDKs are at 0.x today; the public surface is shaped to stay stable, but minor bumps may break compatibility while we shake things out. Pin a specific version in production until we tag 1.0:Next steps
- Sandboxes overview, the lifecycle and billing model.
- Quickstart, the five-minute end-to-end walkthrough with all three SDKs side by side.
- Cookbook, recipes for the highest-traffic use cases.
- Snapshots, deep-dive on the snapshot lifecycle.
- Volumes, persistent volumes that survive sandbox destruction.
- Sandbox API tab, the REST contract the SDKs wrap.