{"slug": "mensfeld-code-on-incus-give-each-ai-agent-its-own-isolated-machine", "title": "Mensfeld/code-on-incus: Give each AI agent its own isolated machine", "summary": "Developer Maciej Mensfeld released Code on Incus (COI), an open-source tool that gives each AI coding agent its own isolated system container with root access, Docker, and active security monitoring. The tool automatically detects and blocks suspicious activities like reverse shells and credential exfiltration, and supports tools including Claude Code, opencode, and pi.dev.", "body_md": "**Isolated machines for AI coding agents - with active defense.**\n\nCOI gives each AI agent its own machine - a full system container with root access, systemd, Docker, and the ability to install anything. Agents work like they would on a real server: run services, manage packages, use cron - without touching your actual system. Files stay correctly owned, no permission hacks needed.\n\nYour credentials stay on the host. SSH keys, environment variables, and Git tokens are never exposed to AI tools unless you explicitly mount them. If something goes wrong, COI catches it - reverse shells, credential scanning, data exfiltration - and pauses or kills the container automatically. No manual intervention needed.\n\nBuilt by developers, for developers who run AI agents and want to know what those agents are doing. Not a product, not a startup - a tool that does the job.\n\n- You run AI coding agents and want them to have full machine access - root, Docker, package managers, services - without risking your host\n- You want to know when an agent does something suspicious, not find out after the fact\n- You run multiple agents in parallel and need them isolated from each other\n- You want persistent dev environments that survive restarts and reboots, not throwaway containers that lose your setup every time\n- You care about your credentials not ending up inside an agent-controlled environment\n\n[\n](https://www.youtube.com/watch?v=t78-JUnTK5Q)\n\n*Watch the BetterStack video about Code on Incus*\n\n[Who this is for](#who-this-is-for)[Supported AI Coding Tools](#supported-ai-coding-tools)[Supported Tools (detailed)](https://github.com/mensfeld/code-on-incus/wiki/Supported-Tools)[Features](#features)[Quick Start](#quick-start)[Why Incus Instead of Docker or Docker Sandboxes?](#why-incus-instead-of-docker-or-docker-sandboxes)[Installation](#installation)[macOS Support](#macos-support)[Usage](#usage)[Run Scripts and Commands in the Sandbox](#run-scripts-and-commands-in-the-sandbox)[Session Resume](#session-resume)[Persistent Mode](#persistent-mode)[Configuration](#configuration)[Profiles](#profiles)[Resource and Time Limits](https://github.com/mensfeld/code-on-incus/wiki/Resource-and-Time-Limits)[Container Lifecycle & Session Persistence](https://github.com/mensfeld/code-on-incus/wiki/Container-Lifecycle-and-Sessions)[Network Isolation](#network-isolation)[Security Monitoring](#security-monitoring)[Security Best Practices](https://github.com/mensfeld/code-on-incus/wiki/Security-Best-Practices)[Snapshot Management](https://github.com/mensfeld/code-on-incus/wiki/Snapshot-Management)[System Health Check](https://github.com/mensfeld/code-on-incus/wiki/System-Health-Check)[Troubleshooting](https://github.com/mensfeld/code-on-incus/wiki/Troubleshooting)[FAQ](https://github.com/mensfeld/code-on-incus/wiki/FAQ)\n\nCurrently supported:\n\n**Claude Code**(default) - Anthropic's official CLI tool** opencode**- Open-source AI coding agent ([https://opencode.ai](https://opencode.ai))** pi**- AI coding assistant ([https://pi.dev](https://pi.dev))\n\nComing soon:\n\n- Aider - AI pair programming in your terminal\n- Cursor - AI-first code editor\n- And more...\n\n**Tool selection** is config/profile-driven:\n\n```\n# ~/.coi/config.toml or ./.coi/config.toml\n[tool]\nname = \"opencode\"            # or \"claude\" (default), \"pi\"\ncoi shell                    # Uses the configured tool (Claude Code by default)\ncoi shell --profile opencode # Or switch via a profile with [tool] name = \"opencode\"\n```\n\n**Permission mode** - Control whether AI tools run autonomously or ask before each action:\n\n```\n# ~/.coi/config.toml or .coi/config.toml\n[tool]\nname = \"claude\"              # Default AI tool\npermission_mode = \"bypass\"   # \"bypass\" (default) or \"interactive\"\n```\n\nSee the [Supported Tools wiki page](https://github.com/mensfeld/code-on-incus/wiki/Supported-Tools) for detailed configuration, API key setup, and adding new tools.\n\n**Core Capabilities**\n\n- Multi-slot support - Run parallel AI coding sessions for the same workspace with full isolation\n- Session resume - Resume conversations with full history and credentials restored (workspace-scoped)\n- Persistent containers - Keep containers alive between sessions (installed tools preserved)\n- Workspace isolation - Each session mounts your project directory\n- Slot isolation - Each parallel slot has its own home directory (files don't leak between slots)\n**Workspace files persist even in ephemeral mode**- Only the container is deleted, your work is always saved- Container snapshots - Create checkpoints, rollback changes, and branch experiments with full state preservation\n\n**Host Integration**\n\n- SSH agent forwarding - Use git-over-SSH inside containers without copying private keys (\n`[ssh] forward_agent = true`\n\n) - Host port publishing - Publish container TCP ports on the host (\n`[ports] pool`\n\nfor identity-mapped agent-usable ports,`[[ports.map]]`\n\nfor fixed services): agent-started dev servers become reachable at`localhost:<port>`\n\n, with per-slot deterministic allocation, a pre-launch conflict check, and`coi trust`\n\ngating for untrusted project configs - Host socket forwarding - Forward arbitrary host Unix sockets into the container (\n`[[sockets]]`\n\n) so the host endpoint never enters the container — the building block for credential brokers (mint short-lived tokens on the host, fetch them on demand inside). Untrusted project-config sockets are gated behind`coi trust`\n\n- Credential catalog - Copy third-party provider credentials into the container via\n`[[credentials]]`\n\nentries (config or profile): reference a named catalog bundle (`bundle = \"ollama\"`\n\n) or declare an ad-hoc host/container file pair for anything not yet cataloged.`claude`\n\n/`opencode`\n\n/`pi`\n\n's own credential files come from the same built-in catalog. Ad-hoc entries from an untrusted project`.coi/config.toml`\n\nare gated behind`coi trust`\n\n; catalog references carry the same trust level the built-in tool credentials already have - Environment variable forwarding - Selectively forward host env vars by name (\n`forward_env`\n\nin config) - Command-sourced env vars - Mint a fresh secret per session by running a host command at start and injecting its output as an env var (\n`[defaults.env_commands]`\n\n) — for short-lived API keys/tokens. Trusted-scope config only - Host timezone inheritance - Containers automatically inherit the host's timezone (configurable via\n`[timezone]`\n\nconfig) - Sandbox context file - Auto-injected\n`~/SANDBOX_CONTEXT.md`\n\ntells AI tools about their environment (network mode, workspace path, persistence, etc.). Automatically loaded into each tool's native context system: Claude Code via`~/.claude/CLAUDE.md`\n\n, OpenCode via the`instructions`\n\nfield in`opencode.json`\n\n, pi via`~/.pi/agent/APPEND_SYSTEM.md`\n\nsymlink (opt out with`auto_context = false`\n\n)\n\n**Security & Isolation**\n\n- Credential protection - SSH keys,\n`.env`\n\nfiles, Git credentials, and environment variables are**never** exposed unless explicitly mounted - Privileged container guard - Refuses to start when\n`security.privileged=true`\n\nis detected, which defeats all container isolation - Security posture verification -\n`coi health`\n\nchecks seccomp, AppArmor, and privilege settings to confirm full isolation - Kernel version enforcement - Warns on host kernels below 5.15 that may lack security features for safe isolation\n- Real-time threat detection - Kernel-level nftables monitoring detects reverse shells, C2 connections, data exfiltration, DNS tunneling, and credential scanning\n- Automated response - Auto-pause on HIGH threats, auto-kill on CRITICAL — no manual intervention needed\n- Network isolation - nftables-based restricted/allowlist/open modes block private network access and prevent exfiltration\n- Protected paths -\n`.git/hooks`\n\n,`.git/config`\n\n,`.husky`\n\n,`.vscode`\n\nmounted read-only to prevent supply-chain attacks - Host-side immutable protection - Protected paths are locked with\n`chattr +i`\n\nduring sessions, preventing`unshare -m`\n\n+`umount`\n\nbypass of read-only mounts (opt out:`[security] host_immutable = false`\n\n) - Git identity guard - Containers enforce\n`user.useConfigOnly=true`\n\n, preventing AI tools from committing as the default \"code\" user - Guest API disabled - Incus guest API (\n`/dev/incus`\n\n) disabled by default, preventing host path and topology leaks - System containers - Full OS isolation with unprivileged containers, better than Docker privileged mode\n- Automatic UID mapping - No permission hell, files owned correctly\n- Audit logging - All security events logged to JSONL for forensics and compliance\n\n**Safe Dangerous Operations**\n\n- AI coding tools often need broad filesystem access or bypass permission checks\n**These operations are safe inside containers** because the \"root\" is the container root, not your host system- Containers are ephemeral - any changes are contained and don't affect your host\n- This gives AI tools full capabilities while keeping your system protected\n\n```\n# Install\ncurl -fsSL https://raw.githubusercontent.com/mensfeld/code-on-incus/master/install.sh | bash\n\n# Build image (first time only, ~5-10 minutes)\ncoi build\n\n# Start coding with your preferred AI tool (defaults to Claude Code)\ncd your-project\ncoi shell\n\n# Or use opencode instead (config-driven: [tool] name = \"opencode\",\n# or a profile: coi shell --profile opencode)\n\n# That's it! Your AI coding assistant is now running in an isolated container with:\n# - Your project mounted at /workspace\n# - Correct file permissions (no more chown!)\n# - Full Docker access inside the container\n# - GitHub CLI available for PR/issue management\n# - All workspace changes persisted automatically\n# - No access to your host SSH keys, env vars, or credentials\n```\n\nIncus is a modern Linux container and virtual machine manager, forked from LXD. Unlike Docker (which uses application containers), Incus provides **system containers** that behave like lightweight VMs with full init systems.\n\n| Capability | code-on-incus |\nDocker Sandbox | Bare Metal |\n|---|---|---|---|\nCredential isolation |\nDefault (never exposed) | Partial | None |\nReal-time threat detection |\nKernel-level (nftables) | No | No |\nReverse shell detection |\nAuto-kill | No | No |\nData exfiltration alerts |\nAuto-pause | No | No |\nNetwork isolation |\nnftables (3 modes) | Basic | No |\nProtected paths |\nRead-only mounts | No | No |\nAuto response (pause/kill) |\nYes | No | No |\nAudit logging |\nJSONL forensics | No | No |\nSupply-chain attack prevention |\nGit hooks/IDE configs protected | No | No |\n\n-\n**Linux-first, not Linux-last.** Docker Sandboxes' microVM isolation is only available on macOS and Windows. Linux gets a legacy container-based fallback. COI is built for Linux from the ground up because Incus is Linux-native. -\n**No Docker Desktop required.** Docker Sandboxes is a Docker Desktop feature. Docker Desktop is not open source and has commercial licensing requirements for larger organizations. COI depends only on Incus - fully open source, no vendor lock-in, no additional runtime. -\n**System containers, not containers-in-VMs.** Incus system containers run a full OS with systemd and native Docker support inside - one clean isolation layer. Docker Sandboxes nests application containers inside microVMs, adding architectural complexity. -\n**No permission hell.** Incus automatic UID/GID shifting means files created by agents have correct ownership on the host. No mapping hacks needed. (Note: files created via`sudo`\n\nin the workspace will be root-owned — the sandbox context file instructs AI tools to fix ownership after sudo operations.) -\n**Credential isolation by default.** Host environment variables, SSH keys, and Git credentials are never exposed to AI tools unless explicitly mounted. -\n**Simple and transparent.** No separate daemon, no opaque VM nesting. COI talks directly to Incus - easy to inspect, debug, and extend.\n\n```\n# One-shot install\ncurl -fsSL https://raw.githubusercontent.com/mensfeld/code-on-incus/master/install.sh | bash\n\n# This will:\n# - Download and install coi to /usr/local/bin\n# - Check for Incus installation\n# - Verify you're in incus-admin group\n# - Show next steps\n```\n\n**Manual installation:** Download the binary from [GitHub Releases](https://github.com/mensfeld/code-on-incus/releases), make it executable, and move to `/usr/local/bin/`\n\n. Requires Linux with Incus installed and user in the `incus-admin`\n\ngroup. **You must log out and back in** (or run `newgrp incus-admin`\n\n) after adding your user to the group — COI runs `incus`\n\ndirectly and requires the group to be active in your session. See the [Incus installation guide](https://linuxcontainers.org/incus/docs/main/installing/) for setting up Incus.\n\n```\n# Build the default coi-default image (5-10 minutes)\ncoi build\n\n# Build without compression (faster iteration):\n# set [container.build] compression = \"none\" in config or the profile\ncoi build\n\n# Build a custom image via a profile\ncoi profile create my-image\n# Edit .coi/profiles/my-image/config.toml: set [container] image and a [container.build] section\ncoi build --profile my-image\n\n# Build images for all profiles that have a [container.build] section\ncoi build --all\n\n# Rebuild all profile images from scratch\ncoi build --all --force\n```\n\n**What's included in the coi-default image:**\n\n- Ubuntu 24.04 base with Docker (full Docker-in-container support)\n**mise**(polyglot runtime manager) — Python 3, pnpm, TypeScript, tsx pre-installed; add more with`mise use go@latest`\n\n,`mise use ruby@3`\n\n, etc.- Node.js 22 LTS (system, for Claude CLI) + npm\n- Claude Code CLI (default AI tool) + GitHub CLI (\n`gh`\n\n) - tmux, git, curl, build-essential, and common build tools\n- Modern CLI utilities: fd-find, bat, tree\n- Debugging tools: strace, lsof\n- Database clients: sqlite3, postgresql-client, redis-tools\n- imagemagick for image processing\n\n**Custom images:** Build your own specialized images using profile-based build scripts that run on top of the base `coi-default`\n\nimage. See the [Image Management wiki page](https://github.com/mensfeld/code-on-incus/wiki/Image-Management) for complete profile-based build workflows.\n\n**COI works on macOS** using [Colima](https://github.com/abiosoft/colima) or [Lima](https://github.com/lima-vm/lima) VMs. See the [macOS Setup Guide](https://github.com/mensfeld/code-on-incus/wiki/macOS-Setup-Guide) for complete instructions.\n\n```\n# Interactive session (defaults to Claude Code)\ncoi shell\n\n# Use a different AI tool (config/profile-driven: [tool] name = \"opencode\")\ncoi shell --profile opencode\n\n# Use specific slot for parallel sessions\ncoi shell --slot 2\n\n# Resume previous session\ncoi shell --resume\n\n# Run a command in the sandbox (streams output, propagates exit code)\ncoi run -- npm test\n\n# Run the workspace run script (./coi-run) in the sandbox\ncoi run\n\n# Attach to existing session\ncoi attach\n\n# Real-time security monitoring dashboard\ncoi monitor\n\n# View session logs (setup messages, network notices, errors)\ncoi logs                        # Auto-detect container from current workspace\ncoi logs coi-abc123-1 -f        # Tail logs live\n\n# Stream the structured (JSON Lines) threat-event audit log for a session\ncoi audit coi-abc123-1 -f\n\n# Approve out-of-workspace mounts / forwarded sockets from a project .coi/config.toml\ncoi trust                       # approve   (coi trust --list to view, coi untrust to revoke)\n\n# List active containers and saved sessions\ncoi list --all\ncoi list --running              # Only running containers (also: --stopped, --status frozen)\n\n# Gracefully shutdown / force kill containers\ncoi shutdown coi-abc12345-1\ncoi kill --all\n\n# Cleanup stopped containers and orphaned resources\ncoi clean\ncoi clean --pools             # Detect containers in unused storage pools\n\n# Update coi to the latest release\ncoi update\n```\n\nUpgrading to 0.10?0.10 removes all config-shaped CLI flags (`--image`\n\n,`--persistent`\n\n,`--tmux`\n\n,`--tool`\n\n,`coi build --compression`\n\n,`coi shutdown --timeout`\n\n) and the legacy`CLAUDE_ON_INCUS_*`\n\n/`COI_LIMIT_*`\n\nenv-var overrides — everything config-shaped now lives in config files and profiles, and a removed flag fails with a hint naming its replacement key. See the[Upgrading from 0.9 to 0.10 guide](the[0.8→0.9 notes]are there too).\n\nAssign human-friendly names to containers for easy management from any directory:\n\n```\n# .coi/config.toml (in your project)\n[container]\nalias = \"myproject\"\ncoi shell myproject              # Launch session using alias (from any directory)\ncoi attach myproject             # Attach to running aliased container\n```\n\nSee the [Container Lifecycle and Sessions guide](https://github.com/mensfeld/code-on-incus/wiki/Container-Lifecycle-and-Sessions#container-aliases) for full alias documentation.\n\n```\n--workspace PATH        # Workspace directory to mount (default: current directory)\n--slot NUMBER           # Slot number for parallel sessions (0 = auto-allocate)\n--resume [SESSION_ID]   # Resume from session (omit ID to auto-detect latest for workspace)\n--continue [SESSION_ID] # Alias for --resume\n--profile NAME          # Use named profile\n```\n\nEverything else — image selection, persistence, network mode, mounts, socket forwarding, environment variables, SSH agent, monitoring, timezone, resource limits — is configured via config files or profiles, not flags (the former `--image`\n\nand `--persistent`\n\nflags were removed in 0.10; set `[container] image`\n\n/ `persistent`\n\ninstead). See the [Configuration wiki page](https://github.com/mensfeld/code-on-incus/wiki/Configuration) for the full reference.\n\nSee the wiki for detailed documentation:\n\n- Container management and low-level operations[Container Operations](https://github.com/mensfeld/code-on-incus/wiki/Container-Operations)- Push/pull files between host and containers[File Transfer](https://github.com/mensfeld/code-on-incus/wiki/File-Transfer)- Automate AI sessions with tmux commands[Tmux Automation](https://github.com/mensfeld/code-on-incus/wiki/Tmux-Automation)- Create and manage custom images[Image Management](https://github.com/mensfeld/code-on-incus/wiki/Image-Management)- Create checkpoints and rollback changes[Snapshot Management](https://github.com/mensfeld/code-on-incus/wiki/Snapshot-Management)\n\nCOI's isolation isn't only for AI agents — `coi run`\n\nexecutes regular commands\nand scripts with the same protection: workspace mount, read-only protected\npaths, secret masking, network isolation, resource/time limits, and security\nmonitoring. Output streams live, stdin is connected, and the command's exit\ncode becomes `coi run`\n\n's exit code.\n\n```\n# Arbitrary commands\ncoi run -- npm test\ncoi run -- make build\ncat data.csv | coi run -- ./process.sh\n\n# Workspace run script: with no command, coi runs ./coi-run\ncat > coi-run <<'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\nnpm ci && npm test\nEOF\nchmod +x coi-run\ncoi run\n```\n\nThe run script is executed **directly from the workspace mount** — it comes\nfrom the host; nothing is copied into the container. It is extensionless and\nmust be executable: the shebang decides the interpreter, so a bash, ruby, or\npython `coi-run`\n\nall work the same way. The container is cleaned up when the\nscript finishes — or kept, with `[container] persistent = true`\n\n, so installed\npackages and caches survive between runs.\n\n**Security note:** a cloned repository can ship its own `coi-run`\n\n, so\n`coi run`\n\nin a repo you don't trust executes that repo's code — inside the\nsandbox, which is exactly what the sandbox is for. For untrusted projects, use\na credential-limiting profile (e.g. `coi run --profile hardened`\n\n, or your own\nprofile with `[ssh] forward_agent = false`\n\nand a restricted network mode) so\nthe script gets no SSH agent, forwarded env, or open egress.\n\nResume a previous AI coding session with full history and credentials restored:\n\n```\ncoi shell --resume              # Auto-detect latest session for this workspace\ncoi shell --resume=<session-id> # Resume specific session\ncoi list --all                  # List available sessions\n```\n\n**What's restored:** Full conversation history, tool credentials, user settings, and project context. The profile used when the session was created is also automatically restored — no need to pass `--profile`\n\nagain (explicitly passing `--profile`\n\noverrides the saved one). Sessions are workspace-scoped — `--resume`\n\nonly finds sessions from the current workspace directory.\n\nSee the [Container Lifecycle and Sessions guide](https://github.com/mensfeld/code-on-incus/wiki/Container-Lifecycle-and-Sessions) for details on how session persistence works.\n\nBy default, containers are **ephemeral** (deleted on exit). Your **workspace files always persist** regardless of mode.\n\nEnable **persistent mode** to also keep the container and its installed packages:\n\n```\n# ~/.coi/config.toml, ./.coi/config.toml, or a profile\n[container]\npersistent = true\n```\n\n**What persists:**\n\n**Ephemeral mode:** Workspace files + session data (container deleted)**Persistent mode:** Workspace files + session data + container state + installed packages, system setup\n\nSee the [Container Lifecycle and Sessions guide](https://github.com/mensfeld/code-on-incus/wiki/Container-Lifecycle-and-Sessions) for details.\n\nConfig file: `~/.coi/config.toml`\n\n```\n[container]\nimage = \"coi-default\"\npersistent = true\n# storage_pool = \"\"            # Empty = Incus default pool\n# alias = \"myproject\"          # Human-friendly name for this workspace's containers\n\n[tool]\nname = \"claude\"\npermission_mode = \"bypass\"\n# auto_context = true          # Auto-inject sandbox context into tool's native system\n```\n\n**Configuration hierarchy** (highest precedence last):\n\n- Built-in defaults\n- User config (\n`~/.coi/config.toml`\n\n, or the file`$COI_CONFIG`\n\npoints at) - Project config (\n`./.coi/config.toml`\n\n) - Profile (\n`--profile <name>`\n\n)\n\nConfig-shaped settings have no CLI flags and no env-var overrides — config\nand profiles are the single source of truth. The remaining CLI flags are\nper-invocation choices only: `--workspace`\n\n, `--slot`\n\n, `--resume`\n\n, `--profile`\n\n.\n\nPlace a `.coi/config.toml`\n\nin any repository root to auto-configure COI for that project — useful for teams to share container image, environment, and resource limits.\n\nSee the [Configuration wiki page](https://github.com/mensfeld/code-on-incus/wiki/Configuration) for the full config reference, per-repo setup, profiles, and environment variables.\n\nThree ways to give containerized tools credentials:\n\nforwards any host Unix socket into the container via an Incus proxy device, so the host endpoint never crosses in — the building block for`[[sockets]]`\n\n**credential brokers**(a host process mints short-lived tokens; an in-container`credential_process`\n\nfetches them on demand).runs a host command at session start and injects its trimmed stdout as an env var — for plain env-var credentials (e.g. an AWS Bedrock bearer token). Trade-off: the value lives in the container env for the session, so prefer the broker for high-value/rotatable secrets.`[defaults.env_commands]`\n\npublishes container TCP ports on the host, so services the agent starts are reachable at`[ports]`\n\n`localhost:<port>`\n\n:`pool = 3`\n\ngives every session identity-mapped ports (the agent binds a pool number, you open the SAME number — the sandbox context file tells the agent to use them), and`[[ports.map]]`\n\npublishes fixed container ports (`name = \"web\"`\n\n,`container = 3000`\n\n) auto-allocated or pinned on the host side. Deterministic per workspace/slot, preflight-checked before launch, isolation-neutral (userspace proxy, no NAT rules);`coi list`\n\nshows each container's published ports. See the[Port Publishing wiki page](https://github.com/mensfeld/code-on-incus/wiki/Port-Publishing).copies static credential files from host to container at session setup — for tools that read credentials from disk rather than an env var. Use`[[credentials]]`\n\n`bundle = \"ollama\"`\n\nto reference a name from COI's built-in catalog (the same catalog`claude`\n\n/`opencode`\n\n/`pi`\n\nuse for their own credentials), or set`host`\n\n/`container`\n\n(plus optional`mode`\n\n) for an ad-hoc file not yet in the catalog. Missing host files are skipped with a log line rather than failing the session.\n\nSockets, `[ports]`\n\n, and ad-hoc `[[credentials]]`\n\nentries are gated behind `coi trust`\n\nwhen they come from an untrusted project `.coi/config.toml`\n\n; `env_commands`\n\nfrom one is ignored outright; catalog-referenced credentials are never gated (the host path is fixed by COI's own catalog, not the referencing config). See the [Configuration wiki page](https://github.com/mensfeld/code-on-incus/wiki/Configuration) for full examples and the trust model.\n\nProfiles are reusable container configurations bundling image, tool, limits, mounts, sockets, build scripts, context files, and environment into named templates.\n\n```\ncoi shell --profile rust-dev                 # Use a profile\ncoi profile create rust-dev                  # Create a new profile (then edit its config.toml)\ncoi profile list                             # List all profiles\n```\n\nEach profile is a self-contained directory (`.coi/profiles/<name>/`\n\n) bundling a `config.toml`\n\nplus optional build script and context file. Profiles support inheritance (`inherits = \"parent\"`\n\n), context files for AI-agent instructions, and custom build scripts. COI also ships a JSON Schema for profile configs (`coi schema profile`\n\n) so external tools can validate them. See the [Profiles wiki page](https://github.com/mensfeld/code-on-incus/wiki/Profiles) for the full reference, examples, and schema details.\n\nFor inspecting code you don't trust, COI ships a built-in ** hardened** profile — a one-flag preset:\n\n```\ncoi shell --profile hardened        # restricted net + secret masking + ephemeral + monitoring\ncoi profile info hardened           # see exactly what it locks down\n```\n\nIt bundles COI's strongest controls: `network.mode = \"restricted\"`\n\n(no exfil path), workspace secret masking (`.env`\n\n, `*.pem`\n\n, `secrets/**`\n\n, …), host immutability, an **ephemeral** container, **no SSH-agent forwarding**, and real-time threat monitoring with auto-pause/kill. It overrides a weaker global config (a global `mode = \"open\"`\n\nstill becomes restricted) and needs no setup.\n\nSee the [Resource and Time Limits guide](https://github.com/mensfeld/code-on-incus/wiki/Resource-and-Time-Limits) for complete documentation on controlling container resource consumption and runtime.\n\n**Quick example:**\n\n```\n# ~/.coi/config.toml\n[limits.cpu]\ncount = \"2\"\n\n[limits.memory]\nlimit = \"2GiB\"\n\n[limits.runtime]\nmax_duration = \"2h\"\n```\n\n**What you can limit:**\n\n- CPU cores and usage percentage\n- Memory and swap\n- Disk I/O rates\n- Maximum runtime and process count\n- Auto-stop on time limits\n\nSee the [Container Lifecycle and Sessions guide](https://github.com/mensfeld/code-on-incus/wiki/Container-Lifecycle-and-Sessions) for detailed explanation of how containers and sessions work.\n\n**Key concepts:**\n\n**Workspace files**: Always saved (regardless of mode)** Session data**: Always saved to`~/.coi/sessions-<tool>/`\n\n**Ephemeral mode**(default): Container deleted after exit, session preserved** Persistent mode**(`[container] persistent = true`\n\nin config or a profile): Container kept with all installed packages**Resume**(`--resume`\n\n): Restore AI conversation in fresh/existing container\n\n**Quick reference:**\n\n```\ncoi shell --resume            # Resume previous conversation\ncoi attach                    # Reconnect to running container\ncoi persist <container>       # Convert a running ephemeral session to persistent\ncoi unfreeze <name>           # Unfreeze paused/frozen container\ncoi unfreeze                  # Unfreeze all frozen COI containers\nclose                         # Properly stop container (inside, safe alias for poweroff)\ncoi shutdown <name>           # Graceful stop (outside)\ncoi close <name>              # Alias for 'coi shutdown' (deletes it — even a persistent one)\n```\n\nSee the [Network Isolation guide](https://github.com/mensfeld/code-on-incus/wiki/Network-Isolation) for complete documentation on network security and nftables-based network filtering.\n\n**Network modes:**\n\n**Restricted (default)**- Blocks private networks, allows internet** Allowlist**- Only specific domains/IPs allowed** Open**- No restrictions (trusted projects only)\n\n```\n# ~/.coi/config.toml\n[network]\nmode = \"restricted\"   # Default — blocks private networks, allows internet\n# mode = \"allowlist\"  # Only specific domains/IPs allowed\n# mode = \"open\"       # No restrictions (trusted projects only)\n```\n\nCOI includes **built-in security monitoring** to detect and respond to malicious behavior in real-time:\n\n```\n# Enable in config (~/.coi/config.toml)\n[monitoring]\nenabled = true\n```\n\n**Protects against:**\n\n**Reverse shells**- Detects common reverse shell patterns (auto-kill)** Data exfiltration**- Monitors large workspace reads/writes (auto-pause)** Environment scanning**- Flags processes searching for API keys and secrets** Network threats (NFT)**- Kernel-level detection of C2 connections, private network access, DNS tunneling, and allowlist violations\n\n**Automated response levels:**\n\n**INFO/WARNING**: Logged (+ alert for WARNING)** HIGH**: Container** paused**(requires`coi unfreeze`\n\nto continue)**CRITICAL**: Container** killed immediately**\n\nAudit logs are stored at `~/.coi/audit/<container-name>.jsonl`\n\nin JSON Lines format.\n\n`coi audit`\n\nstreams this log to stdout as JSON Lines (dump, or `--follow`\n\nfor live in-container events), ready to pipe into a SIEM or `jq`\n\n. See the [Security Monitoring wiki page](https://github.com/mensfeld/code-on-incus/wiki/Security-Monitoring) for monitoring commands, configuration, and NFT setup, and the [Audit Log wiki page](https://github.com/mensfeld/code-on-incus/wiki/Audit-Log) for the event format, field reference, sources, and tuning.\n\nSee the [Security Best Practices guide](https://github.com/mensfeld/code-on-incus/wiki/Security-Best-Practices) for detailed security recommendations.\n\nCOI automatically mounts security-sensitive paths as **read-only** to prevent supply-chain attacks:\n\n`.git/hooks`\n\n,`.git/config`\n\n,`.husky`\n\n,`.vscode`\n\n,`.coi`\n\n,`.claude/settings.json`\n\n,`.claude/settings.local.json`\n\nThe `.claude/settings.*`\n\nfiles can carry auto-executing hooks, so making them read-only stops a contained agent from planting a hook that a later session (or a native run on the host) would auto-execute on open. To opt a path back out, set `[security] writable_paths = [\".claude/settings.json\"]`\n\nin **trusted-scope** config (`~/.coi/config.toml`\n\nor `$COI_CONFIG`\n\n) — an untrusted project `.coi/config.toml`\n\ncannot remove protections. (`[git] writable_hooks = true`\n\nremains as a shorthand for `.git/hooks`\n\n.) See the wiki for details.\n\nSee the [System Health Check guide](https://github.com/mensfeld/code-on-incus/wiki/System-Health-Check) for detailed information on diagnostics and what's checked.\n\n**Run diagnostics:**\n\n```\ncoi health                    # Basic health check\ncoi health --format json      # JSON output\ncoi health --verbose          # Additional checks\n```\n\n**What it checks:** System info, kernel version, Incus setup, permissions, security posture (seccomp/AppArmor), privileged container detection, network configuration, storage, monitoring prerequisites, and running containers.\n\n**Exit codes:** 0 (healthy), 1 (degraded), 2 (unhealthy)\n\nSee the [Troubleshooting guide](https://github.com/mensfeld/code-on-incus/wiki/Troubleshooting) for common issues and solutions.\n\n**Common issues:**\n\n**DNS issues during build**- COI automatically fixes systemd-resolved conflicts- Run\n`coi health`\n\nto diagnose setup problems - Check the troubleshooting guide for detailed solutions\n\nSee the [FAQ](https://github.com/mensfeld/code-on-incus/wiki/FAQ) for answers to common questions.\n\n**Topics covered:**\n\n- Orphaned nftables/iptables rules\n- How COI compares to Docker Sandboxes and DevContainers\n- Windows support (WSL2)\n- Security model and prompt injection protection\n- API key security and trust model\n- What is Incus? (vs tmux)\n\n**Slack**:[Join the COI community on Slack](https://slack.karafka.io)— ask questions, report issues, share feedback** GitHub Issues**:[Open an issue](https://github.com/mensfeld/code-on-incus/issues)for bug reports and feature requests** Wiki**: Browse the[documentation wiki](https://github.com/mensfeld/code-on-incus/wiki)for guides and reference", "url": "https://wpnews.pro/news/mensfeld-code-on-incus-give-each-ai-agent-its-own-isolated-machine", "canonical_source": "https://github.com/mensfeld/code-on-incus", "published_at": "2026-07-14 09:43:43+00:00", "updated_at": "2026-07-14 10:18:46.749960+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-safety", "ai-tools"], "entities": ["Mensfeld", "Code on Incus", "Claude Code", "opencode", "pi.dev", "Anthropic", "Incus", "Docker"], "alternates": {"html": "https://wpnews.pro/news/mensfeld-code-on-incus-give-each-ai-agent-its-own-isolated-machine", "markdown": "https://wpnews.pro/news/mensfeld-code-on-incus-give-each-ai-agent-its-own-isolated-machine.md", "text": "https://wpnews.pro/news/mensfeld-code-on-incus-give-each-ai-agent-its-own-isolated-machine.txt", "jsonld": "https://wpnews.pro/news/mensfeld-code-on-incus-give-each-ai-agent-its-own-isolated-machine.jsonld"}}