SandVault (sv) manages a limited user account to sandbox shell commands and AI agents, providing a lightweight alternative to application isolation using virtual machines.
$ npx -y skills add webcoyote/sandvault --agent claude-code
Run the curl in your terminal, the rest in Claude Code.
Repo: webcoyote/sandvault
What's inside

/Users/Shared/sv-$USERsandbox-execsv uninstallxcodebuild or swift see Sandboxing xcodebuild and swift for details.-x option. See Sandboxing other apps for details.SandVault has limited access to your computer:
- writable: /Users/Shared/sv-$USER -- only accessible by you & sandvault-$USER
- writable: /Users/sandvault-$USER -- sandvault's home directory
- readable: /usr, /bin, /etc, /opt -- system directories
- no access: /Users/* -- other user directories
- writable: /Volumes/Macintosh HD -- accessible as per file permissions
- no access: /Volumes/* -- cannot access mounted/remote/network drives
Install via Homebrew:
brew install sandvault
Install via git:
# 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"'
# 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 gemini
# Run pi in the sandbox
# shortcut: sv p
sv pi
# Run command shell in the sandbox
# shortcut: sv s
sv shell
The default mode for sandvault runs commands as a limited user (basically sudo -u sandbox-$USER COMMAND). Sandvault also configures the limited sandvault account so that you can run commands via SSH (ssh sandbox-$USER@127.0.0.1), and everything works the same. Use the -s or --ssh option to use SSH mode with sv, or use tmux or screen for users so inclined.
# Run using impersonation
# sv COMMAND
sv gemini
# Run using ssh
# sv -s/--ssh COMMAND
sv --ssh gemini
# Run AI agent with optional arguments
# Usage:
# sv <agent> [PATH] [-- AGENT_ARGUMENTS]
# Example:
sv gemini -- --continue
# Run shell command in sandvault and exit
# Usage:
# sv shell [PATH] -- [SHELL_COMMAND]
# Example:
sv shell /Users -- pwd # output: /Users
# Send input via stdin
# Usage:
# <producer> | sv shell [PATH] [-- SHELL_COMMAND]
# Examples:
echo "pwd ; exit" | sv shell /Users # output: /Users
echo ABC | sv shell -- tr 'A-Z' 'a-z' # output: abc
cat PROMPT.md | sv gemini
# Clone local/remote Git repository into /Users/sandvault-$USER/repositories/<git-repository> and open there
# Usage:
# sv <agent|shell> --clone URL_OR_LOCAL_PATH [-- AGENT_OR_SHELL_ARGS]
# Examples:
sv codex --clone https://github.com/webcoyote/sandvault.git
sv codex -c ~/src/my-app
sv shell --clone https://github.com/webcoyote/sandvault.git
sv shell -c ../my-app
Use a full or relative path with a directory name for local clones.
For local Git repositories, sandvault also wires remotes:
- Your local Git repository gets/updates remote `sandvault` -> `/Users/sandvault-$USER/repositories/<git-repository>`
- This lets you run `git fetch sandvault` from the original local Git repository to pull commits made in the sandvault Git repository.
By default, SandVault installs AI tools via Homebrew on the host side. With --native-install (-N), tools are instead installed inside the sandbox using their own installers:
curl -fsSL https://claude.ai/install.sh | bashnpm install -g @openai/codexcurl -fsSL https://opencode.ai/install | bashnpm install -g @google/gemini-clinpm install -g @earendil-works/pi-coding-agentTools are installed on first run and reused on subsequent runs.
# 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
To make native install the default, set SANDVAULT_ARGS:
# Add to your shell profile (~/.zshrc, ~/.bashrc, etc.)
export SANDVAULT_ARGS="--native-install"
# Now 'sv claude' uses native install automatically
sv claude
Set SANDVAULT_ARGS to supply default arguments that are prepended to the command line:
# Add to your shell profile (~/.zshrc, ~/.bashrc, etc.)
export SANDVAULT_ARGS="--verbose --ssh"
# Now these are equivalent:
sv claude
sv --verbose --ssh claude
Shell quoting is supported, so arguments with spaces work:
export SANDVAULT_ARGS='--clone "my project"'
Explicit command-line arguments are appended after SANDVAULT_ARGS, so they are processed afterwards.
# Build sandvault but do not run a command
sv build
sv b
# Rebuild sandvault, including updating all file permissions and ACLs in the shared volume
sv build --rebuild
sv b -r
# Fix permissions when using a restrictive umask (e.g. 077)
sv --fix-permissions
sv --fix-permissions build
# Uninstall sandvault (does not delete files in the shared volume)
sv uninstall
# Misc commands
sv --version
sv --help
agentsview Integrationagentsview is a dashboard that aggregates session history, search, and cost tracking across AI coding agents (Claude Code, Codex, OpenCode, Gemini, pi). If you have agentsview installed on the host, sv-agentsview-setup mirrors sandbox session data so it appears alongside your host-side sessions.
# Detect agentsview, prompt to opt in, and configure
sv-agentsview-setup
Then run agentsview serve and you'll see your sandvault AI sessions included in the agentsview dashboard.
In addition to running in a different macOS user account, sandvault also runs applications using macOS sandbox-exec, which further limits what resources are accessible.
Some applications, like swift, already run inside a sandbox. Because macOS does not support nested (i.e. recursive) sandboxes, these applications fail to run.
Read on for solutions.
For swift (and xcodebuild, which runs swift), you can set the following variables in your build scripts to run inside sandvault:
For swift:
ARGS=()
# Disable sandboxing when running inside sandvault to avoid nested sandbox-exec
if [[ -n "${SV_SESSION_ID:-}" ]]; then
ARGS+=(--disable-sandbox)
fi
swift build "${ARGS[@]}" "$@"
For xcodebuild:
ARGS=()
# Disable sandboxing when running inside sandvault to avoid nested sandbox-exec
if [[ -n "${SV_SESSION_ID:-}" ]]; then
export SWIFTPM_DISABLE_SANDBOX=1
export SWIFT_BUILD_USE_SANDBOX=0
ARGS+=("-IDEPackageSupportDisableManifestSandbox=1")
ARGS+=("-IDEPackageSupportDisablePackageSandbox=1")
# shellcheck disable=SC2016 # Expressions don't expand in single quotes # that is intentional
ARGS+=('OTHER_SWIFT_FLAGS=$(inherited) -disable-sandbox')
fi
xcodebuild \
build \
"${ARGS[@]}" \
...
If the app you intend to run does not support disabling the use of sandbox-exec like xcodebuild and swift you can run sandvault without sandbox-exec:
# Disable use of sandbox-exec (app still runs as sandvault user) using -x / --no-sandbox
sv -x claude
sv --no-sandbox codex
sv --no-sandbox shell $HOME/projects/my-app -- xcodebuild ...
Disabling sandbox-exec has the following security implications:
/Volumes/...)o+w (0002) file permissions# To find all files on your computer that are "world writable" (perms: `o+w` / 0002)
# run this command from your account (not in sandvault):
find / \
-path "/Users/sandvault-$USER" -prune \
-o -path "/Users/sv-$USER" -prune \
-o -perm -o=w -print 2>/dev/null
If your sandbox is misbehaving you can fix it with a rebuild or uninstall/reinstall. They're both safe and will not delete files in the shared sandbox folder.
# Force rebuild
sv --rebuild build
# Uninstall then reinstall
sv uninstall
sv build

If you see a security popup above, it may be because files in the shared sandvault directory don't have the correct ACLs, which occurs when another user's files are copied into the sandvault shared directory (/Users/Shared/sv-$USER). This can be corrected by running the rebuild command sv --rebuild build, or adding the rebuild flag to any command, e.g. sv -r shell. This only needs to be done once.
If you see "Permission denied" errors when running sv, your shell may have a restrictive umask (e.g., 077 instead of the default 022). Check with:
umask
SandVault detects this and warns you. To fix it for the current session, add --fix-permissions:
# Fix permissions (standalone or with build)
sv --fix-permissions
sv --fix-permissions build
If you previously installed Homebrew under a restrictive umask, you may also need to fix its directory permissions:
sudo chmod -R o+rX /opt/homebrew
If you're using sandvault, you're probably the type of person who also sets custom shell configuration files, and you'd be disappointed if you had to use default zsh while running sv shell. Here's how to configure your custom configuration for sandvault:
sandvault build, sandvault shell, sandvault claude, etc..zshrc, .zprofile, etc.) to /Users/Shared/sv-${USER}/user/.Next time you run sandvault, your files will be copied to the sandvault user home directory, and your zsh configuration files will be sourced:
.zshenv โ .zprofile โ .zshrc
NOTE: .zlogin and .zlogout not supported
Note: Earlier versions of sandvault supported configuration files in
guest/home/user/, which didn't work for Homebrew installations. Consequently, this is no longer supported, and you'll get an error message asking you to moveguest/home/userto/Users/Shared/sv-${USER}/user/.
SandVault supports a headless browser for automation from within the sandbox. The browser runs on the host side and the sandbox connects to it via the Chrome DevTools Protocol (CDP) over localhost. Two backends are supported: Chrome (default) and Lightpanda.
# Launch with browser support (Chrome by default)
sv --browser claude
sv --browser shell
# Explicit backend selection
sv --chrome claude # same as --browser
sv --lightpanda claude # use Lightpanda instead of Chrome
Inside the sandbox, the SV_BROWSER_ENDPOINT environment variable contains the CDP endpoint URL (e.g. http://127.0.0.1:52858).
// Playwright
const browser = await chromium.connectOverCDP(process.env.SV_BROWSER_ENDPOINT);
// Puppeteer
const browser = await puppeteer.connect({ browserURL: process.env.SV_BROWSER_ENDPOINT });
From the host, you can query the endpoint URL:
# Prints the CDP endpoint URL (or errors if browser is unavailable)
sv --endpoint
See also ./tests/browser/*.js for examples of using Playwright and Puppeteer. See guest/home/bin/prompts/browser.md for prompt.
localhost.SandVault can expose the iOS Simulator to sandboxed AI agents for iOS app testing. The simulator runs on the host (it is a GUI app and cannot run inside the sandbox), and an HTTP bridge on localhost translates sandbox-side requests into xcrun simctl and iosef invocations.
# Launch with iOS Simulator support
sv --ios claude
sv --ios shell
Add --ios-gui to also show the Simulator.app window โ useful when debugging interactively or watching an agent's actions:
sv --ios-gui shell
Simulator.app is left running on session exit so that other simulators (yours or other tools') aren't disrupted.
Inside the sandbox, the SV_IOS_SIMULATOR_ENDPOINT environment variable points at the HTTP bridge (e.g. http://127.0.0.1:52861).
# Check if simulator is ready
curl $SV_IOS_SIMULATOR_ENDPOINT/ready
# Read the accessibility tree
curl $SV_IOS_SIMULATOR_ENDPOINT/describe
# Tap a button by accessibility name
curl -X POST -H 'Content-Type: application/json' \
-d '{"name":"Sign In"}' \
$SV_IOS_SIMULATOR_ENDPOINT/tap
# Launch an app by bundle id
curl -X POST -H 'Content-Type: application/json' \
-d '{"bundle_id":"com.apple.Preferences"}' \
$SV_IOS_SIMULATOR_ENDPOINT/launch
# Save a screenshot as JPG (point resolution)
curl -o low_res.jpg $SV_IOS_SIMULATOR_ENDPOINT/view
# Save a screenshot as PNG (pixel resolution)
curl -o high_res.png $SV_IOS_SIMULATOR_ENDPOINT/view_pixels
See guest/home/bin/prompts/ios-simulator.md for the full list of endpoints. This file is automatically included in your AI agent prompt when using --ios/--ios-gui. See tests/ios-simulator/scripts/tests for a runnable example.
sandvault-<session-id>) is created and booted on the host for each --ios session, and deleted on exit.helpers/sv-ios-bridge) listens on a dynamic localhost port and fronts a whitelisted set of iosef and xcrun simctl subcommands..app bundles passed to /install must live under /Users/Shared/ (so the sandbox user can already access them); the bridge rejects paths outside that tree.uv, which installs iosef). sandvault installs both automatically on first use.TL;DR: Sorry, macOS security limitations prevent this from working.
It would be great to be able to run GUI applications (e.g. browsers, Claude Desktop) in the sandbox account to limit their access to main account resources.
The issue seems to be that an application cannot report to a WindowServer that's owned by a different user.
Internet posts suggest it's possible using sudo su, sudo launchctl asuser, and sudo launchctl bsexec, but those answers are from long ago and it seems likely that Apple improvements to macOS security have closed those doors.
In the event you do find a solution, send a PR please :)
After exploring Docker containers, Podman, sandbox-exec, and virtualization, I needed something that:
--dangerously-skip-permissions--dangerously-bypass-approvals-and-sandboxOPENCODE_PERMISSION='{"*":"allow"}'--yolo--approve to trust project-local files (pi needs no permission bypass)xcrun simctl, and iosef)SandVault uses macOS's Unix heritage and user account system to create a simple but effective sandbox.
Apache License, Version 2.0
SandVault Copyright ยฉ 2026 Patrick Wyatt
See LICENSE.md for details.
We welcome contributions and bug reports.
See CONTRIBUTORS.md for the list of contributors to this project.
This project builds on the great works of other open-source authors:
... as well as GNU, BSD, Linux, curl, Git, Sqlite, Node, Python, netcat, jq, and more. "We stand upon the shoulders of giants."
.claude/
commands/
release.md
.editorconfig
.envrc
.gitattributes
.github/
scripts/
bump-version
generate-release-data
patch-changelog
tag-version
workflows/
ci.yml
homebrew.yml
.gitignore
.shellcheckrc
AGENTS.md
CHANGELOG.md
CONTRIBUTORS.md
docs/
ios-simulator-tools.md
superpowers/
specs/
2026-05-03-agentsview-export-design.md
2026-05-03-agentsview-export-plan.md
guest/
home/
.zprofile
.zshenv
.zshrc
bin/
claude
codex
gemini
opencode
pi
prompts/
browser.md
ios-simulator.md
sv-tool-prompts.sh
configure
helpers/
agentsview-config.py
agentsview-paths.sh
sv-ios-bridge
sv-ios-pick-device
sv-logging.sh
tomli_w.py
tomli.py
LICENSE.md
playground/
README.md
sandbox-testbed
README.md
scripts/
check-autobump
kill-sandvault
tests
validate
skills/
sandvault/
sv/
scripts/
find-terminal-app.sh
launch-in-terminal.sh
SKILL.md
sv
sv-agentsview-setup
sv-clone
sv-visible-browser
tests/
chrome/
package.json
pnpm-lock.yaml
scripts/
tests
test-playwright.js
test-puppeteer.js
clean
ios-simulator/
scripts/
tests
lightpanda/
package.json
pnpm-lock.yaml
scripts/
tests
test-playwright.js
test-puppeteer.js
swift/
.gitignore
Package.swift
scripts/
build
tests
Sources/
swift/
swift.swift
tests
xcodebuild/
.gitignore
scripts/
build
tests
XcodebuildApp/
XcodebuildApp.xcodeproj/
project.pbxproj
main.swift
TODO.mdFAQ
sandvault is a Claude Code plugin with 1 hand-picked skill for security work, indexed on Flowy. Install it with the command on its page. It includes sv. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.