MicroDev
MicroDev gives each coding task its own isolated Linux microVM with the repository checked out in ~/workspace, and can start Codex, Claude Code or Pi in it on a prompt. A task pauses after 15 idle minutes and resumes where it left off, with the workspace and the agents' sign-in, sessions and settings intact.
You can do the whole onboarding for the human. Their part is entering one GitHub device code in their browser. No card is needed. A few things may add one more step for them, each explained below: a Git consent answer if a private repository needs their GitHub credentials, Codex's own login the first time you start a Codex task, and one sign-in per task for Claude Code or Pi.
1. Install the CLI
curl -fsSL https://microdev.dev/install.sh -o install-microdev.sh
sh install-microdev.sh
export PATH="$HOME/.local/bin:$PATH"
The installer verifies a SHA-256 checksum fetched over HTTPS, installs microdev into ~/.local/bin, needs no sudo and does not edit shell startup files. It runs on macOS and Linux (x86_64 and arm64); on Windows use WSL2. OpenSSH must be installed.
~/.local/bin is often not on PATH, and an export in one of your shell commands does not carry over to the next. Call the CLI as ~/.local/bin/microdev, or start each command with export PATH="$HOME/.local/bin:$PATH";. Check with ~/.local/bin/microdev --version.
2. Log in: the one human step
Run login in the background so you can read its first line while it waits:
microdev login --no-browser --json
It prints one line on stderr, Open VERIFICATION_URL and enter USER_CODE, then polls until the human finishes. Relay that URL and code verbatim:
Open VERIFICATION_URL in your browser, enter the code USER_CODE and approve MicroDev for your GitHub account. No card is needed.
Keep the command running. When the human approves, it prints the account as JSON on stdout and stores the session in ~/.config/microdev (owner-only). The personal organization is selected automatically. If the code expires the command fails with Login expired; run it again and give the human the new code.
The trial needs no card: 5 agent-hours (300 minutes) of running time over 14 days, 2 tasks running at once, no limit per run, and up to 10 new tasks an hour. Resuming a paused task doesn't count against that (resumes have their own limit of 30 an hour). Paused time is free, so pause tasks that are waiting on the human. Check what is left:
microdev trial --json
3. Start a task
The public sample repository:
microdev demo --json
The human's own repository. A public one needs nothing more. A private one clones with this computer's Git credentials (gh auth login or a Git credential helper), which the CLI hands to the task on its first start; see Git consent below.
microdev start OWNER/REPO --json
microdev start OWNER/REPO --ref BRANCH_OR_TAG --json
--json never opens a shell. The CLI prints Request ID: … and Environment: … on stderr before it waits for setup, then prints the ready environment as JSON on stdout. Record the environment ID.
Start an agent on a prompt
microdev start OWNER/REPO --agent codex --prompt "Fix the flaky retry test and push a branch" --json
microdev start OWNER/REPO --agent claude --prompt-file task.md --json
--agent codex|claude|pi|custom starts that agent in a tmux session in the task's ~/workspace on the prompt (--prompt TEXT, or --prompt-file PATH, at most 32 KiB). With --json the command returns as soon as the agent is running, and the JSON has an extra field: "agent": {"name": "codex", "terminal": "UUID"}. To fan work out, run one start per task, each with its own prompt and request ID.
- Codex runs on the human's Codex account, connected once and kept encrypted by MicroDev, so Codex tasks work without them. The first
--agent codex start (or microdev codex connect --no-browser up front) runs Codex's own login on this computer, which needs Codex installed here; relay what it prints to the human. Once Git is connected, the task can push branches with the human's credential. Nothing pushes on its own: the task's agent pushes when its prompt asks it to. - Claude Code runs on the human's Claude plan once they connect it for every task: they run
claude setup-token and pipe what it prints to microdev agent connect claude - (an Anthropic API key works too). Never ask for the token yourself. Pi reuses a login the human makes once in a task (microdev agent reuse PROVIDER ID). microdev agent status shows what is connected. Without a connection, ask the human to run microdev attach ID in their own terminal (the same MICRODEV_CONFIG_DIR as you), follow the agent's login, then detach with Ctrl-b d; that login is kept across pause and resume. - Custom (
--agent custom) runs the human's own harness: the executable their dotfiles init.sh registers at ~/.config/microdev/custom-agent, called as custom-agent start PROMPT, open or resume in ~/workspace.
Claude Code runs with permission prompts off, like Codex. Pi never asks for tool approval. The task's VM is the boundary.
microdev attach ID reopens the agent this computer started in the task: it resumes a paused task and continues that agent's most recent conversation, with no picker (the human must use the same MICRODEV_CONFIG_DIR as you). If Claude has no conversation yet, it opens fresh. For any other task, --agent codex|claude|pi|custom opens that agent fresh. It opens a terminal, so it is for the human, not for you.
For safe retries, choose the request ID yourself and reuse it. Repeating the command with the same UUID after a timeout or crash creates no second task:
REQUEST_ID=$(uuidgen)
microdev start OWNER/REPO --agent codex --prompt "…" --json --request-id "$REQUEST_ID"
Rerunning the same start command (same --request-id) is always safe. It resumes a paused task, finishes setup, and starts the agent once. If the agent already got the prompt, it reopens the agent instead, so the prompt is never sent twice. If setup was interrupted, inspect the existing task with microdev status ID --json rather than creating another. When setup failed, guest.setup_failure says where: {"stage": "repository"|"dotfiles"|"dotfiles_init"|"task_configuration", "outcome": "failed"|"timed_out"}. Fix the cause (for example your dotfiles), then microdev resume ID --json retries setup, even on a running task and even when the repository clones without Git credentials. If a private project still needs Git consent, resume stops first and names microdev git connect ID.
Git consent
When this computer has GitHub credentials (gh auth login or a Git credential helper), the first start of a repository asks, on a terminal, whether to sync those credentials into the task. The demo is never asked about. Without a terminal:
- A public repository starts anyway and clones anonymously; stderr says
Continuing without Git credentials: OWNER/REPO is public and clones anonymously, but agents cannot push. To let them push: microdev git connect ID. The agent starts as usual and resume still retries a failed setup. The task's agent cannot push until the human connects Git. - A private repository (or one whose visibility GitHub would not confirm) stops with
Connect Git for OWNER/REPO interactively: microdev git connect ID. The task already exists; do not create another. - A dotfiles repository is a project of its own: private dotfiles stop the start with
Connect Git for OWNER/DOTFILES interactively: microdev git connect ID, even when the repository is public.
To connect Git, ask the human to run microdev git connect ID in their own terminal and answer y to each question. It asks about every project of the task that isn't connected to their local GitHub account: the repository and its dotfiles repository. This is a second human step; never answer the consent prompt for them. On a paused task, git connect saves consent without resuming it; on a running task it hands the credentials over right away. After a stopped start, run the same start command again with the same --request-id (the one printed as Request ID: … if you did not choose it). It finds the same task, waits until it is ready and starts the agent, if you asked for one, without sending the prompt twice. A task started without --agent can use microdev resume ID --json instead.
4. Work in the task
microdev ssh ID opens an interactive shell and needs a TTY. It resumes a paused task first. There is no microdev ssh ID -- COMMAND form. To run commands non-interactively, read the connection from status and call OpenSSH yourself:
microdev status ID --json # .ssh.host, .ssh.port, .ssh.user
ssh -o BatchMode=yes \
-o StrictHostKeyChecking=yes \
-o HostKeyAlias=microdev-ID \
-o UserKnownHostsFile=$HOME/.config/microdev/known_hosts/ID \
-i $HOME/.config/microdev/id_ed25519 \
-p PORT USER@HOST 'cd ~/workspace && cargo test'
The CLI writes the verified host keys to ~/.config/microdev/known_hosts/ID whenever start, resume, ssh or attach reaches the running task, with or without --agent and with or without Git credentials. A start that stopped for Git consent has not reached the task yet: after microdev git connect ID, rerun the start (or microdev resume ID --json) first. The task authorizes ~/.config/microdev/id_ed25519 unless the start named another key with --key. If MICRODEV_CONFIG_DIR is set, use that directory instead of ~/.config/microdev.
The image has Ubuntu 24.04, Git, Python, Node, compilers, tmux, ripgrep and the Codex, Claude Code and Pi CLIs. Agents inside the task run on the human's own model account; model usage is not part of MicroDev.
5. Lifecycle
microdev list --json # every task and its phase
microdev status ID --json # one task: phase, guest status, setup_failure, errors, SSH
microdev pause ID --json # waits until the workspace is saved
microdev resume ID --json # resume without opening a shell
microdev trial --json # remaining trial allowance
microdev delete ID # permanent; deletes the saved work too
Leaving an SSH session does not pause compute. Pause explicitly when a task is done for now. Ask the human before delete.
What survives pause
- Kept: all of
~/workspace, byte for byte: every branch, stash and commit, and staged, uncommitted, untracked and ignored files. - Kept: Codex, Claude Code and Pi sign-in, session history and settings files.
- Rebuilt: a fresh copy of the pinned system image, plus the human's dotfiles, cloned fresh with
init.sh run on every resume. - Lost: installed packages, other home-directory files, shell history, environment variables, agent logs, general caches and other agent files outside the kept list.
- Lost: running processes, tmux sessions, open connections, unsaved editor buffers.
- Auto-stop: a task pauses after 15 idle minutes, even with an agent open and a terminal or the Mac app attached: every minute under 2% of one vCPU and 32 bytes/s of data (packet headers and SSH sessions into the task excluded), with no typing in the terminal and no transcript change. Builds, tests, downloads, streaming model calls and typing are busy. A tool that sleeps with no CPU and no network for the whole window is paused too (the workspace is kept).
Teams and billing
microdev org create "Team name" # prints the organization as JSON
microdev org use ORGANIZATION_UUID # select it for later commands
microdev billing plans # plans and prices
microdev billing subscribe PLAN SEATS # PLAN: microdev-pro-v2 (1–500 seats), microdev-team-v1 (2–250 seats), microdev-business-v1 (5–125 seats), microdev-payg-v1 (1 seat)
microdev billing setup # returns a payment link for the human
microdev billing status
Payment happens only in the browser, through the link billing setup returns. Hand that link to the human; never enter payment details yourself. microdev help advanced and microdev billing --help list the remaining team commands.
Do not
- Do not keep durable state outside
~/workspace; everything else is rebuilt on resume. - Do not assume a process, tmux session or server survives a pause.
- Do not create a second task to retry a failed start; reuse
--request-id or inspect with status. - Do not answer Git consent or payment prompts on the human's behalf.
- Do not put model API keys in the repository. Model credentials are the human's own.