Run commands under the macOS sandbox with writes confined to selected paths
Project description
sbrun
sbrun launches commands under the macOS sandbox and only allows writes beneath
the current directory tree plus paths you explicitly opt into.
The implementation is a single Rust crate:
- the
sbrunbinary is the CLI - the same crate also exposes a Python
sbrun.exec(...)API via PyO3 - the binary applies the sandbox directly through
libsandbox
Install
Install the latest macOS arm64 release:
curl -fsSL https://raw.githubusercontent.com/AnswerDotAI/sbrun/main/install.sh | bash
Build locally:
cargo build --release
Install the Python extension into an active virtualenv:
maturin develop --release
Use
Start an interactive login shell:
cd /path/to/project
sbrun
Run a command directly:
cd /path/to/project
sbrun python3 app.py
Run a shell snippet with your current $SHELL:
cd /path/to/project
sbrun -c 'touch ok.txt && echo hello'
Allow writes to an extra directory:
cd /path/to/project
sbrun --write /tmp -- python3 -c 'open("/tmp/sbrun-demo", "w").write("ok")'
Set environment variables to project-local directories:
cd /path/to/project
sbrun --env-dir IPYTHONDIR --env-dir MPLCONFIGDIR -- ipython
Remove selected variables from the child environment:
cd /path/to/project
sbrun --unset-env GITHUB_API_KEY --unset-env OPENAI_API_KEY -- python3 app.py
If the command itself starts with -, use -- to stop option parsing:
cd /path/to/project
sbrun -- -lc 'printf hello\n'
Help and version:
sbrun --help
sbrun --version
CLI
-w, --write PATH: allow writes to a regular file or directory; repeatable-d, --env-dir VAR: setVARto.sbrun/VAR; repeatable-u, --unset-env VAR: removeVARfrom the child environment; repeatable-c, --command STRING: run$SHELL -lc STRING--config PATH: load that TOML file and ignore the standard config locations--no-config: ignore config files entirely--: stop parsingsbrunoptions
Behavior:
- with no command,
sbrunlaunches your$SHELLas an interactive login shell - with
-c/--command,sbrunruns$SHELL -lc STRING - otherwise
sbrunexecs the given command directly SBRUN_ACTIVE=1is exported in the child environmentHOMEstays your real home directory when one is availableTMPDIRis set to/tmp- the shell history file is writable by default
- stdout/stderr redirected to regular files outside allowed writable paths are rejected unless
SBRUN_ALLOW_STDIO_REDIRECTS=1
For bash prompt logic, you can use SBRUN_ACTIVE without replacing an existing
PROMPT_COMMAND or PS1. Put this in ~/.bashrc:
sbrun_prompt_prefix() {
[[ ${SBRUN_ACTIVE:-} == 1 ]] || return
case $PS1 in
'🔒 '*) ;;
*) PS1="🔒 $PS1" ;;
esac
}
case "$(declare -p PROMPT_COMMAND 2>/dev/null)" in
"declare -a "*)
case " ${PROMPT_COMMAND[*]} " in
*" sbrun_prompt_prefix "*) ;;
*) PROMPT_COMMAND+=(sbrun_prompt_prefix) ;;
esac
;;
*)
case ";${PROMPT_COMMAND:-};" in
*";sbrun_prompt_prefix;"*) ;;
*) PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND; }sbrun_prompt_prefix" ;;
esac
;;
esac
If your login shell does not source ~/.bashrc, put the same snippet in
~/.bash_profile.
Config
sbrun reads TOML config from:
$XDG_CONFIG_DIRS/.../sbrun/config.toml$XDG_CONFIG_HOME/sbrun/config.toml~/.config/sbrun/config.tomlwhenXDG_CONFIG_HOMEis unset
--config PATH replaces those defaults with one explicit file. --no-config
skips config loading entirely.
Example:
version = 1
write = ["/tmp", "/Volumes/scratch"]
optional_write = [
"~/.cache",
"~/Library/Caches",
]
Rules:
versionmust be1when presentwriteentries are required and error if they do not resolveoptional_writeentries are ignored when they do not resolve- config paths must be absolute or start with
~/ env_dirandunset_envare CLI-only
The repo ships a practical default config in sbrun.default.toml.
Python
The Python API is intentionally minimal and follows the same exec model as the
CLI:
import sbrun
sbrun.exec(
["python3", "app.py"],
write=["/tmp"],
env_dir=["IPYTHONDIR"],
unset_env=["GITHUB_API_KEY"],
)
On success, sbrun.exec(...) does not return because it replaces the current
process image. On failure, it raises a Python exception.
Development
Build, test, and release notes live in DEV.md.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sbrun-0.0.5-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: sbrun-0.0.5-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 324.0 kB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8dd457b10dc0a3bf82bf3c9999ee8c979c207a97284045e8961eba43982dba8d
|
|
| MD5 |
7bc4b6e9be4685acdec9a3deb033e5b6
|
|
| BLAKE2b-256 |
03daf41690d1f29d20e18389fcdac1b3cad8488deb8619b0b021883da68f5ff9
|
Provenance
The following attestation bundles were made for sbrun-0.0.5-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on AnswerDotAI/sbrun
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sbrun-0.0.5-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
8dd457b10dc0a3bf82bf3c9999ee8c979c207a97284045e8961eba43982dba8d - Sigstore transparency entry: 1219456121
- Sigstore integration time:
-
Permalink:
AnswerDotAI/sbrun@c753e9957004403b245a2cea6a7c2369f5556864 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/AnswerDotAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c753e9957004403b245a2cea6a7c2369f5556864 -
Trigger Event:
push
-
Statement type: