Sandboxes
A macOS app for managing Docker Sandboxes.
Features
- See the details of your sandboxes, including resource usage, network traffic, and the network and file system rules that apply to them.
- Create new sandboxes quickly and easily.
- Create and manage sandbox environment files and use them to create sandboxes.
- Start, stop and remove sandboxes.
- Publish a sandbox's ports on your Mac when creating it or later, open them in your browser, and unpublish them.
- Open an interactive shell inside a sandbox in your terminal.
- Run a sandbox's agent in your terminal, or open any sandbox in Claude Desktop.
- Get notified when an agent makes a network request that needs approval, and approve or deny across the current or all sandboxes.
- Manage global and sandbox specific secrets.
- Manage global and sandbox specific network rules.
- Manage MCP servers and configure sandboxes to use them.
- Manage agent skills and choose how sandboxes share them.
- View and change
sbx settings, such as proxies and kit policy, and restart the daemon when a change needs it.
- Install, upgrade and sign in to
sbx from the app, and run diagnostics to find problems with your setup.
- Install new versions of Sandboxes from inside the app.
Getting started
Requirements
- macOS 26 or later
- The
sbx CLI. Install it with Homebrew: brew install docker/tap/sbx
sbx v0.48.0 or later to unload MCP servers from a sandbox.
sbx v0.50.0 or later to choose and show kit host mounts.
- An
sbx with the experimental sbx skills commands (checked against v0.49.1) to manage skills.
- Claude Desktop v2.110.0 or later, to open sandboxes in Claude Desktop. Create the sandbox with the Claude Code agent so it has Anthropic credentials and network access.
Install
Run this in Terminal to install the latest release into Applications, or to update an existing install:
curl -fsSL https://geofflamrock.github.io/Sandboxes/install.sh | bash
The script is site/install.sh. It downloads the latest release's DMG, quits Sandboxes if it's running and copies the app into /Applications, asking for your password only if you can't write there. A download made with curl doesn't have the quarantine flag, so you can skip Allow the app to run.
Or install it by hand:
- Download the latest
Sandboxes-<version>.dmg from GitHub releases.
- Open the DMG and drag Sandboxes onto Applications.
Allow the app to run
If you installed the app by hand: Sandboxes isn't notarized by Apple yet, so the first time you open it macOS blocks it with a message that it can't check the app for malicious software. To allow it:
- Open Sandboxes from Applications and click Done on the warning.
- Open System Settings › Privacy & Security.
- Scroll down to the message that Sandboxes was blocked and click Open Anyway.
- Click Open Anyway again and enter your password when asked.
You only need to do this once. Updates installed from inside the app aren't blocked. Alternatively, clear the quarantine flag in Terminal after copying the app to Applications:
xattr -dr com.apple.quarantine /Applications/Sandboxes.app
Or download the release with the GitHub CLI, which doesn't set the quarantine flag at all:
gh release download --repo geofflamrock/Sandboxes --pattern '*.dmg'
Find sbx
The app looks for sbx on your login shell's PATH. If it can't find it, set its location in Settings › sbx.
Contributing
Building locally
The Xcode project is generated from project.yml with XcodeGen:
brew install xcodegen
xcodegen generate
open Sandboxes.xcodeproj
Local builds show a "Local" badge at the bottom of the sidebar and have version 0.0.0, a placeholder in project.yml (MARKETING_VERSION). CI sets the real version (see Test builds and Releases).
Update checks
Release builds use Sparkle to check for and install updates. Every 6 hours, Sparkle reads appcast.xml from the latest GitHub release, so drafts and pre-releases are ignored. When it lists a newer build, the sidebar and the menu bar menu show it, and clicking it opens Sparkle's window with the release notes and Install Update. Sandboxes › Check for Updates… and Settings › General check straight away. Sparkle downloads the DMG, checks its EdDSA signature against the public key in the app (SUPublicEDKey), replaces the app and reopens it. Updates installed this way aren't quarantined, so macOS doesn't block them.
Other builds don't check. To try it out in one, open Settings › Development and turn on Check for updates in this build. Sparkle compares build numbers, and builds from CI are numbered after the releases before them, so only a local build (build 1) sees the latest release as an update. You can also read another repository's appcast there.
These settings are stored as the UpdateCheckForceEnabled and UpdateCheckRepository defaults, so they also work as launch arguments:
open Sandboxes.app --args -UpdateCheckForceEnabled YES
Release builds ignore them, so values left over from testing a build don't affect a release.
App icon
The app icon is Design/AppIcon.icon, an Icon Composer file. Its layers are generated by scripts/generate-app-icon.py, which also writes a flattened preview to Design/AppIcon.svg and the artwork without its background, for the website, to Design/AppIconArtwork.svg. In the website's copy, the robot peeks out of the sand when the page loads, unless Reduce Motion is on. To change the artwork, edit the script, then run it from the repository root:
python3 scripts/generate-app-icon.py
To change the background fill, or how the layers are grouped, scaled or lit, open Design/AppIcon.icon in Icon Composer.
Website
The website at geofflamrock.github.io/Sandboxes is the static page in site/, along with the install script. The Website workflow (.github/workflows/pages.yml) publishes it to GitHub Pages when site/ or the icon SVGs in Design/ change on main, copying in Design/AppIcon.svg (the favicon) and Design/AppIconArtwork.svg (the header's artwork). When it deploys, it fills in the release button with the latest release's version and page, such as "Latest release: v0.6.1", so the button doesn't change after the page loads. Publishing a release redeploys the site once the DMG is attached. Before the first release, and in a local preview, the button says "Latest release" and links to the latest release page.
To preview the site locally at http://localhost:8000:
scripts/preview-site.sh
It assembles _site/ from symlinks to site/ and the icon SVGs, so edits show up without running it again. With Node installed, it serves the site with Browsersync, which reloads the page when a file changes. Otherwise it uses Python's web server, and you reload yourself. Pass a port to use another one, such as scripts/preview-site.sh 8080.
Test builds
Every pull request and every push to main uploads a DMG as the Sandboxes artifact on the workflow run. Test builds are ahead of the latest release, so their version is the next minor version after it: when the latest release is v0.4.0, they're 0.5.0. Pull request builds are named Sandboxes-<version>-pr<number>-<commit>.dmg and main builds Sandboxes-<version>-build<run number>-<commit>.dmg, where <commit> is the short commit SHA. The app shows a badge such as "PR #12 · abc1234" or "main · abc1234" at the bottom of the sidebar so you can tell you're not on a release. Sandboxes › About Sandboxes shows the version, build number and, for test builds, the PR or branch and the commit.
To install the latest test build, run scripts/install-test-build.sh from a clone of the repo. It needs the GitHub CLI, signed in. It downloads the build, quits Sandboxes if it's running, replaces /Applications/Sandboxes.app and opens it.
scripts/install-test-build.sh # latest main build
scripts/install-test-build.sh 12 # latest build of PR #12
Releases
Publishing a GitHub release builds the app with the release's version and attaches Sandboxes-<version>.dmg and appcast.xml to it, then redeploys the website so its release button shows the new version. Tag releases v1.2.3 (or 1.2.3). Release builds show their version, such as "v1.2.3", at the bottom of the sidebar. Builds are ad-hoc signed and not notarized.
The appcast is what update checks read. Sparkle's generate_appcast writes it with the release's notes and an EdDSA signature for the DMG, made with the private key in the SPARKLE_PRIVATE_KEY repository secret. The app checks the signature with the public key in project.yml (SPARKLE_PUBLIC_ED_KEY). To set up the keys, download Sparkle from its releases and run:
./bin/generate_keys
It saves the private key in your Keychain and prints the public key to put in project.yml (./bin/generate_keys -p prints it again). Then export the private key, copy it into the secret and delete the export:
./bin/generate_keys -x sparkle-private-key && gh secret set SPARKLE_PRIVATE_KEY --repo geofflamrock/Sandboxes < sparkle-private-key && rm -P sparkle-private-key
Keep a backup of the private key. Without it, existing installs can't install any later release, and users have to download one from GitHub again. A release fails to build if either key is missing or they don't match.
Commands the app runs
| Action | Command |
|---|
| List | sbx ls --json |
| Installed version (home page and Settings › sbx) | sbx version, compared with the latest release from the GitHub API for docker/sbx-releases |
| Install sbx (home page) | brew install docker/tap/sbx, when you click Install sbx |
| Upgrade sbx (home page and Settings › sbx) | brew upgrade docker/tap/sbx (or the cask it was installed from, such as sbx@rc), when you click Upgrade sbx |
| Diagnostics (home page and Settings › sbx) | sbx diagnose --json, at most every 5 minutes, straight away when sbx ls starts failing, or when you click Run Again in Settings › sbx or choose Help › Run sbx Diagnostics… |
| Start the daemon (home page) | sbx daemon start --detach, when the Daemon check fails and you click Start Daemon |
| Sign in to Docker (home page) | sbx login, when the Authentication check fails and you click Sign In. The app shows the one-time code and opens the sign-in page |
| Create | [DOCKER_SANDBOXES_ROOT_SIZE=SIZE] [DOCKER_SANDBOXES_DOCKER_SIZE=SIZE] [DOCKER_SANDBOXES_CLONED_WORKSPACE_SIZE=SIZE] sbx create [--name NAME] [--clone] [--cpus N] [--memory SIZE] [--env KEY=VALUE]... [--env-file FILE]... [--kit KIT]... [--kit-arg [HANDLE.]NAME=VALUE]... [--mount HOST:SANDBOX_PATH]... [--static-mcp SERVER]... [--publish [HOST_PORT:]SANDBOX_PORT]... [--deny-network HOST]... [--skills off|readonly|readwrite] WORKLOAD [PATH [PATH[:ro]]...]. An argument is aimed at its kit with the kit's handle only when another picked kit declares the same name. The disk sizes from Runtime › Disks and Workspace › Clone Size (Clone mode only) are set as SIZEg, or as typed for Custom, in that sbx create process's environment, not your shell's or the sandbox's, since sbx create has no flags for them |
| Review an environment file's plan | sbx env plan [--name NAME] [--clone] [--env-arg NAME=VALUE]... [FILE], with the name entered (the file's name: to start with), and --clone when you choose Clone for a workspace that doesn't set . With , it runs in the file's folder without FILE, so sbx adds . That's offered when the defaults file exists and the file is called , and not for a file in the environment files folder whose defaults would mount the project directory, which would be the file's own folder |
Agent and service logos are from Simple Icons (CC0).
Licence
Sandboxes is free and open source under the MIT licence.