Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# coop — agent and contributor guide

Isolated VM environment for running Codex and Claude Code — Firecracker on
Linux, Lima on macOS.
Isolated VM environment for running Codex, Claude Code, and Grok Build —
Firecracker on Linux, Lima on macOS.

## Agent entrypoint

Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

## Unreleased

### New features

- **Grok Build** — `coop grok` launches Grok Build inside the guest with
`--always-approve`, `--trust`, and `--cwd /workspace`. The golden image
installs `~/.grok/bin/grok` (and an `agent` link) and a `grok-yolo`
shortcut. `[grok]` forwards `XAI_API_KEY`, copies an allowlist from
`config_dir` (`AGENTS.md`, `auth.json`, `config.toml`, `lsp.json`, `rules/`,
`skills/`, `commands/`, `plugins/`, `hooks/`, `agents/`, `workflows/`;
directory symlinks skipped), drops the host `[plugins]` table, merges Model Context Protocol
servers and `permission_mode` into the guest `~/.grok/config.toml`,
records `/workspace` as a trusted folder, and installs configured
marketplaces/plugins on first boot. A copied host `auth.json` is set to
owner-only (`0600`) and signs the guest in; otherwise use `coop grok --
login --device-auth`.
`coop agent update --grok` runs `grok update`. Existing images need
`coop setup --rebuild`; existing VMs also need
`coop restore <vm> --image <image> --reprovision` (or destroy/recreate)
to pick up the new binary.

## v0.6.0

### Upgrading from v0.5.4
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# coop

Isolated VM environments for running Claude Code and Codex.
Isolated VM environments for running Claude Code, Codex, and Grok Build.

> **Pronunciation:** "coop" (/kuːp/) — one syllable, rhymes with "loop", like the thing you keep chickens in. Not "co-op".

coop is a Rust CLI that manages disposable virtual machines where Claude Code and Codex have full tool access: Docker, git, compilers, package managers, all without risk to your host machine. Each VM is isolated, reproducible, and cheap to create and destroy.
coop is a Rust CLI that manages disposable virtual machines where Claude Code, Codex, and Grok Build have full tool access: Docker, git, compilers, package managers, all without risk to your host machine. Each VM is isolated, reproducible, and cheap to create and destroy.

## Setup

Expand Down Expand Up @@ -47,6 +47,8 @@ coop up
coop claude
# or
coop codex
# or
coop grok
```

## Documentation
Expand All @@ -59,6 +61,7 @@ coop codex
- [Workspace sync](docs/workspaces.md)
- [Claude Code integration](docs/claude-integration.md)
- [Codex integration](docs/codex-integration.md)
- [Grok Build integration](docs/grok-integration.md)
- [Editor integration](docs/editor.md)
- [Multi-instance](docs/multi-instance.md)
- [Platform backends](docs/backends.md)
Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ attestation.
## Scope

coop provisions isolated virtual machines — Firecracker microVMs on Linux, Lima
VMs on macOS — to run coding agents such as Claude Code and Codex. **The
VMs on macOS — to run coding agents such as Claude Code, Codex, and Grok Build. **The
security boundary is the VM.** coop's job is to stand that boundary up and hand
work to it without weakening it.

Expand Down Expand Up @@ -67,6 +67,6 @@ Out of scope:
[`docs/platform-notes.md`](docs/platform-notes.md) and
[`docs/trust-model.md`](docs/trust-model.md) for details.
- Vulnerabilities in the software coop runs or orchestrates rather than ships —
the guest agents (Claude Code, Codex), Docker, the guest OS, Firecracker, and
the guest agents (Claude Code, Codex, Grok Build), Docker, the guest OS, Firecracker, and
Lima. Report those to their respective projects.
- Behavior that requires an attacker who already controls the host coop runs on.
18 changes: 18 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@
# model = "gpt-oss:120b"
# auth_token = "sk-..." # Optional; permissive servers ignore it.

# [grok]
# config_dir = "~/.grok" # AGENTS.md, auth.json, config.toml, lsp.json, rules/, skills/, commands/, plugins/, hooks/, agents/, workflows/; skips hidden dirs and *.git (false to disable)
# env_forward = ["CUSTOM_TOKEN"] # Extra env vars to forward to guest
# marketplaces = ["owner/grok-plugins"] # owner/repo, git URL, or local path
# plugins = ["my-skill"] # plugin name (`grok plugin install --trust`)
#
# Secrets: `api_key` accepts the same "cmd:" prefix as `claude.api_key`.
# api_key = "cmd:op read op://Private/xAI/credential"
# api_key = "xai-..."
# Host `~/.grok/auth.json` is copied into the guest (same as Codex). A
# copied session token takes precedence over `XAI_API_KEY`. If there is
# no host file, sign in with `coop grok -- login --device-auth`.

# [grok.mcp_servers.my-server]
# command = "npx"
# args = ["-y", "@example/mcp-server"]
# env = { API_KEY = "MY_HOST_ENV_VAR" }

# [proxy] # Host-side credential-injecting proxy
# # (issue #411). Opt-in: when set, coop runs
# # a `coop-proxy` process on the host and the
Expand Down
4 changes: 2 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Architecture

`coop` is a Rust CLI that orchestrates isolated VM environments for running AI
coding agents (Claude Code, Codex). It manages the full VM lifecycle — setup,
coding agents (Claude Code, Codex, Grok Build). It manages the full VM lifecycle — setup,
start, shell, stop, destroy, status, logs — behind two platform backends:

- **Linux** — Firecracker microVMs on KVM.
Expand Down Expand Up @@ -87,7 +87,7 @@ backends.)

Everything above the trait is **backend-shared**: the entire "shared guest
operations" surface in `backend.rs` (env/secret forwarding, agent bootstrap,
Claude/Codex config injection, git-repo cloning), plus `workspace.rs`,
Claude/Codex/Grok config injection, git-repo cloning), plus `workspace.rs`,
`ssh.rs`, `config.rs`, and the `commands/` handlers. When you touch shared
code, it must hold for **both** backends. Known intentional divergences:

Expand Down
8 changes: 4 additions & 4 deletions docs/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Setup verifies that `limactl --version` is reachable. If it is not, setup fails

1. Generates an ed25519 SSH key pair, stored in the coop data directory.
2. Creates a temporary builder VM from an Ubuntu 24.04 cloud image. The Lima YAML template includes a cloud-init provision script.
3. The provision script installs all packages (Docker, GitHub CLI, Claude Code, Codex, and any profile packages), creates the `ubuntu` user with SSH access, and enables services.
3. The provision script installs all packages (Docker, GitHub CLI, Claude Code, Codex, Grok Build, and any profile packages), creates the `ubuntu` user with SSH access, and enables services.
4. After provisioning completes, cleans cloud-init state so it re-runs on cloned instances.
5. Stops the builder VM and extracts its disk as the golden image.
6. Generates a fast-start Lima template that references the golden image directly. No cloud-init provisioning runs on instance start.
Expand All @@ -43,7 +43,7 @@ The Lima template configures:

### Resize (disk, memory, vCPUs)

Resizing a stopped instance's disk truncates the Lima disk to the new size. Cloud-init's `growpart` module expands the partition and filesystem on next boot. Shrinking is not supported.
Resizing a stopped instance's disk truncates the Lima disk to the new size and updates the instance `lima.yaml` `disk:` field to match, so the next start sees the grown size. Cloud-init's `growpart` module expands the partition and filesystem on next boot. Shrinking is not supported.

Memory and vCPU changes rewrite the `cpus`/`memory` fields in the instance's `lima.yaml`, which Lima re-reads on `limactl start`. The edit is written atomically, then coop starts the instance to validate and apply the new spec — if `limactl` rejects it (e.g. a spec larger than the host), the previous `lima.yaml` is restored. Without `--start` the instance is stopped again after the validating boot. The `lima.yaml` is authoritative: the global `[vm]` `cpus`/`memory` settings only seed *new* instances.

Expand All @@ -68,7 +68,7 @@ The Firecracker backend runs [Firecracker microVMs](https://firecracker-microvm.

1. **Firecracker binary**: Downloaded from the latest GitHub release and stored in the data directory. The jailer binary is extracted alongside it.
2. **Guest kernel**: Fetched from Firecracker's CI S3 bucket. This is a minimal `vmlinux` image matching the Firecracker release version.
3. **Template rootfs**: Built by downloading the Firecracker CI squashfs rootfs (Ubuntu-based), unpacking it, creating an ext4 image at the configured template size, and running an install script inside a chroot. The script installs Docker, GitHub CLI, Claude Code, Codex, and profile packages. It configures the `ubuntu` user with SSH keys and sets up systemd-networkd.
3. **Template rootfs**: Built by downloading the Firecracker CI squashfs rootfs (Ubuntu-based), unpacking it, creating an ext4 image at the configured template size, and running an install script inside a chroot. The script installs Docker, GitHub CLI, Claude Code, Codex, Grok Build, and profile packages. It configures the `ubuntu` user with SSH keys and sets up systemd-networkd.

All three steps are idempotent. If the artifact already exists and is up to date, setup skips it.

Expand Down Expand Up @@ -159,7 +159,7 @@ Both backends support the same CLI commands and guest capabilities:
| `coop status` | Queries `limactl list --json` | Reads PID file, queries guest via SSH |
| `coop logs` | Reads Lima's `serial.log` | Reads Firecracker log file |
| `coop shell` | SSH to localhost on Lima-assigned port | SSH to guest IP on configured port |
| `coop resize` | Disk: truncates Lima disk. Mem/vCPU: edits `lima.yaml`, validated via start | Disk: truncates + resize2fs on rootfs. Mem/vCPU: edits per-instance JSON |
| `coop resize` | Disk: truncates Lima disk and updates `lima.yaml` `disk:`. Mem/vCPU: edits `lima.yaml`, validated via start | Disk: truncates + resize2fs on rootfs. Mem/vCPU: edits per-instance JSON |
| Resource monitoring | SSH query to guest | SSH query to guest |
| Docker in guest | Works (full kernel) | Works (with iptables-legacy workaround) |
| `--mount` host mounts | Live virtiofs (changes visible immediately) | One-time rsync sync (use `push`/`pull` to re-sync) |
Expand Down
68 changes: 51 additions & 17 deletions docs/commands.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Command Reference

coop creates isolated VM environments for running Claude Code and Codex. It runs Firecracker microVMs on Linux and Lima VMs on macOS, selecting the backend automatically based on platform.
coop creates isolated VM environments for running Claude Code, Codex, and Grok Build. It runs Firecracker microVMs on Linux and Lima VMs on macOS, selecting the backend automatically based on platform.

## Global Flags

Expand Down Expand Up @@ -58,7 +58,7 @@ Use `--git-repo <url>` instead of `DIR` to clone a remote repository into
| `--vcpus <N>` | Number of vCPUs when creating a new instance |
| `--mem <MiB>` | Memory in MiB when creating a new instance |
| `--disk <GiB>` | Instance disk size when creating a new instance |
| `--no-agents` | Skip injecting Claude Code and Codex credentials/config into the VM |
| `--no-agents` | Skip injecting Claude Code, Codex, and Grok Build credentials/config into the VM |
| `--no-github` | Use `github = "off"` for this invocation and suppress the PAT setup prompt. See [scope and limitations](configuration.md#github-auth). |
| `--image <name>` | Named image to use when creating a new instance (default: `default`) |
| `--profile <list>` | Build or reuse a profile-derived image when creating a new instance, named from the sorted profiles (for example `node-python`) |
Expand Down Expand Up @@ -245,7 +245,7 @@ instances, pass the instance name.
|------|-------------|
| `NAME` | Stopped instance name (optional only when exactly one stopped instance exists) |
| `--workspace <dir>` | Restart the stopped instance associated with this project path |
| `--no-agents` | Skip injecting Claude Code and Codex credentials/config into the VM |
| `--no-agents` | Skip injecting Claude Code, Codex, and Grok Build credentials/config into the VM |
| `--no-github` | Use `github = "off"` for this invocation and suppress the PAT setup prompt. See [scope and limitations](configuration.md#github-auth). |
| `--forward-port <spec>` | Forward a guest port to the host (`GUEST[:HOST]`, repeatable). Lives for the lifetime of the VM; torn down on `coop stop`. |
| `--no-prompt` | Suppress the interactive prompt to set up a scoped GitHub PAT when one is missing for the resolved repo (see [`coop github setup-pat`](#github)). |
Expand Down Expand Up @@ -359,6 +359,37 @@ coop codex my-project -- --model gpt-5
coop codex my-project -- login --device-auth
```

### `grok`

Launch Grok Build inside the VM. By default coop passes `--always-approve`,
`--trust`, and `--cwd /workspace`. The VM is the isolation boundary, so Grok
Build's own permission prompts add no protection. `--trust` records
`/workspace` as a trusted folder so project `.grok/` hooks and Model Context
Protocol servers load without a first-run question. Use `--ask` to restore
permission prompts for that session (coop passes `--permission-mode default`,
which overrides the guest `ui.permission_mode = "always-approve"`). The
`login` and `logout` subcommands are launched without `--always-approve`.
Host `~/.grok/auth.json` is copied into the guest on boot when `config_dir`
is enabled and set to owner-only (`0600`). If there is no host file, sign
in with `coop grok -- login --device-auth`.

```
coop grok [NAME] [FLAGS] [ARGS...]
```

| Flag | Description |
|------|-------------|
| `NAME` | Instance name (required if multiple instances exist) |
| `--ask` | Prompt for permissions instead of skipping them |
| `ARGS...` | Extra arguments passed through to `grok` |

```
coop grok
coop grok my-project --ask
coop grok my-project -- --model grok-4.6
coop grok my-project -- login --device-auth
```

### `exec`

Run a command in the VM and print its output. No PTY is allocated and stdin is not forwarded; use `shell` for interactive work.
Expand Down Expand Up @@ -468,34 +499,35 @@ $ coop status my-project --json

### `agent update`

Update the coding agents (Claude Code and Codex) installed inside a running VM
to their latest versions, without rebuilding the golden image. Both agents are
installed "latest at build time" during `coop setup`, so they can go stale in
long-running VMs and in new VMs created from an old image. To refresh the image
itself instead, rebuild it with `coop setup --rebuild`.
Update the coding agents (Claude Code, Codex, and Grok Build) installed inside
a running VM to their latest versions, without rebuilding the golden image. The
agents are installed "latest at build time" during `coop setup`, so they can go
stale in long-running VMs and in new VMs created from an old image. To refresh
the image itself instead, rebuild it with `coop setup --rebuild`.

```
coop agent update [NAME] [--claude] [--codex] [--check] [-y]
coop agent update [NAME] [--claude] [--codex] [--grok] [--check] [-y]
```

| Argument / Flag | Description |
|-----------------|-------------|
| `NAME` | Instance name (required if multiple instances exist) |
| `--claude` | Update Claude Code |
| `--codex` | Update Codex |
| `--grok` | Update Grok Build |
| `--check` | Only report installed vs. latest versions — change nothing |
| `-y`, `--yes` | Skip the confirmation prompt |

With no agent flag, both agents are updated; passing both `--claude` and
`--codex` is the same as passing neither. The VM must be running.
With no agent flag, every agent is updated; passing every flag is the same as
passing none. The VM must be running.

Codex has no background updater, so `coop agent update --codex` re-runs coop's
own installer inside the guest as root. It installs the complete upstream
package, verifies its published checksums, and switches the CLI and code-mode
host through the same current-release link.
Claude Code already auto-updates in the background;
`coop agent update --claude` runs `claude update` now, synchronously — a
convenience rather than a fix.
Claude Code and Grok Build already auto-update in the background;
`coop agent update --claude` / `--grok` run `claude update` / `grok update`
now, synchronously — a convenience rather than a fix.

`--check` reports each agent's installed version and, for Codex, the latest
release on GitHub, changing nothing:
Expand All @@ -504,12 +536,14 @@ release on GitHub, changing nothing:
$ coop agent update my-project --check
Claude Code 1.2.3 up to date (auto-updates in background)
Codex 0.4.1 → 0.5.0 update available — run: coop agent update --codex
Grok Build 1.0.24 up to date (auto-updates in background)
```

```
coop agent update # both agents, resolved instance
coop agent update my-project # both agents, instance "my-project"
coop agent update # every agent, resolved instance
coop agent update my-project # every agent, instance "my-project"
coop agent update --codex # Codex only
coop agent update --grok # Grok Build only
coop agent update --check # report versions, change nothing
```

Expand Down Expand Up @@ -806,7 +840,7 @@ coop restore [NAME] [--image <name>] [--reprovision] [-y] [--no-agents] [--no-pr
| `--image <name>` | Image to restore from. Required on its own; with `--reprovision` it defaults to the image the instance already records |
| `--reprovision` | Provision the new disk as a first boot and leave the instance running (see below) |
| `-y`, `--yes` | Skip the `--reprovision` confirmation prompt (required when stdin is not a TTY). Requires `--reprovision` |
| `--no-agents` | Skip injecting Claude Code and Codex credentials/config into the VM. Requires `--reprovision` |
| `--no-agents` | Skip injecting Claude Code, Codex, and Grok Build credentials/config into the VM. Requires `--reprovision` |
| `--no-prompt` | Suppress the interactive prompt to set up a scoped GitHub PAT. Requires `--reprovision` |

Unlike `destroy` + `up --image`, `restore` keeps the same instance identity (name, index, IP) instead of allocating a new one. The disk is reset to the image's size, so restoring an image built before a `coop resize` returns the instance to the smaller size.
Expand Down
Loading