Skip to content

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.

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, subprocess
out = subprocess.run(["python3", "solution.py"], capture_output=True, text=True)
reward = 1.0 if out.stdout.strip() == "42" else 0.0
print(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).

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 browser

For 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.

You want to…UseWhy
Run a rollout and get a rewardexec-in-guestGrade in the guest, return a scalar — the RL default
Run a test suite / eval and read the verdictexec-in-guestCommand + exit code, no external round-trips
Fan a warmed parent out with forkexec-in-guestEach child runs the same in-guest step
Byte-exact file IOexec-in-guestupload_bytes / download over the exec channel — see Files
Drive a browser step-by-step from a Playwright scriptconnect-from-outsideYour existing client code drives the live browser over CDP
Hit a preview / dev server running in the sandboxconnect-from-outsideA live HTTP handle to a port in the guest
Interactive, human-in-the-loop or IDE-attached workconnect-from-outsideA durable live session you reach into repeatedly
Reuse an existing client library (CDP, notebook, HTTP)connect-from-outsideKeep 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.