isx
Give your AI coding agents their own machines, not your credentials. isx.run is the website, with the full documentation.
An AI agent in your terminal can read your API keys, your GitHub token, your ~/.ssh and every repo you have checked out.
Your IDE and build tools execute anything the agent writes.
isx onboards agents the way you'd onboard a new teammate:
- A real machine of their own.
Each agent gets a full Linux workstation: its own filesystem, init system, networking and process tree.
It can
dnf install, run Docker Compose, usestraceand nested containers, because it is a real system, not an app container. One flag gives the agent a hardware-isolated KVM virtual machine instead, when you want the strongest isolation. - Zero credential exposure.
API keys and tokens never enter the machine in any form.
A proxy on your host adds them to the agent's requests as the requests leave the machine, so
claude,pi,gh,gitandcurlwork unmodified inside it. See Credential Isolation. - Instant branches. Branch a prepared template like you'd branch a repo: an instant copy-on-write clone. Use it, throw it away, branch again from a clean state.
- Full autonomy, no supervision. Each agent runs without permission prompts, and commits under the identity you assign it. It has no credentials to leak, and what it runs, installs or deletes is in its own machine, which you can throw away.
Each agent works in its own machine and asks for no approval, so you can run many side by side and build automations that run with nobody watching. isx runs on Linux and macOS, on your hardware, and your repositories and credentials stay there.
Agents are the main use, not the only one. The same disposable machines are ideal for triaging untrusted patches, reproducing bug reports and testing on a clean system.
Built with Quarkus and Tamboui, powered by Incus system containers. Written in Java, compiled to native binaries with GraalVM. isx was formerly known as incus-spawn.
Quick Start
Requires Linux, or macOS 15 (Sequoia) or later (Installation); isx init sets up the rest, or your coding agent can do it all for you.
brew install Sanne/tap/incus-spawn
sudo dnf install -y 'dnf5-command(copr)' # on RHEL 9: 'dnf-command(copr)'
sudo dnf copr enable sanne/incus-spawn
sudo dnf install incus-spawn
curl -fsSL https://sanne.github.io/isx-apt-releases/public.gpg | sudo gpg --yes --dearmor -o /usr/share/keyrings/incus-spawn.gpg
echo "deb [signed-by=/usr/share/keyrings/incus-spawn.gpg] https://sanne.github.io/isx-apt-releases stable main" | sudo tee /etc/apt/sources.list.d/incus-spawn.list
sudo apt update && sudo apt install incus-spawn
curl -fsSL https://isx.run | sh
jbang app install isx@Sanne/incus-spawn
jbang app install isx-proxy@Sanne/incus-spawn
The package is called incus-spawn, and it installs the isx command.
# One-time host setup (Incus, firewall, auth)
isx init
On a fresh Linux host, isx init adds you to the incus-admin group and finishes its run in that group.
Other terminals get the group at your next login, or in each one with newgrp incus-admin.
A template is a machine image you build once: an OS, tools and repos.
The template below adds Claude Code to the built-in tpl-minimal template.
Save it as ~/.config/incus-spawn/images/tpl-agent.yaml, or run isx templates new tpl-agent and paste it over the skeleton that opens in your editor:
name: tpl-agent
description: Claude Code on the base OS
parent: tpl-minimal
tools:
- claude
default-action: claude
# Build it (builds missing parent images first)
isx build tpl-agent
# Branch it: an instant copy-on-write machine of its own, with Claude Code open inside
isx branch my-first --from tpl-agent
# The interactive TUI: your templates and branches
isx
Claude Code signs in with the Claude credential you set up in isx init; isx account add claude adds one later.
Claude Code never sees the credential: the proxy adds the credential to each request after the request leaves the instance.
If you did not install the services isx init offers, start them before building: isx proxy start on Linux, isx vm start && isx proxy start on macOS.
For another agent, replace claude with codex, pi, opencode, gemini-cli, bob or copilot; see Agents.
Pi and opencode use your Claude credential too, unless their provider names another API (Pi, opencode).
To give the agent a project, add repos: (Declarative Repos).
Its commits come back to your host with git fetch (Git Remotes).
Credential Isolation
The agent must be free to act, so its instance cannot hold anything worth stealing.
API keys and tokens never enter an instance in any form.
Instead, a proxy on your host (isx proxy) adds the real credentials to the agent's requests as the requests leave the instance, so curl, git, gh, claude and pi work unmodified inside it.
How it works:
- The instance's DNS resolves the intercepted domains to the proxy, and the instance trusts the proxy's certificate authority (CA), so the traffic reaches the proxy without any setting in the tool.
- The proxy replaces the placeholder credential that the tool sent with the real credential and forwards the request upstream over TLS.
- Inside the instance, a tool sees only a placeholder such as
sk-ant-placeholder. The placeholder satisfies the tool's local checks, and no service accepts it as a credential. - Nothing inside the instance yields a real credential: no API, no endpoint, no variable, no file.
What the proxy can sign in as:
| Credential | Who uses it in the instance | What you give isx init |
|---|---|---|
| Anthropic API key | Claude Code (claude), Pi (pi), opencode (opencode) |
the key |
| Claude Pro or Max | Claude Code (claude), Pi (pi), opencode (opencode) |
the token from claude setup-token; the proxy adds the OAuth bearer token |
| Vertex AI | Claude Code (claude), Pi (pi), opencode (opencode) |
your project and region; the proxy translates each request and signs it with the host's GCP credentials, which never enter the instance |
| OpenAI API key | Codex CLI (codex), Pi (pi), opencode (opencode) |
the key |
| Gemini API key | Gemini CLI (gemini-cli), Pi (pi), opencode (opencode) |
the key |
| IBM API key | Bob Shell (bob) |
the key |
| GitHub token | gh, git, GitHub Copilot CLI (copilot) |
the token |
| Atlassian API token | the atlassian-mcp tool, for Jira and Confluence |
your account email and the token |
| A service of your own | whatever your tool YAML installs | its credential, declared in the YAML's proxy: block (Proxy Credentials) |
A credential can have several accounts, for example a personal and a client Claude subscription, or two GitHub identities.
Each template or instance chooses which account it uses: see Credential accounts.
Git must use HTTPS URLs, not SSH; gh defaults to HTTPS.
The proxy must be running for every instance that has a network.
isx init installs it as a service, systemd on Linux and launchd on macOS, or you run isx proxy start in a terminal of its own.
isx checks that the proxy is reachable and compatible before a build, a branch or a shell.
The proxy also caches container image layers and build artifacts on the host, so the same dependency is never downloaded twice (Caching).
Credentials reach the login environment
Each start replaces the placeholders with proof tokens unique to the instance and the credential, such as gho_isx_… in GH_TOKEN, exported by the login profile.
The proxy adds an instance's credentials only to requests carrying its proof token.
The tokens stay the same across stops, starts and reboots, so a nested service you configured with one keeps working.
Only what runs in the instance's login environment receives the proof tokens, so a nested container gets none unless you pass one in.
Plain Git is the exception: for its HTTPS requests to github.com, a credential helper isx installs presents the GitHub proof token even outside a login shell.
Credential proofs says how to share or revoke proof tokens, and what to do after an upgrade.
Git Configuration
With a GitHub token configured, every instance gets a ready-made .gitconfig: your GitHub name and email, the private @users.noreply.github.com address when there is one, and sensible defaults.
- A dedicated agent account (recommended): give
isx initthe token of a separate GitHub account, so the agent's commits and PRs are clearly its own. - Your personal account: use your own token.
To use your aliases and settings in the instance, mount
~/.gitconfigas a host resource; the mounted file takes precedence over the generated config.
Commit signing is not supported inside an instance yet (#271). To sign, fetch the changes to your host through git remotes and rebase there.
Branching
A branch is an instance made from a template, the way git branch makes a branch from a commit: an instant copy-on-write clone.
It has its own filesystem, and nothing it changes reaches the template or another branch.
The storage backend (btrfs, zfs or lvm) shares the unchanged data, so a branch uses disk space only for what it changes.
isx init creates a btrfs storage pool if there is none.
tpl-agent (stopped template, ~2GB)
├── fix-nasty-bug (running, uses ~50MB extra)
├── review-pr-423 (running, uses ~30MB extra)
└── experiment (stopped, uses ~10MB extra)
Install packages, break things, and destroy the branch when done.
Sudo works without a password, and the shell sets the terminal title to isx:<name>, so you always know which instance you are in.
What a branch can have, each on request:
- GUI and audio passthrough: Wayland and PipeWire with GPU acceleration, Linux only.
- Restricted networking: see Network Modes.
- An inbox: a read-only mount of a host directory, to give files to the instance.
- Other credential accounts than its template's.
- Resource limits for CPU, memory and disk, detected from the host and overridable.
The TUI (isx with no arguments) is a Midnight Commander-style view of your templates and branches.
It has dialogs for branching, renaming and building, F3 for an instance's details and F9 for tool actions.
Without function keys, press the digit instead: 1 to 0 stand for F1 to F10, and Alt with a digit stands for Shift with that function key. On macOS, Alt is Option once the terminal sends Option as Meta.
A storage gauge and per-row disk usage show what is filling the pool.
Sizes are approximate (~): the base template carries the shared base image, and each branch only its own data.
Press C to reclaim space, or on macOS grow the disk with isx vm resize <size>.
Templates pre-install your baseline tools and repos. Integrations are installed through the same tool system: VS Code Remote, JetBrains Gateway and agent skills.
Network Modes
Each branch runs in one of three network modes:
| Mode | Flag | What it means |
|---|---|---|
| Full internet | (default) | Unrestricted access through NAT, signed in through the proxy |
| Proxy only | --proxy-only |
Outbound traffic reaches only the proxy; the host enforces the restriction, so root in the instance cannot remove it |
| Airgapped | --airgap |
No network device at all, and no pci or usb passthrough |
Git Remotes
An instance is isolated, but the work done in it has to reach your host.
isx speaks git's remote helper protocol, so plain git fetch, git push and git pull work between a host repo and a repo in an instance:
# Inside the instance, the agent makes some commits...
# Back on the host:
git fetch fix-auth
git diff main..fix-auth/main # review exactly what it did
git cherry-pick fix-auth/main # take what you like
This is the intended review workflow: the code you review is the code you take, whatever the agent does afterwards. You act on fetched commits, which git identifies by a hash of their content, never on a live directory the agent can still change. The FAQ says why there is deliberately no read-write project mount.
isx:// URLs
The remote uses the isx:// URL scheme, where ~ expands to /home/agentuser:
git remote add fix-auth isx://fix-auth/~/quarkus
git fetch fix-auth
git diff main..fix-auth/main
An absolute path inside the instance follows a double slash, as in isx://fix-auth//home/agentuser/quarkus.
The instance must be running for git operations to work.
Automatic remotes
With host-paths in ~/.config/incus-spawn/config.yaml, isx manages the remotes for you:
# Base directories where your repos live on the host
# Subdirectories are scanned recursively (up to 4 levels deep),
# so ~/projects finds ~/projects/java/my-repo, ~/projects/go/another-repo, etc.
# If a repo name appears in multiple locations, the checkout whose remote matches is used;
# add an explicit repo-paths entry when more than one matches
host-paths:
- ~/projects
- ~/workspace
# Explicit overrides for repos in non-standard locations or to resolve ambiguity
repo-paths:
quarkus: ~/work/quarkus
hibernate: /opt/hibernate
isx branch adds a remote named after the instance to each host repo that matches one in the instance.
SSH and HTTPS URLs of the same repo count as the same.
isx destroy removes the remote again.
Templates
A template is a machine image you prepare once, in YAML: an OS, packages, tools and repositories. Templates inherit from one another, and building one builds any missing parent first:
name: tpl-maven
description: JDK + Maven
parent: tpl-minimal
packages:
- java-25-openjdk-devel
tools:
- maven-3
tpl-minimal, on a Fedora base, is built in.
Your own templates go in ~/.config/incus-spawn/images/, or a project's .incus-spawn/images/.
isx templates new <name> opens a skeleton listing every field, and isx templates edit <name> validates what you save.
isx build tpl-maven # builds missing parents first
isx build tpl-maven --with-parents # rebuilds it and its parents from scratch
isx build --out-of-sync # rebuilds what changed since it was built
Templates lists every field: repositories cloned at build time, the working directory and default action, host resources, base image updates and VM templates.
Agents
An agent is a tool in a template's tools: list, set up for unattended work.
It signs in through the proxy with an account you configured in isx init, whose menu lists each credential under the tool that owns it, so Pi, opencode and Copilot have no entry of their own:
| Agent | Tool | Signs in with |
|---|---|---|
| Claude Code | claude |
an Anthropic API key, Claude Pro/Max, or Vertex AI |
| Codex CLI | codex |
an OpenAI API key |
| Pi | pi |
Claude's credentials, the OpenAI key or the Gemini key |
| opencode | opencode |
Claude's credentials, the OpenAI key or the Gemini key |
| Gemini CLI | gemini-cli |
a Gemini API key |
| Bob Shell | bob |
an IBM API key |
| GitHub Copilot CLI | copilot |
the GitHub token gh uses |
Agents has each agent's parameters (model, reasoning effort, provider), agent skills, and the notes isx writes for an agent about its instance.
Tools
Templates are made of tools.
gh, herdr, maven-3, orca, podman, sshd, the VS Code and IntelliJ remote backends, tmux and zmx ship with isx; isx tools list -v lists them all.
Your own tools are YAML files in ~/.config/incus-spawn/tools/ or a project's .incus-spawn/tools/: packages, commands, environment variables, TUI actions, and credentials the proxy adds for a service of yours.
See Tools.
Delegating over MCP
An agent on your host, such as Claude Code, can control isx through isx mcp: branch instances from templates you approved, run builds and tests in them, and give whole tasks to Claude Code, Codex, Pi or opencode inside an instance.
isx mcp is off by default, because it gives an agent that control; isx init offers to set it up.
Delegating from an agent on your host says what such an agent can and cannot do, and how to run the coordinating agent in an instance of its own, branched with isx branch --mcp-client.
Driving isx mcp from a program is the reference for a program of your own that controls isx mcp.
Configuration
isx init writes your settings and credentials to ~/.config/incus-spawn/config.yaml; your own templates and tools go beside it, in images/ and tools/.
A credential can have several accounts, for example a personal and a client Claude subscription, and each template or instance chooses which account it uses.
Configuration covers the files, credential accounts and the download caches.
Installation
isx runs on Linux, and on macOS 15 (Sequoia) or later. Windows is not supported.
Linux: isx init installs Incus with your package manager.
It also installs and enables firewalld when the host has neither firewalld nor UFW, since the proxy's redirect needs a firewall.
macOS: isx init uses vfkit to provision a lightweight Linux VM, and the VM starts when needed.
isx vm start|stop|restart|status|resize|reset|console|check-version manages the VM, and its reference says what to do when the VM stops answering.
Apple Silicon is supported, and Intel Macs are not: v0.4.2 and v0.5.0-dev.2 were the last releases built for them.
On macOS isx runs containers only. These are Linux-only:
- VM instances (
type: vm,--type vm) and KVM passthrough (--kvm,type: kvm). isx refuses them on macOS before it creates anything, and branches atype: kvmtemplate without KVM. - GUI and audio passthrough (Wayland and PipeWire).
- The
overlaymode for host resources: usereadonlyorcopyinstead.
Behind TLS inspection (a corporate proxy that re-signs HTTPS), isx trusts the host's CA store for its own downloads and for the proxy's upstream connections.
- On Linux that is the system bundle:
/etc/pki/ca-trust,/etc/ssl/certs. - On macOS it is
/etc/ssl/cert.pemplus every certificate an administrator or MDM trusts for SSL in the System keychain, which is where inspection agents install their CA. A certificate an administrator denied there, or you marked "Never Trust" in your login keychain, is not trusted from any source, and neither is a chain holding it or a cross-signed copy. - A CA trusted only in your login keychain is not used, because isx leaves the choice of which CAs to trust to an administrator. Trust it system-wide, then restart the proxy:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca.pem
isx proxy restart
isx doctor lists the Keychain CAs isx trusts.
| Installed with (Quick Start) | Update with |
|---|---|
| Homebrew | brew upgrade incus-spawn |
| DNF: Fedora 43 and later, RHEL 9 and 10, CentOS Stream 9, AlmaLinux 9, Rocky Linux 9 (via EPEL) | sudo dnf upgrade |
| APT | sudo apt upgrade |
curl -fsSL https://isx.run | sh |
the same command again |
| JBang | both jbang app install commands again |
The curl script installs the isx and isx-proxy native binaries to ~/.local/bin (set INSTALL_DIR to change it), with no JVM needed, and only once every download matches the release's SHA256SUMS.
JBang installs need both commands, and jbang must stay on your PATH.
A source build (./install.sh in a checkout) does not change a Homebrew install, since other accounts may use it, so whichever isx comes first on your PATH is the one you and your proxy service run.
To uninstall, run uninstall.sh:
- It keeps your configuration in
~/.config/incus-spawn/and the caches in~/.cache/incus-spawn/unless you pass--purge. - On macOS it keeps the VM's data disk, with every instance and template, unless you pass
--delete-instances. --binaries-onlyremoves only the binaries, to switch install channel, for example from a source build to Homebrew.
Shell completions are available for bash, zsh, fish and PowerShell via isx completion <shell>.
CLI
| Command | Description |
|---|---|
isx |
Launch the interactive TUI |
isx init |
One-time host setup |
isx build |
Build or rebuild template images |
isx branch |
Create a CoW clone from a template or instance |
isx shell |
Open a shell in an instance |
isx start |
Start an instance without opening a shell |
isx stop |
Stop a running instance |
isx run |
Run an action on an instance |
isx destroy |
Destroy an instance |
isx list |
List instances, for people or for scripts |
isx update-base |
Check for and install base image updates |
isx update-all |
Update packages and repos in all templates |
isx templates |
Manage template definitions |
isx tools |
List and inspect available tool definitions |
isx account |
Add credentials, and show or change the accounts an instance uses |
isx project |
Manage project templates |
isx proxy |
Manage the MITM authentication proxy |
isx doctor |
Run health checks and offer to fix problems |
isx clean |
Remove cached data, state, or configuration |
isx reset |
Reset to a clean slate |
isx vm |
Manage the VM appliance (macOS only) |
isx ask |
AI-powered help |
isx mcp |
Serve isx to a local agent over MCP |
isx completion |
Print shell completion script |
Use isx <command> --help for detailed options on any command.
Scripts read --format=plain or --format=json, which stay stable: see Scripting isx.
Further reading
- FAQ: URL clicks in Codex, the clipboard, and why there is no read-write project mount.
- Compared with other sandboxes: why an isx instance is a whole machine, and how nono, Claude Code's sandbox runtime, NVIDIA OpenShell, dev containers, Docker Sandboxes and a VM you manage compare with it.
- What differs from a stock Fedora install: the few deliberate deviations, mostly so that every runtime trusts the proxy's CA.
- Vision: where isx is going, from a container manager to mission control for parallel coding agents.
- DESIGN.md: the architecture and why each choice was made.