Appearance
Chrome
The chrome template runs Chromium on a VNC display, with the remote DevTools protocol (CDP) on port 9222.
Setup
bash
pip install ovrin
export OVRIN_API_KEY="ovrin_..."Drive it from your machine
sandbox.devtools() returns the browser's DevTools WebSocket on the control plane and the header it takes, under the names Playwright uses:
python
import ovrin
from playwright.sync_api import sync_playwright
client = ovrin.Client() # reads OVRIN_API_KEY
sandbox = client.sandboxes.create(template="chrome", timeout=900)
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(**sandbox.devtools())
page = browser.contexts[0].new_page()
page.goto("https://example.com")
print(page.title()) # Example Domain
page.screenshot(path="example.png")
sandbox.kill()ts
const { endpointURL, headers } = await sandbox.devtools();
const browser = await chromium.connectOverCDP(endpointURL, { headers });- Your API key is the lock. DevTools itself checks nothing; the connection goes through the control plane, which takes your key and the sandbox's scope.
- From a script or an agent, not a web page. A page cannot set the
Authorizationheader, and Chromium refuses DevTools connections that carry anOrigin. - Use
devtools()rather than/json/versionyourself. Chrome names its WebSocket after the address it was reached on, which for a sandbox is the sandbox's own;devtools()swaps in one you can reach.
This needs a deployment that serves Ovrin's Chrome image: GET /capabilities then reports the chrome template as available. On upstream's image, reported as degraded with devtools_loopback_only, DevTools answers inside the sandbox only, so drive it from there, as below.
Drive it from inside the sandbox
python
import ovrin
client = ovrin.Client() # reads OVRIN_API_KEY
sandbox = client.sandboxes.create(template="chrome", timeout=900)
sandbox.files.write("/workspace/drive.py", """
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp("http://localhost:9222")
page = browser.contexts[0].new_page()
page.goto("https://example.com")
print(page.title())
page.screenshot(path="/workspace/example.png")
""")
result = sandbox.run("python /workspace/drive.py", timeout=300)
print(result.stdout) # Example Domain
png = sandbox.files.read_bytes("/workspace/example.png")
open("example.png", "wb").write(png)
sandbox.kill()Publishing a port
For a service the sandbox serves on 0.0.0.0, sandbox.endpoint(port) returns its public URLs:
python
endpoint = sandbox.endpoint(8080)
print(endpoint["url"]) # HTTP
print(endpoint["websocket_url"]) # WebSocketThe console's endpoint panel shows the same two. Both take your API key in an Authorization header, so they are for scripts and agents rather than a browser tab — see WebSocket endpoints.
Chrome or Playwright?
chrome | playwright | |
|---|---|---|
| What runs | Chromium with CDP on 9222 | Headless Chromium plus the Playwright library |
| Best for | Attaching to a live browser, DevTools work | Scripted navigation, scraping, screenshots |
Related
- Playwright — the scripted-automation path
- Agent environments — every template