Two ways to use a sandbox
00 / Access models
A Collimate sandbox is a live microVM. There are two ways to put it to work, and they answer two different questions:
- exec-in-guest — "run this and tell me what happened." You hand the platform a command; it runs inside the sandbox and returns stdout, stderr, and an exit code. The work — and any grading — happens in the guest; you get a result back.
- connect-from-outside — "give me a live handle to something running in there." A service is already running inside the sandbox (a browser speaking CDP, an HTTP server, a notebook kernel). The platform hands you a URL, and you drive that service remotely from your own machine.
Same sandbox, same lifecycle, same fork — two ways to reach in.
exec-in-guest
Section titled “exec-in-guest”This is the default, and the right call for almost all RL and batch work. The command runs in the guest, so the loop is compute → grade → return a scalar with no round-trips leaving the machine.
from collimate_rl import connect
client = connect(api_key="col_...")
sb = client.create_sandbox("python")result = client.exec( sb["id"], commands=[["python3", "-c", "print(sum(range(100)))"]], timeout_seconds=30,)print(result["stdout"]) # -> "4950\n"client.delete_sandbox(sb["id"])The grader lives in the guest too, so a rollout returns the number you actually train on — not a transcript you have to re-score outside:
GRADE = """import json, subprocessout = subprocess.run(["python3", "solution.py"], capture_output=True, text=True)reward = 1.0 if out.stdout.strip() == "42" else 0.0print(json.dumps({"reward": reward}))"""result = client.exec(sb["id"], code=GRADE, timeout_seconds=60)Because everything stays in the guest, exec-in-guest is what
fork fans out: one warmed parent, N children, each running
the same in-guest step. See Running code (exec) for the full
request shape (command / code / commands, files, timeout_seconds,
seed, egress).
connect-from-outside
Section titled “connect-from-outside”Sometimes the work isn't "run and return" — it's an interactive session you drive step by step from client code you already have: a Playwright script, a notebook client, an HTTP client hitting a preview server. For that, connect to a service running inside the sandbox and drive it live.
The SDK gives you a signed, per-sandbox URL to a port inside the guest. Open a
session — its handle carries the mint methods:
with client.session("browser") as sb: # a live Session (not the raw dict) url = sb.connect_url(9222) # wss URL to port 9222 (raw tcp tunnel) cdp = sb.cdp_url() # == connect_url(9222, protocol="cdp")connect_url(port) returns a
wss://api.collimate.ai/v1/sandboxes/<id>/connect?port=...&t=<token> URL. The
token scopes the connection to that one sandbox and port; the platform bridges it
to the in-guest service. cdp_url() mints the same URL with protocol="cdp" — a
DevTools-framed tunnel a CDP client consumes directly (a plain tcp tunnel can't
carry CDP). From your own machine you drive it like any other remote endpoint —
for a browser, connect_over_cdp:
from playwright.sync_api import sync_playwright
with client.session("browser") as sb, sync_playwright() as pw: browser = pw.chromium.connect_over_cdp(sb.cdp_url()) page = browser.new_context().new_page() page.goto("https://example.com") print(page.title()) browser.close() # closes the connection, not the in-guest browserFor a plain HTTP/WebSocket/TCP service (a preview server, Jupyter, an OpenEnv
env-server) forward the guest port to a local one instead — any client hits
localhost:
with client.session("app") as sb: # a template that exposes :8000 with sb.port_forward(8000) as fwd: import urllib.request urllib.request.urlopen(fwd.url + "/health") # fwd.url = http://127.0.0.1:<local>See Remote browser for the full walkthrough, and Live browser for baking the CDP service into the template so every fork wakes with it already running.
When to use which
Section titled “When to use which”| You want to… | Use | Why |
|---|---|---|
| Run a rollout and get a reward | exec-in-guest | Grade in the guest, return a scalar — the RL default |
| Run a test suite / eval and read the verdict | exec-in-guest | Command + exit code, no external round-trips |
Fan a warmed parent out with fork | exec-in-guest | Each child runs the same in-guest step |
| Byte-exact file IO | exec-in-guest | upload_bytes / download over the exec channel — see Files |
| Drive a browser step-by-step from a Playwright script | connect-from-outside | Your existing client code drives the live browser over CDP |
| Hit a preview / dev server running in the sandbox | connect-from-outside | A live HTTP handle to a port in the guest |
| Interactive, human-in-the-loop or IDE-attached work | connect-from-outside | A durable live session you reach into repeatedly |
| Reuse an existing client library (CDP, notebook, HTTP) | connect-from-outside | Keep your driver on your machine; only the service is remote |
The two are not exclusive. A common pattern: bake a live service into the template so it is already running (Live browser), then either attach in-guest with a fast raw-CDP step (exec-in-guest) or connect to it from your own machine (connect-from-outside) depending on whether you need a scalar reward or an interactive session.
Next steps
Section titled “Next steps”- Running code (exec) — the exec request and response shape.
- Remote browser —
cdp_url()andconnect_over_cdpend to end. - Live browser — bake the CDP service into the template.
- Live fork — fan one warmed parent out into N children.