Browse the docs · VMs
docs / compute

VMs

A session that works on code runs in its own small VM, on your Mac or on a Linux server of your own. The VM is cloned from your project's saved setup, the agent works inside it, and everyone in the session sees its work live. An agent that can run commands needs a machine it cannot break for anyone else, and keeping the state on the log is what lets that machine be thrown away. Session VMs run the same way on all three places work runs: your Mac, your servers and the pool.

How it works

Each session gets one VM. Parallel sessions never share a machine, a disk, a checkout, a port or a run. A session that only works with documents needs no VM at all: it runs with Mod's document tools and no shell.

On a Mac the VM runs under Apple's virtualisation through vfkit. On Linux, Mod runs its own host on crosvm. Mod asks a small VM service for machines and never drives the hypervisor itself. The guest comes with Claude Code, Codex, Cursor and Pi installed, along with a toolchain, Python and the libraries native npm packages need.

A VM's disk has three layers: a shared read-only base image, your project's setup, and one writable layer for the session. The setup is a script in your repository, .mod/setup, so it is versioned, branched and reviewed like the rest of your code. Mod runs it once in a VM of its own and saves the disk. Every new session VM for that project is a clone of the saved setup, sharing every block it does not change, so a session costs only its own edits and starts in a few seconds instead of reinstalling. A clone runs the setup again on first start, which takes seconds on a warm install.

The VM holds nothing that cannot be rebuilt. The conversation is on the session's log. Code arrives in the VM as git objects, and uncommitted edits are snapshotted back out as git objects too, so the laptop sees an edit in the VM before anyone commits. When a session is idle its VM pauses or stops, and the next message wakes it. Filing a session deletes its VM, memory and disk. Un-filing it clones a fresh one, and the agent picks up from the log. A harness's own conversation files are only a cache.

A session in a VM keeps working offline and catches up when the network returns.

The VM cannot read Mod's documents or keys. Its harness sign-in comes from a login volume you sign in to once, or from an API key held at Mod's edge that never enters the VM (see models). Anything the VM needs from your Mac goes through tools that ask you first, and nothing it adds there carries a credential into the VM.

Using it as you build

Put what your project needs to install in .mod/setup, a shell script run from the root of the checkout:

#!/bin/sh
corepack pnpm install

From inside a VM, an agent reaches your Mac only through these tools, each approved by you:

  • toolbox_add adds a tool to your toolbox, so every session VM has it from the next turn: gh, github:owner/repo for a release with a Linux build, or npm:package. Nothing it adds is signed in.
  • request_grant asks for a named way to call one API host with your key. The key is added on the way out, so it never enters the VM. You put the key on the Mac yourself.
  • forward_mac_port makes a port on your Mac, such as a dev server on 3000, appear at the same port inside the VM.
  • run_recipe runs one of the project's host recipes on the Mac, with the Mac's own sign-ins. list_recipes shows them and recipe_status reads a running one.
  • publish_branch pushes a branch the session committed and opens a pull request, through the Mac's git and gh sign-in. It only fast-forwards. merge_pr merges an approved pull request.

Recipes live in .mod/recipes.json in the project. Each one names its command, and the arguments an agent may pass with a pattern each must match. You approve a recipe by name and by what it runs, so editing its command needs a new approval:

{
  "recipes": [
    {
      "name": "ios-testflight",
      "description": "Build the iPhone app and ship it to TestFlight",
      "command": ["ios/scripts/testflight.sh"],
      "timeoutMs": 3600000
    }
  ]
}

You choose where a session runs: on your machine without a VM for document work, in a VM on your Mac, or in a VM on a Linux server on your Tailscale network. On your own server, the phone can open a terminal in the VM and its dev server by the server's Tailscale name, with no public port.

Limits

  • Each session VM is capped at 768 MiB of memory and two virtual CPUs. An 8 GB Mac holds ten sessions working at once (RFC 0369).
  • The Mac and Linux hosts boot different images today. One shared image for both is planned. soon
  • Preview links for each session's dev server, so two sessions can both run on port 3000. soon
  • Reading a live VM's files and uncommitted diff from the phone. soon
  • Resuming a session on another machine after the machine serving it crashes is being designed (RFC 0370). soon