Run dev tools.
Not risks.
Silo wraps every tool invocation in a fresh Apple Containerization
micro-VM with its own Linux kernel. npm install can't
exfiltrate your SSH keys. pip can't read your cloud
credentials. And it feels native — ~600 ms warm start
(rootfs cache hit).
Your dev tools run a lot of code you never read.
A single npm install might execute thousands of
postinstall scripts from hundreds of maintainers. Your
machine trusts all of them by default. One malicious release — and
you've shipped your life to a Discord webhook.
SSH keys
~/.ssh/id_ed25519, known_hosts, agent sockets — readable by any process you run. A four-line postinstall exfils every server you touch.
Cloud credentials
~/.aws/credentials, gcloud tokens, kubeconfig. Stolen credentials turn into crypto miners on your company's account by breakfast.
Browser cookies
Chrome and Safari cookie jars are just files. Session cookies let attackers skip your 2FA entirely — on GitHub, Gmail, banking, anything.
Slack & Discord tokens
Desktop chat apps keep tokens on disk in predictable paths. Impersonation in your company Slack is a ten-line script away.
Source code
Every private repo, every draft, every half-finished side project. Tarball ~/code to a remote host — done in seconds.
Your keychain
Once a malicious binary runs under your user, it can prompt for keychain access with your own app's name. You'll click yes. We all would.
AI agent blast radius
Claude Code, Cursor, and friends read your tree and run shell commands. A prompt-injected README is game over. Same access as you.
Other projects
Lateral movement doesn't need privilege escalation. Your dev laptop has every client's code on one disk, under one UID.
Python venvs, Node's nvm, and even pipx isolate dependencies —
not permissions. Everything still runs as you, with your full filesystem,
on your kernel.
One process → one micro‑VM. Real kernel boundary.
Built on Apple Containerization
macOS 26 shipped a native Containerization.framework that
boots stripped-down Linux VMs in the low hundreds of milliseconds.
Silo is a thin CLI on top of it: when you run a tool, it spins
up a one-shot VM, mounts only the files you asked for, runs your
command, streams output to your terminal, and evaporates.
Feels native, isn't
Silo installs shims for python, node, cargo, go, deno —
so typing python app.py transparently runs inside a VM.
No ceremony, no exec, no container runtime glued to
your shell.
Explicit, not magic
Every non-default exposure — network egress, host files,
forwarded ports — is an explicit key in silo.toml,
checked in with the repo. Reviewed once, reused forever.
The CLI is small on purpose.
silo <tool> shorthand and via ~/.silo/bin shims.package.json, requirements.txt, Cargo.toml, go.mod) and generate a silo.toml.silo build node npm install. Survives every future run.<projectRoot>/.silo/<tool>/. silo add kotlin appends JDK + Kotlin to claude-code via silo.toml overrides — no touch on the global install.~/.silo/bin shims so extra commands route through silo — yarn, pnpm, ipython, anything.silo.toml: install missing tools, warm the rootfs cache. Safe to re-run.~/.silo disk use, LRU-evict cold rootfs entries, zstd-compress stale ones, or wipe everything.silo.toml overrides.Silo vs. the tools you already use.
| silo | docker desktop | devcontainers | venv / nvm | native | |
|---|---|---|---|---|---|
| Malicious code exposure | ✓ micro-VM per invocation | × depends on workflow | partial (workspace + mounts) | × full $HOME, keys, creds | × full $HOME, keys, creds |
| Kernel isolation | ✓ real (new kernel) | × shared linuxkit | × shared linuxkit | × none | × none |
| Filesystem scoping | ✓ cwd only, by default | partial (volumes) | partial (workspace mount) | × full $HOME | × full $HOME |
| Network deny-by-default | ✓ per-project allowlist | × bridge wide open | × bridge wide open | × same as host | × same as host |
| Start latency | ~600 ms warm / ~25 s first run | 2–8 s + daemon | 2–8 s + daemon | instant | instant |
| Memory overhead | 2 GB / VM (default) | 1–4 GB persistent | 1–4 GB persistent | none | none |
| Transparent shims | ✓ python/node/cargo/go | × explicit docker run | × explicit devcontainer | partial (per-tool) | n/a |
| Background daemon? | ✓ none | × required | × required | none | none |
Docker and devcontainers are container orchestration that happens to offer isolation. Silo is isolation that happens to use a container. Different goals; they coexist fine.
Three lines to sandboxed.
# 1. install silo (the user/tap/formula triple dodges homebrew-cask's silo) $ brew install rchekalov/apps/silo # 2. put silo shims on your PATH via shellenv (same convention as brew) $ echo 'eval "$(silo shellenv)"' >> ~/.zshrc # 3. install a tool (first run fetches a prebuilt kernel + initfs; ~30 sec once, seconds after) $ silo install python # python is now sandboxed: $ python suspicious_script.py # one silo install per tool you need: $ silo install node # adds npm, npx shims $ silo install claude-code # adds claude shim # network is off by default. opt in per-tool via silo.toml: $ silo init # auto-detects tools + writes an allowlist stub
Requires macOS 26+ on Apple Silicon. The Homebrew formula installs a
codesigned binary with com.apple.security.virtualization
entitlements — everything is handled for you. See the
quickstart for silo.toml and
opt-in network rules.
Fast enough you'll forget it's there.
measured on M2 Pro · macOS 26.1 · silo 0.6.2 · your results will vary
The questions you're about to ask.
Is this just Docker with a nicer CLI?
No. Docker orchestrates containers — namespaced processes sharing the host kernel. Silo uses Apple Containerization, which boots a real Linux VM per container with its own kernel. That's the difference between "shared kernel, fences between processes" and "separate kernel, hypervisor boundary." Different goals; they coexist fine.
Why only Apple Silicon?
Apple Containerization framework (macOS 26+) is the piece that makes sub-second VM starts possible on macOS. It needs Apple Silicon. A Docker backend for Linux users is on the roadmap — on Linux, native namespaces give you most of what Silo gives you on macOS.
Do I lose editor integration? LSPs?
No. silo lsp <tool> runs the language server (pyright, tsserver, rust-analyzer, gopls) inside the VM and proxies JSON-RPC over stdio with automatic path rewriting. silo ide vscode|zed|neovim generates editor configs that point at the proxied server. Autocomplete and go-to-definition work; code analysis stays isolated.
How do I give a project network access?
Add a silo.toml with an allowlist. The VM has no network by default. Flip hostAccess = true and list the domains each tool is allowed to reach — registry.npmjs.org, pypi.org, *.github.com, your private registry. Everything else is blocked by an in-VM HTTP proxy.
What about files outside my cwd?
They don't exist. The VM only mounts the directory walk-up finds the silo.toml in (or the cwd, if none). ~/.ssh, ~/.aws, other projects — none of it is visible. That's the whole point: a compromised package literally cannot open a file that isn't there.
Does `npm install` / `pip install` work out of the box?
Yes, for the standard registries. Since 0.6.0 the registry ships built-in allowlists: pip reaches pypi.org, npm reaches registry.npmjs.org, cargo reaches crates.io. Everything else is blocked — if your project needs a private registry or an extra CDN, add it to silo.toml. To allow open internet for a tool: set proxy.allow = ["*"] in its overrides block.
Can I use it in CI?
Silo is macOS-only today (Apple Silicon + macOS 26+), so CI works if your runners are macOS. A prebuilt-image workflow that ships ready-to-boot rootfs artifacts from GitHub Actions is planned — see docs/ci-prebuilt-images.md in the repo. For Linux CI, use a normal container.
What persists between runs?
Your project directory (mounted read-write), the rootfs cache (so warm starts are ~600 ms), and per-tool package caches (pip, npm, cargo). Everything else is ephemeral. If you want packages installed via pip or npm to survive, either keep them inside the project (venv, node_modules) or use silo build to bake them into the rootfs.
Is Silo free?
Yes. Apache 2.0. It's a personal project, not a product. The CLI wraps Apple's Containerization framework (also open-source) — there's no paid tier, no telemetry, no account.
What shipped, when.
Fix install proxy regression; uv + poetry cache mounts.
- 0.6.0 introduced deny-by-default networking, but also broke
silo installfor any tool with a registry allowlist: the install-time proxy was cleared beforeRunSetup, so pyright, eslint, and similar postInstall commands saw a deny-all proxy and failed. Fixed in 0.6.2:BakeToolnow preserves the registry's proxy and letsRunSetupunion allow ∪ installAllow as designed. - Python tool gains two new cache mounts:
~/.cache/uvand~/.cache/pypoetry. Poetry and uv users now get warm-cache behavior between runs (no re-download on everysilo run). - Registry overlay extended to cache mounts: existing installs pick up new registry mounts (uv, poetry) without needing a
silo install --force. Includes end-to-end integration tests for venv auto-activation, pyproject editable installs, pytest, poetry, and uv workflows.
Deny-by-default networking; pip install and npm install work out of the box.
- Network policy is now deny-by-default. The registry ships built-in allowlists for every standard tool:
pip installreaches pypi.org,npm installreaches registry.npmjs.org,cargo addreaches crates.io, and so on — without any[network]block in yoursilo.toml. To allow open internet: setproxy.allow = ["*"]. - Network config merges as a list-union:
silo config network allow node 'corp.registry.io'extends the npm allowlist rather than replacing it. Addproxy.denyto block individual hosts the registry allows. - Before 0.6.0, tools with
hostAccess: trueand no allowlist reached the open internet. Affected claude-code, playwright, cypress, psql, and jupyter at runtime. All are now proxy-gated with purpose-appropriate allowlists. Legacy projects that want open internet can opt in explicitly withproxy.allow = ["*"].
rootfs sync fix, venv auto-activation, shim shadowing detection.
silo buildnow runssyncinside the guest before snapshotting the rootfs — fixes 0-byte package directories afterpip install.- Python projects with a
.venv/orvenv/in the workspace root auto-activate it on entry:VIRTUAL_ENV+PATHare set sopipand the interpreter resolve inside the venv, not the rootfs. silo doctorandsilo installwarn when a shim in~/.silo/bin/is shadowed by an earlier PATH entry (homebrew, conda, asdf) so the shim is silently unreachable.
Pyenv-style pinning, TOML config, ssh-agent forwarding.
- Sync-installed tools now fall through to the next instance on PATH (homebrew, pyenv, system) when invoked outside any project that claims them —
silo installstill claims globally.silo pin/silo unpinflip the flag;silo list/silo currentsurface it. (0.5.0) - Default VM memory raised from 512 MB to 2 GB — the old default was tight enough that
npm installon real projects would OOM mid-postinstall. Per-tool overrides still apply. - Project bakes are now content-addressed under
~/.silo/baked/<sha>/instead of scattered under each project tree.silo list/silo syncaccurately surface project-pinned images and stop printing "rootfs up-to-date" when there is no project rootfs. (0.5.1) .siloconf(YAML) is nowsilo.toml; auto-migration on first run, YAML accepted until 0.6. SSH-agent forwarding lands as a config option — forwards$SSH_AUTH_SOCKinto the VM without exposing host private keys, so privategit+sshURLs work. (0.5.2)- Argv parsing went Docker-style: silo flags before the tool, inner command after — the old
--separator still works but is no longer required.
Homebrew prebuilts, claude-code reliability, install UX polish.
- Homebrew install in seconds, not minutes (0.4.17): prebuilt ad-hoc-codesigned tarball attached to each release — no Go or Swift toolchain pulled in, ~3.5 GB of transitive deps gone. Reproducible Swift TLS/HTTP stack via committed
Package.resolved(0.4.10). Tap moved torchekalov/apps; old path auto-redirects (0.4.19). - First
silo installfetches a prebuiltvmlinux+initfs.ext4from the GitHub release (~30 sec) instead of cross-compiling vminitd (~5 min); on failure (offline, bad checksum) drops to source-build fallback (0.4.4). - Claude Code actually installs and stays logged in: registry entries can declare
postInstall:, baked into~/.silo/builds/<tool>/rootfs.ext4(0.4.12);CLAUDE_CONFIG_DIR+ANTHROPIC_CONFIG_DIRpinned so credentials persist across runs (0.4.13); cache mounts can mark themselvesnoGCso background GC doesn't silently drop OAuth state (0.4.14). silo add <packages|language>records project-scopedpostInstalloverrides and bakes a per-project rootfs without mutating the global tool. Language shortcuts:silo add kotlin/java/ruby(0.4.15).- Install UX: atomic install — pull before write, no half-installed zombies in
silo list(0.4.6); live byte-accurate progress during the OCI pull (0.4.8 / 0.4.9 / 0.4.11); realHTTP 401/429/5xxmessages instead ofRegistryClient.Error error 0(0.4.9); Python 3.13/3.14, plus rejection of unknown versions (was silently rewriting3.13to the 900 MB debian variant) (0.4.7); broadenedsilo shellenvto fish/csh/tcsh/pwsh (0.4.5). - In-VM HTTP proxy now binds
0.0.0.0so the guest can reachhost.silo.internal— was silently 100% failing allowlisted requests withECONNREFUSED(0.4.16).
CLI reshape, Go-only, disk reduction.
- One verb per job:
silo buildabsorbssetup/rebuild,silo syncabsorbspull/apply,statussplits intodoctor+current. - Rust implementation removed; Go is now the sole binary.
- Disk reclaim: LRU + age-based rootfs GC, zstd cold-tier compression (~4× smaller), image deletion on uninstall.
- Two-phase SIGINT handler; reaper for stale
silo-*container dirs.
Project config.
.siloconfwalk-up with project-overrides-global merge; per-tool env, network allowlists, port forwarding.silo use/silo unusefor pyenv-style project pins.silo initauto-detects tools from marker files.
Transparent shims.
- Shell shims for
python,node,cargo,rustc. Editor-friendly (LSPs work). - Kernel image split from binary —
silo installfetches it on demand. - Fixed a deadlock when stdout was a pipe and the tool wrote > 64 KB.
Hello world.
- Initial release.
silo runworks for Python, Node, and bash. - macOS 26+, Apple Silicon only.
Short reads to get real work done.
Silo v0.5.0 release notes
Pyenv-style shim fall-through. Tools added via `silo use` no longer shadow your homebrew binaries everywhere — only inside projects that claim them.
Running Claude Code in silo when every MCP server is a loaded gun
OX Security disclosed a by-design MCP vulnerability that turns a config entry into arbitrary command execution on the user's laptop. Anthropic calls it expected behaviour. Running Claude Code inside silo turns this RCE into a torn-down VM with no keys, no network, no history to steal.
Understanding silo.toml: where it lives, how it merges, what every field does
A complete reference for Silo's project config file — what belongs in it, what belongs in ~/.silo/silo.toml, and the full set of fields with worked examples.
Node.js with Silo: npm, yarn, pnpm, and version pinning
Running Node under Silo — persisting node_modules with silo build, switching Node versions per project, and wiring yarn / pnpm via corepack or custom shims.
Python with Silo: install, persist, pin versions
Everything you need to run Python under Silo — installing pip packages so they survive, switching versions per project, and configuring network access for PyPI.
Silo v0.4.0 release notes
The CLI is reshaped around one-model-per-command, the Rust implementation is gone, and disk reclamation now runs automatically. Smaller surface, fewer footguns.