Give an agent a machine to test on
Questi contenuti non sono ancora disponibili nella tua lingua.
A rig is a throwaway machine an agent opens when it needs to actually try something: load a page, click through a desktop app, tap around a phone. You can watch it work in real time, and take the controls whenever you want.
Enclosed terminal, browser and desktop rigs are VMs with throwaway disks and gated networks. Android and iOS are host-managed emulators: their device state is disposable, but their networking follows the server host.
Set it up once
Section titled “Set it up once”Rigs run on the machine hosting cc_server — the same machine, not the one
you are looking at. There are five user-facing surfaces; desktop and mobile
surfaces need host setup:
| Surface | Runs on | Setup |
|---|---|---|
| Terminal (VM) | A microVM booting a pinned Ubuntu image | None — the image is fetched automatically on first use |
| Browser (VM) | A microVM booting a pinned Debian image with Chromium, Firefox or WebKit installed on first use | None — same |
| Computer (VM) | QEMU with a Linux desktop image that ships Chromium, Firefox, WebKit and a GitHub-hosted-runner CLI baseline | Install QEMU (brew install qemu), then Import a desktop image under Settings → Server → Rigs |
| Android | Google’s Android emulator | Install the Android SDK, create an AVD and start it (below) |
| iOS Simulator | CoreSimulator on the server Mac | Install Xcode and the requested iOS runtime, then install the automation bridge under Settings → Server → Rigs |
The terminal and browser machines boot from digest-pinned images: the exact bytes are named in advance, fetched once on first boot, and cached — after the first boot of each kind, later machines skip the download entirely and come up in seconds. Settings → Server → Rigs shows what your server can boot and every machine running right now.
An installed desktop image stays manageable from the same page: Import replaces it with a newer qcow2, and Delete removes it so you can install a fresh build.
Mobile devices are set up differently
Section titled “Mobile devices are set up differently”There is no image of ours for Android or iOS, and that is not an omission. Android system images come from Google’s SDK; iOS runtimes come from Xcode. For Android, install Android Studio (or the command-line SDK tools), add a virtual device in Device Manager, and start it. The Rigs page then reports Android as available.
It also tells you exactly which step you are missing — no SDK, no emulator, no
virtual device, or a device that simply is not running — because those have
four different fixes and only one of them is a download. If you start
cc_server from a shell that cannot see the SDK, export it:
export ANDROID_HOME="$HOME/Library/Android/sdk"For iOS, the server itself must run on macOS with Xcode and an iOS Simulator runtime installed. Open Settings → Server → Rigs and press Install iOS automation bridge. The server fetches the checksum-pinned WebDriverAgent artifact, verifies it, and installs it into the server-owned automation store. No simulator boots during setup.
Open a machine
Section titled “Open a machine”Machines live as tabs, next to the chat and the diff — in a space or on a
PR page, in the same + menu, under Machines:
- Computer — a Linux desktop with Chromium, Firefox and WebKit
- Chromium, Firefox or WebKit — a browser
- Android — the running Android emulator
- iOS Simulator — a disposable simulator created for this conversation
Pick one and press Start the machine. The tab says which stage the boot is in (“Starting the microVM”, “Waiting for the guest to come up”) rather than just spinning.
The tab is scoped to the conversation, and so is the agent tool. A Chromium,
Firefox or WebKit tab and browser_use, Android and mobile_use, or iOS
Simulator and ios_use address the same default machine. Additional UI slots
are separate comparison machines; agent tools always use the default.
To build, install and start a Flutter app on Android:
fvm flutter build apk --debugThen call mobile_use with install_apk and
build/app/outputs/flutter-apk/app-debug.apk, followed by start_app and the
Android package name. ui_dump, screenshots and input drive the same device
shown in the human tab. stop_app, clear_app_data, uninstall_app,
open_url, and argv-shaped shell calls cover the rest of the development
cycle without invoking a shell on the server host.
To build, install and start a Flutter app on iOS Simulator:
fvm flutter build ios --simulator --debug/usr/bin/plutil -extract CFBundleIdentifier raw -o - \ build/ios/iphonesimulator/Runner.app/Info.plistThen call ios_use with install_app and the .app directory, followed by
start_app and the bundle identifier printed by plutil.
The iOS tool also exposes stop_app, uninstall_app, open_url, and an
argv-shaped spawn action for simulator-local diagnostics.
Desktop and browser displays resize to the panel. iOS keeps the simulator’s fixed point size and letterboxes it. Every surface still has two independent lanes: the agent gets small, cheap screenshots while you get the live view. Switching tabs stops the human stream so a background machine costs no display bandwidth.
A rig tab does not start a machine by itself, including during layout restore. That rule prevents an app launch from unexpectedly booting VMs or creating simulator devices.
Drive the machine
Section titled “Drive the machine”The canvas is yours to type and click into whenever nobody else holds exclusive control — or when you do. While you hold that lock, the status tag reads You have control, the agent can still take screenshots, and the server refuses its mutating actions at the single chokepoint every action passes through. Everything you do is recorded against your name.
Press Stop the machine when you are done.
Control is exclusive on purpose: two actors typing into one machine produces input neither of them meant.
Move things in and out
Section titled “Move things in and out”Copy and paste work in both directions across the rig canvas, and so does dragging files.
Copy and paste. Press the copy chord over the canvas and whatever the machine copied — text, an image, a list of files — lands on your clipboard. Paste sends yours the other way and presses paste inside the machine. On macOS the chord is ⌘C/⌘V; ⌃C still reaches a guest shell as an interrupt. On Windows and Linux use ⌃C/⌃V — the guest receives the same keystroke it would have anyway, so ⌃C in a guest terminal still interrupts and your own clipboard is left alone.
Nothing syncs in the background. A password you copy on your own machine does not arrive inside a VM because a timer fired; it crosses when you paste.
Paste into a rig is allowed by default. Copy out of a rig prompts first — a ten-minute grant, or Always allow under Settings → Server → Rigs → Clipboard access. The two directions are independent, and they follow you, not the workspace.
Drag files in. Drop them anywhere on the canvas.
- On the Browser (VM), the page receives a real drop at the point you let go, so an upload zone behaves exactly as it would with a local file.
- On the Computer (VM), the files land in
~/Dropsand their paths go on the machine’s clipboard, so ⌃V pastes them into a file manager or an upload field. A host cannot synthesize a drag into an arbitrary Linux app without running a privileged daemon inside the VM, which rigs deliberately do not have — so the toast tells you where the files went rather than pretending a drop happened. - On a terminal, the file is copied in and its path is typed at the prompt, ready to pass to a command. Pasting an image into a terminal does the same thing: it is saved in the machine and you get the path.
Drag files out works from the Computer (VM). Start dragging a file inside the machine, carry the pointer out of the canvas, and it becomes a real drag on your desktop — drop it in Finder or Explorer. Copying files in the machine’s file manager and pasting on your side works too. The Browser (VM) does not offer this: a headless browser has no drag payload to read, and guessing from the page’s text selection would hijack ordinary clicks.
Terminals inside a rig
Section titled “Terminals inside a rig”The terminals in a space or on a PR page can run inside a rig instead of on the server’s own machine. When they do, the panel shows an Enclosed VM badge.
What changes:
- Nothing you run reaches the host. A
rm -rfin there costs you the rig. - The network is deny-by-default. Only hosts on the rig’s allowlist are reachable, and everything goes through a gate that enforces it.
git pushstill works. The rig has no stored credential; it asks the server for a short-lived one per operation, and that token is revoked when the rig closes.
What to know:
- A restored enclosed terminal waits for you. Like a rig tab, a
microvmterminal that comes back with your layout shows an Open the shell button rather than booting its VM at launch. A host-shell terminal costs a process; an enclosed one costs a machine. - Files dropped on the terminal are typed, not opened. Drop a CSV on an enclosed terminal and it is copied into the machine and its guest path appears at the prompt — the host path would be a command that fails there.
- The copy in the rig is a copy. Your worktree on the host stays the source
of truth. Commits you make inside a rig are carried back into
refs/rigs/<id>/*when you ask — they never move your branch or touch your working tree. - Host-target work still runs on the host. A Linux guest cannot build for
macOS or run
xcodebuild. Pick the native backend explicitly for that; the app will ask you to confirm rather than switching quietly.
Run a dev server in the Terminal (VM) — pnpm dev on port 3000, say — and
within a few seconds the plug icon in the terminal’s corner lights up: the
port is forwarded to localhost:3000 on your machine, reachable at
localhost:3000 inside the Browser (VM), shareable on your network, and
nameable as https://myapp.test. The whole flow — auto-forwarding, LAN
sharing, dev domains with HTTPS — is its own guide:
Forward ports from a terminal.
Custom images
Section titled “Custom images”A workspace can point the Terminal (VM) and Browser (VM) at its own images — under Settings → Server → Rigs → Custom images (this workspace). Typical use: extend the default image with the toolchain your project needs so every terminal starts ready, or reuse an image your team already publishes.
The machine still boots inside the same enclosure: same egress gate, same credential broker, same lifecycle. Only workspace admins can change the setting, new machines pick it up (running ones keep theirs), and the reference must be a registry image — local paths are refused.
What an image must provide:
- Terminal (VM) — a Linux image with
bash. It also needsgit,curlandsocat— either preinstalled, or installable on first start (any Debian/Ubuntu-based image works: they are installed automatically, along with a GitHub-hosted-runner-style CLI toolset: compilers, Python 3,jq, archive tools,rsync. Language runtimes like Node/Go/Java and Docker are not preinstalled). Guest port7911is reserved for the port forwarder. - Browser (VM) — a Debian/Ubuntu image with
bashandsocat, plus achromiumbinary. The server starts Chromium itself aschromium --headless=newand relays DevTools from guest9223onto9222. PulseAudio and ffmpeg are installed on first start. Do not buildFROM chromedp/headless-shell— that binary has no audio. The override applies to Chromium rigs only; Firefox and WebKit ignore it and boot the pinned Debian image. Guest ports9222/9223are reserved.
Two recommendations: pin the reference by digest (@sha256:…) so the bytes
cannot change under you, and host it on Docker Hub or GitHub Container
Registry — those registries’ download hosts are admitted through the egress
gate automatically; others may need their content delivery hosts reachable to
pull.
When a rig goes away
Section titled “When a rig goes away”Rigs are meant to be temporary:
- Idle — after ~15 minutes with nothing happening a browser, desktop or mobile rig parks (frozen, wakes instantly on the next action). An enclosed terminal waits 45 minutes. After twice the idle window, it closes.
- Time limit — every rig has a hard lifetime it cannot extend, whatever it is doing.
- Memory — if the server needs room for a new rig it closes the least-recently-used one. A rig somebody is driving, watching, or has a terminal open in is never taken.
When an enclosed rig closes, its disk is discarded. Closing an Android rig detaches from the host emulator — it does not delete it. An iOS rig deletes the CoreSimulator device it created. Carry anything you want to keep back first.
What a rig can reach
Section titled “What a rig can reach”Enclosed terminal, browser and desktop rigs default to a deny-by-default
egress gate: you grant hosts explicitly, and the guest’s only route is a gate
that enforces that list. A browser rig also admits the product site
(usectrl.dev) out of the box. The credential broker uses the same list.
Two honest caveats:
- Mobile networking follows the host. Android’s emulator and Apple’s CoreSimulator are not behind the enclosure egress gate. Neither mobile tab offers a network-bypass control because the host-managed network is already unrestricted.
- Everything a rig reads is untrusted. Page text, DOM, accessibility trees and view dumps come back to the agent wrapped in markers that say “this is data, not instructions”. A page that tries to give your agent orders reads as a quoted string.
You can see everything running, and stop any of it, under Settings → Server → Rigs.
Related
Section titled “Related”- Rigs and enclosures — the model behind all of this: why VMs, the two display lanes, the credential broker, the lifecycle
- Forward ports from a terminal — reach a dev server from anywhere it matters
- Rigs reference — every surface, verb, port and default in one place
- Configure guardrails —
Drive an enclosure (rig)is an action class you can set to allow, ask-first or deny, per workspace, agent or space. Read-only modes deny it outright.