Run VS Code and AI coding agents inside an isolated container, while keeping your development environment defined by your project flake.
This is the flat-folder version and is intended to be shareable via git subtree.
Files in this folder:
Dockerfilecontainer-entrypoint.shai-sandboxai-sandbox.nixREADME.md
With modern AI workflows, the bigger risk is often not just CLI tools, but VS Code plugins.
Coding-agent extensions can:
- execute shell commands
- modify your repository
- access tokens and credentials
In many cases you do not fully know what they do, and some are not even open source.
Even if a tool sandboxes parts of its execution, VS Code itself still usually runs on your host machine.
ai-sandbox takes a different approach:
- VS Code runs inside a container
- extensions run inside that container
- your host
$HOMEis not mounted - your host
/nixis not mounted - the actual development environment still comes from your project
flake.nix
So instead of trusting every coding-agent plugin, you isolate the whole editor environment it runs in.
- builds one global Ubuntu image with real Microsoft VS Code and Nix
- uses bind-mounted host directories for
/nixand sandbox home (defaults:~/.cache/ai-sandbox/nixand~/.cache/ai-sandbox/home) - mounts the current project at
/workspace - if the project is a Git submodule, mounts the top superproject at
/workspaceand opens the submodule path inside it (preserves nested submodule.gitpath resolution) - if a flake is available, launches via
nix develop - if no flake is available, launches plain VS Code / plain shell
- supports multiple concurrent containers per workspace (auto instance names, optional
--instance)
In practice, that means:
- no per-project container config is required
- you can just run
aisorai-sandboxinside a flake-enabled repository - the sandbox reuses a shared
/nixcache across projects - VS Code, extensions, and coding agents run inside the container instead of directly on your host
Dev Containers are mainly about reproducible development environments.
This project is more specifically about running VS Code itself in a sandboxed container, which makes VS Code extensions and coding-agent plugins much safer to use.
| Aspect | ai-sandbox | Dev Containers |
|---|---|---|
| Goal | isolate VS Code + agent plugins | reproducible dev environments |
| Env definition | flake.nix via nix develop |
devcontainer.json (+ Docker / Compose) |
| Per-project config | none needed | usually required |
| Editor runs | inside container | on host |
| Plugin isolation | yes | usually no |
| Cache reuse | shared /nix store |
Docker layers |
| Multi-service setup | no | yes |
| Portability | mostly Linux/Nix | cross-platform |
If your main concern is “I want to use coding-agent plugins without giving them direct access to my host editor session”, this is a better fit than normal devcontainers.
If your main concern is standardized team environments across platforms and tools, devcontainers are the more standard choice.
Build/update the base image only:
ai-sandbox build-baseBuild the image and then install default user-space software (Codex + VS Code):
ai-sandbox build .Install or refresh default user-space software without rebuilding:
ai-sandbox install .
ai-sandbox install . --force
ai-sandbox install . --only codex
ai-sandbox install . --only vscodeInside sandbox terminals, apt-get and apt are available directly. They are
wrapped to run with root privileges for package-management commands.
Rebuild the base image from scratch (remove old image tag first, keep storage dirs):
ai-sandbox rebuildRemove persistent sandbox containers (keep home/nix storage):
ai-sandbox reset-container .
ai-sandbox reset-container --allReset sandbox storage (clear ~/.cache/ai-sandbox/nix and ~/.cache/ai-sandbox/home by default):
ai-sandbox reset-storageRepair shared ai-sandbox Nix cache in place (verify/repair store paths, no delete):
ai-sandbox repair-nixSync global Codex instructions (sandbox-wide, not project-local):
ai-sandbox agents pull
ai-sandbox agents push
ai-sandbox agents reset
ai-sandbox agents clear
ai-sandbox agents pull --file ./AGENTS.md --forceInside sandbox terminals, ai-sandbox is available as an alias to
/workspace/ai-sandbox/ai-sandbox, so these agents commands can be run there too.
Sync global Codex skills:
ai-sandbox skills pull
ai-sandbox skills push
ai-sandbox skills push --dir ./skills --forceWarm the current project flake into the shared /nix storage directory:
ai-sandbox warm .Start VS Code for the current directory:
ai-sandbox start .By default, start streams startup logs (including flake/Nix setup) and auto-detaches once VS Code launch begins.
Start and continue following logs even after VS Code launch:
ai-sandbox start . --logsStart with a stable instance suffix (useful for multiple VS Code windows/workspaces side by side):
ai-sandbox start . --instance vscode-a
ai-sandbox start . --instance vscode-bEach sandbox instance now uses a hybrid VS Code profile model (details below).
If you do not pass --instance, ai-sandbox now uses a stable default instance name per workspace so VS Code profile state is preserved across relaunches. If that default instance is already running, ai-sandbox automatically falls back to a unique instance suffix.
Open an interactive shell in the sandbox:
ai-sandbox shell .By default, start and exec reuse persistent per-workspace containers
(instance=default) instead of always using disposable --rm containers.
shell starts a fresh disposable container each time so interactive sessions stay isolated.
Run a command directly in shell mode (flake-aware):
ai-sandbox shell . -- codex
ai-sandbox shell codexRun any command in the sandbox (auto-reuses a running workspace sandbox when available):
ai-sandbox exec . -- codex --version
ai-sandbox exec . -- ai-sandbox-default-install --only codex --forceWith the short alias, unknown commands are routed to sandbox exec automatically:
ais codex --version
ais ai-sandbox-default-install --only codex --force
ais code --versionThe shell prompt includes a clear AI-SANDBOX marker, project name, directory, and Git branch/status.
Show logs from an existing sandbox container:
ai-sandbox logs . # one-shot
ai-sandbox logs . -f # followOpen a file in the running sandbox VS Code from the host:
ai-sandbox open-in-editor /abs/path/to/file.ts 1062 55Override the flake location:
ai-sandbox start . --flake /path/to/flake.nix
ai-sandbox start . --flake /path/to/flake-root
ai-sandbox warm . --flake ../some/other/flake-projectThe workspace is still the first positional directory. --flake only changes which flake gets used for nix develop.
Override network mode (default is host for localhost OAuth callback compatibility):
ai-sandbox start . --network host
ai-sandbox start . --network bridgeAutomatic network healing for long-running containers (default enabled):
ai-sandbox start . --auto-reconnect
ai-sandbox start . --no-auto-reconnect
ai-sandbox start . --auto-reconnect-interval 8Diagnose or repair an already running sandbox after host network changes:
ai-sandbox doctor-net .
ai-sandbox reconnect-network .In a flake-enabled repository:
cd your-project
ai-sandbox warm .
ai-sandbox start .Important: ai-sandbox start launches VS Code from nix develop when your project exports a default dev shell, so flake-provided tools and shellHook environment variables should be available inside VS Code and agent processes.
That does not replace project bootstrap steps. Repo-local tools such as vue-tsc often come from node_modules/.bin, Corepack shims, generated SDKs, or other files that only exist after you run the project initialization command inside the sandbox itself. If a tool is present in your normal dev shell but missing for Codex, the usual fix is to open a terminal in the sandboxed VS Code window and run the repo's setup step there first, for example yarn install, pnpm install, npm install, or a project-specific bootstrap command.
Or, if you want a shell instead of VS Code:
ai-sandbox shell .If you have the short alias installed:
cd your-project
aisThat is the intended workflow: enter a project, run ais, and get VS Code inside a container with the project dev environment coming from the flake.
When multiple independent VS Code processes run in different containers, sharing one full --user-data-dir can break Chromium webview/service-worker state.
To keep concurrent containers stable while preserving desktop-like behavior, ai-sandbox uses:
- per-instance
--user-data-dirinternals at/sandbox-home/.vscode-data/instances/<workspace-hash>-<instance> - shared user config at
/sandbox-home/.vscode-shared-user:settings.jsonkeybindings.jsontasks.jsonlocale.jsonsnippets/
- shared extensions at
/sandbox-home/.vscode-extensions/shared
Practical behavior:
- settings/keybindings/snippets stay consistent across instances
- extensions installed in one instance appear in all instances
- webview/process/cache internals remain isolated per instance to avoid cross-container collisions
Sandbox home is persisted in ~/.cache/ai-sandbox/home by default (configurable), and is mounted at /sandbox-home inside containers.
ai-sandbox build,ai-sandbox build-base, andai-sandbox rebuilddo not erase sandbox home.ai-sandbox reset-storageis the command that erases persisted home and nix storage.
CODEX_HOME is pinned to:
/sandbox-home/.codexCodex global instructions are seeded once (if missing) to:
/sandbox-home/.codex/AGENTS.mdfrom image default:
/usr/local/share/ai-sandbox/default-AGENTS.mdThis means global instructions and skills are persisted on host storage and can
be modified from any workspace. Project-local AGENTS.md remains separate.
ai-sandbox also ensures ~/.codex/config.toml contains:
[sandbox_workspace_write]
writable_roots = ["/sandbox-home/.codex"]so Codex started from /workspace can still edit global files in CODEX_HOME.
To fully disable default seeding and erase global Codex instructions:
ais bash -lc 'mkdir -p ~/.codex && touch ~/.codex/.disable_default_agents_seed && rm -f ~/.codex/AGENTS.md ~/.codex/AGENTS.override.md'To re-enable default seeding later:
ais bash -lc 'rm -f ~/.codex/.disable_default_agents_seed'Codex is installed in user space (~/.npm-global) and persisted in sandbox home:
ai-sandbox install . --only codex
ais codex --versionVS Code runs from a user-space install in sandbox home:
ai-sandbox install . --only vscode --force
ais code --versionShell behavior note:
- ai-sandbox shell startup does not source
$HOME/.bashrcby default (to avoid host/sandbox prompt hook conflicts) - set
AI_SANDBOX_SOURCE_USER_BASHRC=1if you explicitly want to opt back in
If only the first sandbox VS Code window works and later ones show:
Error loading webview: Could not register service worker: InvalidStateError
then clear stale shared VS Code profile data from older ai-sandbox runs and restart:
ai-sandbox reset-storageThen launch separate instances again (for example with different --instance names).
If nix develop fails with missing /nix/store/... files, run:
ai-sandbox repair-nixThis verifies and repairs the shared ai-sandbox /nix cache without deleting it.
If you see database disk image is malformed for /nix/var/nix/db/db.sqlite:
- Stop running ai-sandbox containers for this workspace.
- Run
ai-sandbox repair-nix. - If it still fails, run
ai-sandbox reset-storageto recreate shared caches from scratch.
Recent ai-sandbox versions now seed /nix only once and avoid copying seeded Nix DB runtime files into a live cache, which reduces the chance of this corruption pattern.
If you recently changed ai-sandbox scripts, rebuild and restart containers so the new entrypoint is used:
ai-sandbox rebuildTo confirm home persistence across rebuild:
ais bash -lc 'echo ok > ~/.local/state/ai-sandbox-persist-check'
ai-sandbox rebuild
ais bash -lc 'cat ~/.local/state/ai-sandbox-persist-check'If dev-server "open in editor" links (error overlays, stack traces, click-to-open file links) open host VS Code instead of the sandbox window:
ai-sandbox start .
LAUNCH_EDITOR=ais <your-dev-command>This pattern is framework-agnostic and works for many stacks that honor LAUNCH_EDITOR through launch-editor style tooling (for example Vite apps like React/Vue/Svelte, Quasar CLI with Vite, and other dev servers that support LAUNCH_EDITOR).
Examples:
LAUNCH_EDITOR=ais npm run dev
LAUNCH_EDITOR=ais pnpm dev
LAUNCH_EDITOR=ais yarn dev
LAUNCH_EDITOR=ais quasar devThis works because ais detects launch-editor style arguments (<file> [line] [column]) and forwards them to:
ai-sandbox open-in-editor <file> <line> <column>If you prefer, ai-sandbox-launch-editor remains available and does the same forwarding.
If you do not use the Nix module helper package, create a tiny wrapper script and point LAUNCH_EDITOR to it:
#!/usr/bin/env bash
exec ai-sandbox open-in-editor "$@"Then:
LAUNCH_EDITOR=/absolute/path/to/your-wrapper.sh <your-dev-command>If shell prompts look corrupted (for example visible \[\] markers), leave AI_SANDBOX_SOURCE_USER_BASHRC unset (default 0) or explicitly disable it:
export AI_SANDBOX_SOURCE_USER_BASHRC=0Put this whole folder somewhere in your NixOS repo, for example:
modules/ai-sandbox/
Dockerfile
container-entrypoint.sh
ai-sandbox
ai-sandbox.nix
README.md
Import the module from your system config:
{
imports = [
./modules/ai-sandbox/ai-sandbox.nix
];
programs.ai-sandbox.enable = true;
}Then rebuild:
sudo nixos-rebuild switch --flake .After that, ai-sandbox is available everywhere.
Create a split branch from this repo and push it to a dedicated remote:
git subtree split --prefix=ai-sandbox --branch ai-sandbox-split
git push git@github.com:<org>/<ai-sandbox-repo>.git ai-sandbox-split:mainConsume it from another repository:
git subtree add --prefix=modules/ai-sandbox git@github.com:<org>/<ai-sandbox-repo>.git main --squashPull updates later:
git subtree pull --prefix=modules/ai-sandbox git@github.com:<org>/<ai-sandbox-repo>.git main --squashPush local subtree changes back to the subtree remote:
git subtree push --prefix=ai-sandbox git@github.com:<org>/<ai-sandbox-repo>.git mainDo not auto-launch the container from direnv. That gets annoying fast.
Use direnv to expose helper aliases instead.
Example .envrc:
use flake
alias sandbox-start='ai-sandbox start .'
alias sandbox-shell='ai-sandbox shell .'
alias sandbox-warm='ai-sandbox warm .'If your flake is elsewhere:
use flake ./packaging/nix
alias sandbox-start='ai-sandbox start .'
alias sandbox-shell='ai-sandbox shell .'
alias sandbox-warm='ai-sandbox warm .'ai-sandbox also detects flake overrides from .envrc before startup:
use flake ./path/to/flake-rootexport AI_SANDBOX_FLAKE_OVERRIDE=./path/to/flake.nix
Then run:
direnv allow
sandbox-warm
sandbox-start- This has only been tested with Nix Home Manager so far.
- This has been tested with an X.org server; it will likely not work with Wayland yet.
- Contributions are welcome.
- The sandbox still has X11 access. That is the weakest part of this design.
- The repo is mounted read/write on purpose.
- Host
$HOMEis not mounted. - Host
/nixis not mounted. - The shared bind-mounted storage makes repeated launches much faster after the first warmup.
- Storage defaults to
~/.cache/ai-sandbox/{home,nix}and is directly manageable as your user on the host. ai-sandbox start,shell, andwarmauto-register a host URL handler forvscode://andvscode-insiders://so OAuth callbacks (for example GitHub login) route back into the running sandbox container.- Security note: sandbox image grants passwordless
sudoforapt/apt-get/dpkgto support in-sandbox package installs.
This is not a hardened sandbox.
It improves isolation in a very practical way, especially for VS Code extensions and coding agents, but it is not equivalent to a VM or a strict security boundary.
The main tradeoff is convenience vs isolation:
- real VS Code runs in the container
- host home and host
/nixstay out - but X11 access, writable workspace mounts, and optional host networking still exist
So the right way to think about this is:
a practical containment layer for AI-assisted development
not
a perfect sandbox
If you install AI coding agents directly into VS Code on your host, you are effectively trusting arbitrary plugin code with a lot of access.
This project gives you a much more practical setup:
- open a flake-based repo
- run
ais - get real VS Code inside a container
- keep your dev environment Nix-native
- reuse cached dependencies across projects
- reduce the blast radius of VS Code plugins and coding agents