SandVault isolates agents in a macOS user account, and its own printed profile contradicts itself
Run AI agents isolated in a macOS user account and sandbox-exec. Configured to run Claude Code, OpenAI Codex, Cursor Agent, Google Gemini.
At a glance
- What is it?
- A shell-based isolation layer that runs AI coding agents and arbitrary commands as a limited macOS user under sandbox-exec, with a shared workspace, optional SSH mode and native per-agent installs. Useful for giving an agent a real filesystem without a VM, but the security profile it prints cannot be read straight, and GUI access is declared impossible while the same page advertises Chrome and iOS Simulator.
- Who is it for?
- Use SandVault when you want an agent to work on real files with a real shell and no VM overhead, and accept the SSH account, the per-repo deploy keys and the host-side Homebrew install as part of the cost. Do not treat the printed profile as an audited boundary: /Users/Shared is writable inside a rule that denies /Users, and /Volumes/Macintosh HD is writable inside a rule that denies /Volumes.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 17 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Isolation is two layers: a limited account plus sandbox-exec
The isolation is not one mechanism. It is a limited user account and `sandbox-exec` layered together, which is why the setup also configures passwordless account switching: after setup, invoking `sv` does not prompt for credentials.
The default execution mode is impersonation, described as basically `sudo -u sandbox-$USER COMMAND`. That is the whole design in one line. You stay in your own session, the agent's process runs under a different uid, and the shell profile rules decide what that uid may touch. The page's own summary is four claims: it cannot access your home directory, it runs with standard user privileges, it cannot modify system files, and it has no access to mounted drives.
What makes that worth reading closely is that the profile listing immediately below those claims does not agree with them. The rules name `/Users/Shared/sv-$USER` as writable while also denying `/Users/*` as other user directories, and name `/Volumes/Macintosh HD` as writable while denying `/Volumes/*` as mounted, remote and network drives. Whether the deny rules are evaluated after the specific allows or whether the listing is illustrative rather than the literal profile is not settled by the text, and that ordering question is the whole security question. The four bullets above are a summary of intent. The rule list is what an auditor needs, and the two are not in agreement.
Filesystem access outside the sandbox lives at `/usr`, `/bin`, `/etc` and `/opt`, readable only.
GUI applications cannot run, yet Chrome and iOS Simulator are advertised
The quick-links list ends with a hard negative: it is not possible to run GUI applications from within the sandbox. That is not a caveat about degraded rendering or missing performance, it is a categorical statement, and it has a page pointing at a section for details.
Two lines earlier the same page advertises sandbox access to Chrome, Lightpanda and the iOS Simulator, under a heading for web and app automation. Both of those are desktop applications in the ordinary sense, and the iOS Simulator in particular is a windowed app driven by the Simulator runtime. A project that both refuses GUI apps and lists GUI apps as a supported use is telling you the boundary is more specific than the summary implies, but it does not say where that boundary sits.
So the practical question has no answer in the documentation: which process in the browser or iOS testing path is not a GUI app. Without that, the automation path is something to probe rather than plan against. If your workflow depends on driving a visible browser to test a web interaction, verify it end to end inside the sandbox before building on it, because the page simultaneously tells you it works and tells you the class of thing it uses cannot run.
The repository description names Cursor Agent, the quick start does not
The project summary says SandVault is configured to run Claude Code, OpenAI Codex, Cursor Agent and Google Gemini. The quick-start block launches six things instead, and Cursor is not among them:
# Run Claude Code in the sandbox
# shortcut: sv cl
sv claude
# Run OpenAI Codex in the sandbox
# shortcut: sv co
sv codex
# Run OpenCode in the sandbox
# shortcut: sv o
sv opencode
# Run Google Gemini in the sandbox
# shortcut: sv g
sv geminiOpenCode, pi and Muse Code appear as launch targets but not in the description; Cursor Agent appears in the description but has no `sv` subcommand. Metadata written before a feature list catches up is ordinary drift, but it has a direct cost here: the description is the first thing a reader sees and the natural assumption is that every agent named there works the same way. Test the named agent before assuming parity.
The pattern for adding your own binary is the `-x` option, which is documented as the way to run other sandboxed applications inside sandvault. That is the answer for anything not in the six, including Cursor if it is still supported at all.
Default installs agents through Homebrew on the host, -N moves the install inside
Where the agent binary lives is a deliberate choice with a flag. By default SandVault installs AI tools via Homebrew on the host side, so the executable being invoked from inside the sandbox was placed there by the host's package manager. `--native-install`, short form `-N`, moves the install inside the sandbox so each tool installs itself there:
# Install and run Claude Code natively
sv --native-install claude
sv -N claude
# Works with all AI agents
sv -N codex
sv -N opencode
sv -N gemini
sv -N pi
sv -N museThe six native paths split by vendor convention. Claude Code, OpenCode and Muse Code come from piped shell installers of the form `curl -fsSL https://claude.ai/install.sh | bash`, `curl -fsSL https://opencode.ai/install | bash` and `curl -fsSL https://dev.meta.ai/install.sh | bash`. Codex, Gemini and pi come from global npm installs: `@openai/codex`, `@google/gemini-cli` and `@earendil-works/pi-coding-agent`.
That split is worth noting for supply-chain reasons rather than convenience. Under `-N` the sandbox account itself fetches and executes remote install scripts, so the code that enters the sandbox arrives over the network at first run. Tools are installed on first run and reused on later runs, which means the pinned state of your sandbox agents is whatever those scripts produced on one particular day, with no version recorded anywhere in the project. Make it the default deliberately if you want it:
export SANDVAULT_ARGS="--native-install"Leaving it off keeps the host's Homebrew copy as the single version across sandboxes.
authorized_keys.d under codeofhonor is the real source of SSH access
SSH mode runs the same command through `ssh [email protected]` instead of impersonation, selected with `-s` or `--ssh`. The useful design decision is not the transport but the key management. Adding a key is a file copy, and the file list is authoritative:
cp ~/.ssh/id_ed25519_laptop.pub ~/.config/codeofhonor/sandvault/authorized_keys.d/laptop
sv buildNote the directory prefix. It is `codeofhonor`, not the project name, so the configuration lives under a namespace you would not guess from the tool's name.
Four behaviours follow from making those files the source of truth, and they are all documented rather than inferred. SandVault regenerates the sandvault user's `authorized_keys` from that directory plus its own key on every run, so deleting a file revokes that key on the next invocation rather than leaving a stale entry behind. Files that are not SSH public keys are ignored with a warning. A private key left in the directory stops the build instead of being copied into the sandbox, which is the right failure and an easy one to trigger by accident with a stray `.pub`-less file. Because regeneration happens every run, `sv` doubles as the apply command, and `sv build` is the explicit way to force it.
The same account serves the tmux or screen workflow, so long-running agent sessions survive a local disconnect when you use `--ssh`.
The clone target path and the wired remote path disagree
Cloning a repository into the sandbox is the entry point for real work, and its two documented paths do not match. The annotated example says sv-clone places the clone in `/Users/Shared/sv-$USER/re`:
sv shell /Users -- pwd # output: /UsersThe remote-wiring description, one paragraph later, says the local repository gets a remote named `sandvault` pointing at `/Users/Shared/sv-$USER/repos/<git-repository>`. That is `re` in one place and `repos` in the other, for the same operation in the same tool. It is the kind of discrepancy you find when reading source rather than prose, and here it sits in the documentation.
The mechanics of the wiring are worth stating because they change your workflow. A remote named `sandvault` is created or updated on your local repository, which lets you run `git fetch sandvault` from the original checkout to pull commits made inside the sandbox. That makes the sandbox the place work happens and the original repository a mirror of it, which is the reverse of the usual arrangement and the detail that makes the isolation useful: the agent commits inside, you review outside.
Arguments pass through after `--`, so a clone can start a session in one command:
sv-clone ~/src/my-app -- claude -- --model opusUse a full or relative path with a directory name for local clones. `sv-clone --help` carries the rest, including `-k` and `-w` to provision a per-repository deploy key, so the push side is scoped per repo rather than sharing the sandbox account's identity.
SANDVAULT_ARGS prepends silently, and the git install path needs editing before you run it
One environment variable shapes every invocation. `SANDVAULT_ARGS` supplies default arguments that are prepended to the command line, so the following two calls are equivalent:
export SANDVAULT_ARGS="--verbose --ssh"`sv claude` then behaves as `sv --verbose --ssh claude`. Prepending is the mechanism to understand: an argument in this variable lands before your own, and any shell quoting you apply to it is handled by your shell profile rather than by `sv`. The documentation on that point breaks off mid-sentence, so treat the exact quoting rules as undocumented and quote accordingly.
The git install route needs a manual step the Homebrew route does not:
# Clone the repository
git clone https://github.com/webcoyote/sandvault
# Option 1: add the sandvault directory to your path
export PATH="$PATH:/path/to/where/you/cloned/sandvault"
# Option 2: add to your shell configuration for easy access
echo >> ~/.zshrc 'alias sv="/path/to/where/you/cloned/sandvault/sv"'
echo >> ~/.bashrc 'alias sv="/path/to/where/you/cloned/sandvault/sv"'Both lines append with `>>`, unconditionally, for both shells, and they append the placeholder path verbatim. Edit the path before running the block, or your profile files will contain an alias pointing at a directory that does not exist. Running it twice appends a second copy. And the `export PATH` on the line above is scoped to that one shell session, so it is the alias or a profile file that actually persists.
Removing it later is `sv uninstall`, described as complete removal. There are no GitHub releases to pin against, so whatever Homebrew serves you is what you get, and the last push to main was 2026-09-14.
Editorial conclusion
Use SandVault when you want an agent to work on real files with a real shell and no VM overhead, and accept the SSH account, the per-repo deploy keys and the host-side Homebrew install as part of the cost. Do not treat the printed profile as an audited boundary: /Users/Shared is writable inside a rule that denies /Users, and /Volumes/Macintosh HD is writable inside a rule that denies /Volumes. Verify first by running `sv shell /Users -- pwd` from inside the sandbox and checking what the agent can actually read.
Frequently asked questions
Does SandVault sandbox AI agents on macOS without a VM?
Yes. It runs shell commands and AI agents as a limited user account layered with sandbox-exec, and describes itself as a lightweight alternative to application isolation using virtual machines, with instant user switching and no VM overhead.
Can SandVault run GUI applications inside the sandbox?
The documentation states it is not possible to run GUI applications from within the sandbox, while the same page advertises sandbox access to Chrome, Lightpanda and the iOS Simulator. Where that boundary falls for browser and iOS automation is not spelled out.
What can an AI agent access when running under SandVault?
The stated model is no access to your home directory, standard user privileges, no ability to modify system files, and no access to mounted drives. The printed profile lists /Users/Shared/sv-$USER and /Users/sandvault-$USER as writable, /usr, /bin, /etc and /opt as readable, and other user directories as no access.
How does SandVault manage SSH keys for the sandbox user?
Drop each public key into its own file under ~/.config/codeofhonor/sandvault/authorized_keys.d/, then run any sv command to apply it. SandVault regenerates the sandvault user's authorized_keys from those files plus its own key on every run, so deleting a file revokes that key, non-key files are ignored with a warning, and a private key stops the build.
Where does SandVault install the AI coding agents it launches?
By default via Homebrew on the host side. With --native-install, short form -N, each tool installs itself inside the sandbox: Claude Code, OpenCode and Muse Code from piped curl installers, and Codex, Gemini and pi from global npm installs of @openai/codex, @google/gemini-cli and @earendil-works/pi-coding-agent.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/webcoyote-sandvault)