Metadata-Version: 2.1
Name: sbrun
Version: 0.0.3
Summary: Run commands under the macOS sandbox with writes confined to the working tree
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# sbrun

`sbrun` launches commands under the macOS sandbox and only allows writes beneath the directory where `sbrun` was started.

## Install

Install the latest release:

```sh
curl -fsSL https://raw.githubusercontent.com/AnswerDotAI/sbrun/main/install.sh | bash
```

Install from PyPI into a Python environment:

```sh
pip install sbrun
```

The installer:

- resolves the latest macOS arm64 release tarball via `https://latest.fast.ai/latest/AnswerDotAI/sbrun/.gz`
- downloads that tarball and verifies it with `SHA256SUMS`
- installs `sbrun` and `sbrun.pl` into `bin`
- installs the default user config to `$XDG_CONFIG_HOME/sbrun/config` or `~/.config/sbrun/config` only if no config is already present
- defaults to `/opt/homebrew` when it exists, otherwise `/usr/local`

The PyPI wheel is macOS arm64 only. It installs the native `sbrun` binary into
your Python environment's `bin/`. On first run, `sbrun` seeds
`$XDG_CONFIG_HOME/sbrun/config` or `~/.config/sbrun/config` with the built-in
default config if you do not already have one.

You can override the install location:

```sh
curl -fsSL https://raw.githubusercontent.com/AnswerDotAI/sbrun/main/install.sh | PREFIX=/usr/local bash
```

You can also pin a release:

```sh
curl -fsSL https://raw.githubusercontent.com/AnswerDotAI/sbrun/main/install.sh | SBRUN_INSTALL_VERSION=0.1.0 bash
```

## Use

Interactive shell:

```sh
cd /path/to/project
sbrun
```

Run a command directly:

```sh
cd /path/to/project
sbrun ipython --profile-dir=.ipython profile list
```

Allow writes to an extra directory:

```sh
cd /path/to/project
sbrun -w /tmp python3 -c 'open("/tmp/sbrun-demo", "w").write("ok")'
```

Set specific environment variables to project-local directories:

```sh
cd /path/to/project
sbrun -e IPYTHONDIR -e MPLCONFIGDIR ipython
```

Use the long form when you prefer:

```sh
cd /path/to/project
sbrun --envdir=XDG_CACHE_HOME --envdir=XDG_STATE_HOME python3 app.py
```

Run a shell snippet:

```sh
cd /path/to/project
sbrun -lc 'touch ok.txt && echo hello'
```

You can combine `sbrun` options with shell mode:

```sh
cd /path/to/project
sbrun -w /tmp -lc 'echo hi > /tmp/hi.txt'
```

If you need to stop parsing `sbrun` options and force command mode, use `--`:

```sh
cd /path/to/project
sbrun -w /tmp -- ipython --profile-dir=/tmp/ipython
```

The Perl variant is used the same way:

```sh
cd /path/to/project
./sbrun.pl ipython --profile-dir=.ipython profile list
```

Help is available in both variants:

```sh
sbrun --help
sbrun --version
./sbrun.pl --help
./sbrun.pl --version
```

## Properties

- reads are broadly allowed, writes are confined to the launch directory tree
- with no arguments, `sbrun` launches your `$SHELL` as an interactive login shell
- with arguments, `sbrun` runs that command directly, preserving flags and argv
- if the first argument starts with `-`, `sbrun` passes those flags to your shell
- `-w PATH` or `--writable PATH` adds an extra writable file or directory; you can repeat it
- `-e VAR` or `--envdir VAR` sets `VAR` to `.sbrun/VAR`; you can repeat it
- `HOME` stays your real home directory when one is available
- `TMPDIR` is set to `/tmp`
- the shell's normal history file is writable by default
- `SBRUN_ACTIVE=1` is exported in the child environment so shells can show a sandbox indicator if desired
- extra file descriptors `>= 3` are closed before entering the sandbox
- on macOS, if stdout or stderr is redirected to a regular file outside the
  allowed writable paths, `sbrun` refuses to start unless you set
  `SBBASH_ALLOW_STDIO_REDIRECTS=1`

For example, you can use `SBRUN_ACTIVE` in your shell prompt logic instead of
having `sbrun` override `PS1`, `PROMPT_COMMAND`, or similar shell-specific
customizations.

Development, build, test, and release notes live in `DEV.md`.

## Config

Global extra writable paths can be set in:

- `$XDG_CONFIG_DIRS/.../sbrun/config`
- `$XDG_CONFIG_HOME/sbrun/config`
- `~/.config/sbrun/config` when `XDG_CONFIG_HOME` is unset

Use one entry per line:

```ini
writable_path=/tmp
writable_path=~/scratch
optional_writable_path=~/.cache
```

`writable_path=...` is required and errors if the path does not resolve to an
existing regular file or directory.
`optional_writable_path=...` is ignored when the path does not resolve to an
existing regular file or directory, which is useful for shared default configs.
For compatibility, `writable_dir=...` and `optional_writable_dir=...` are also
accepted.

Configured paths and `-w/--writable` paths are combined. System config is
loaded first, then user config, then CLI flags.

`-e/--envdir VAR` is CLI-only. Each requested variable is set to
`.sbrun/VAR`, and those directories are created on demand inside the launch
directory.

## Envdir

`-e VAR`, `--envdir VAR`, and `--envdir=VAR` all mean the same thing.

- `VAR` must be a valid environment variable name: `[A-Za-z_][A-Za-z0-9_]*`
- `sbrun` creates `.sbrun/` only when at least one envdir flag is used
- each requested variable gets a directory at `.sbrun/VAR`
- the child process sees `VAR` set to that directory, even if `VAR` already had a different value
- repeated `-e/--envdir` flags are fine; duplicate names are ignored after the first
- envdir settings are CLI-only and are not read from config files

This is mainly useful for tools that want a writable state or cache directory
without granting broad write access to your real home directory. Typical
examples are `IPYTHONDIR`, `MPLCONFIGDIR`, `XDG_CACHE_HOME`, and
`XDG_STATE_HOME`.

The installed default global config includes a practical allow-list of common
user state/cache locations such as:

- `/tmp`
- `~/.config`
- `~/.cache`
- `~/.local/share`
- `~/.local/state`
- `~/.ipython`
- `~/.jupyter`
- `~/Library/Caches`

Edit the global config or your user config to tighten or extend that list.
