Skip to content

Give an agent a machine to test on

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.

The machine is a VM. Nothing an agent does inside a rig touches your computer, the VM’s disk is thrown away when it closes, and its network reaches only the hosts you allowed.

Rigs run on the machine hosting cc_server — the same machine, not the one you are looking at. There are four surfaces, and only the last two need any 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 headless-Chromium image None — same
Computer (VM) QEMU with a Linux desktop image Install QEMU (brew install qemu), then Import a desktop image under Settings → Server → Enclosures
Phone (VM) Google’s Android emulator Install the Android SDK (below)

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 → Enclosures shows what your server can boot and every machine running right now.

There is no image of ours for Phone (VM), and that is not an omission. The other surfaces boot machines we can define; Android runs on Google’s emulator, whose system images come from their SDK under their licence. Install Android Studio (or the command-line SDK tools), add a virtual device in its Device Manager, and start it — the Enclosures page then reports the mobile surface 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:

Terminal window
export ANDROID_HOME="$HOME/Library/Android/sdk"

Machines live as tabs, next to the chat and the diff — in a space or on a PR page, in the same + menu as Terminal and Browser:

  • Computer (VM) — a Linux desktop
  • Browser (VM) — a headless Chromium
  • Phone (VM) — an Android device

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 that conversation, and so is the agent. Open Browser (VM) in a conversation and an agent calling browser_use in the same conversation drives that machine — you are both looking at one screen rather than at two copies of it.

The picture is sized to your panel: drag it wider and the guest changes resolution to match. It is a genuinely different stream from the one the agent sees — the agent gets small, cheap screenshots, you get a full-resolution live view, and neither is compromised for the other. Switch to another tab and the stream stops so a background machine costs you nothing.

A rig tab does not start a machine by itself, including when the app restores your tabs at launch — three VMs booting because you opened the app would be an expensive surprise.

Press Take control in the tab’s header. The agent keeps watching and can still take screenshots, but it cannot type or click until you hand back. The status tag changes to You have control and everything you do is recorded against your name.

Press Hand back when you are done, or the power button to stop the machine.

Control is exclusive on purpose: two actors typing into one machine produces input neither of them meant.

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.

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 ~/Drops and 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.

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 -rf in 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 push still 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 microvm terminal 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 an enclosed terminal.

A workspace can point the Terminal (VM) and Browser (VM) at its own images — under Settings → Server → Enclosures → Custom images. 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 needs git, curl and socat — either preinstalled, or installable on first start (any Debian/Ubuntu-based image works: they are installed automatically). Guest port 7911 is reserved for the port forwarder.
  • Browser (VM) — an image with the headless-shell layout: the Chromium binary at /headless-shell/headless-shell, plus bash and socat. The easiest way to get that is to build FROM chromedp/headless-shell:stable and add what you need. The server starts the browser itself and expects its DevTools endpoint to be relayed on guest port 9222 — with that layout, it wires this up for you. Guest ports 9222/9223 are 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.

Rigs are meant to be temporary:

  • Idle — after ~15 minutes with nothing happening it parks (frozen, wakes instantly on the next action). After twice that, 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 a rig closes, its disk is discarded. Carry anything you want to keep back first.

By default: nothing outbound. You grant hosts explicitly, and the guest’s only route to the network is a gate that enforces that list. The credential broker uses the same list — the server will not hand a rig a token for a host it cannot reach.

Two honest caveats:

  • The mobile surface is less contained. An Android emulator manages its own networking, so its egress is not fully enforced the way the browser and desktop surfaces are. Full enforcement needs a Linux worker.
  • 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 → Enclosures.