Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
364 changes: 170 additions & 194 deletions README.md

Large diffs are not rendered by default.

116 changes: 53 additions & 63 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,68 @@
# Configuration

`sx` uses a layered configuration system with global, project, and CLI options.
`sx` uses a layered configuration system: global config, project config, CLI flags.

## Configuration Files
## Global Config (`~/.config/sx/config.toml`)

### Global Configuration

Location: `~/.config/sx/config.toml`
Your personal paths. Terminal, shell prompt, directory jumper…

```toml
[sandbox]
default_network = "offline" # offline | online | localhost
default_profiles = ["base"] # profiles to always include
shell = "/bin/zsh" # shell to use inside sandbox
prompt_indicator = true # show sandbox indicator in prompt
log_file = "~/.sx/violations.log"
default_profiles = ["base"] # always include these
shell = "/bin/zsh" # shell inside sandbox
prompt_indicator = true # show [sx:mode] in prompt
inherit_base = true # include base profile

[filesystem]
allow_read = ["/usr", "/bin"] # paths to allow reading
deny_read = ["~/.ssh", "~/.aws"] # paths to deny reading
allow_write = ["/tmp"] # paths to allow writing

[network]
allow_domains = [] # domains to allow when online
deny_domains = [] # domains to block even when online
allow_read = [
# Shell prompt
"~/.config/starship.toml",
"~/.cache/starship/",

# zoxide
"~/.local/share/zoxide/",

# Ghostty users - required or terminal breaks
"/Applications/Ghostty.app/Contents/Resources/terminfo",
]
allow_write = [
"~/.local/share/zoxide/",
"~/Library/Application Support/zoxide/",
"~/.cache/",
]
deny_read = [] # additional paths to block

[shell]
pass_env = ["TERM", "PATH"] # env vars to pass through
deny_env = ["AWS_*", "*_SECRET*"] # env vars to block
pass_env = ["CUSTOM_VAR"] # env vars to pass through
deny_env = ["*_SECRET*"] # env vars to block (wildcards)
set_env = { CI = "true" } # env vars to set inside sandbox
```

### Project Configuration
## Project Config (`.sandbox.toml`)

Location: `.sandbox.toml` in project root
Per-project overrides. Create with `sx --init`.

```toml
[sandbox]
inherit_global = true # inherit from global config
profiles = ["rust", "online"] # additional profiles for this project
profiles = ["rust"] # profiles for this project
network = "localhost" # override network mode
inherit_global = true # inherit from global config
inherit_base = true # include base profile (false for full custom)

[filesystem]
allow_read = ["./target"]
allow_read = ["./vendor"]
allow_write = ["./target", "/tmp/build"]
deny_read = ["./secrets"]
allow_write = ["./target", "./build"]

[shell]
pass_env = ["RUST_LOG"]
set_env = { CI = "true" }
pass_env = ["RUST_LOG", "NODE_ENV"]
set_env = { DEBUG = "1" }
```

## Configuration Precedence

1. CLI flags (highest priority)
2. Project config (`.sx.toml`)
3. Global config (`~/.config/sx/config.toml`)
4. Built-in defaults (lowest priority)

## Network Modes

| Mode | Description |
|------|-------------|
| `offline` | Block all network access (default) |
| `online` | Allow all network access |
| `localhost` | Allow only localhost (127.0.0.1) connections |

## Filesystem Rules

- **allow_read**: Paths the sandbox can read from
- **deny_read**: Paths explicitly denied (overrides allows)
- **allow_write**: Paths the sandbox can write to (besides working directory)

The working directory always has full read/write access.

## Environment Variables

- **pass_env**: Environment variables passed into the sandbox
- **deny_env**: Environment variables blocked (supports wildcards)
- **set_env**: Environment variables to set inside the sandbox

### Wildcard Patterns

Environment variable patterns support wildcards:
- `AWS_*` - matches any variable starting with `AWS_`
- `*_SECRET*` - matches variables containing `_SECRET`
- `*_KEY` - matches variables ending with `_KEY`

## Custom Profiles

Create custom profiles in `~/.config/sx/profiles/`:
Create in `~/.config/sx/profiles/`:

```toml
# ~/.config/sx/profiles/myproject.toml
Expand All @@ -101,3 +75,19 @@ allow_write = ["~/.myproject/cache"]
[shell]
pass_env = ["MYPROJECT_TOKEN"]
```

Use with `sx myproject -- command`.

## Precedence

1. CLI flags (highest)
2. Project config (`.sandbox.toml`)
3. Global config (`~/.config/sx/config.toml`)
4. Built-in defaults (lowest)

## Environment Wildcards

`deny_env` supports wildcards:
- `AWS_*` - matches `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`…
- `*_SECRET*` - matches `DATABASE_SECRET`, `MY_SECRET_KEY`…
- `*_KEY` - matches `API_KEY`, `SSH_KEY`…
105 changes: 59 additions & 46 deletions docs/PROFILES.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,101 @@
# Profiles

Profiles are composable sandbox configurations that can be combined to create the right security posture for your project.
Profiles are composable sandbox configs. Stack them: `sx online rust -- cargo build`

## Built-in Profiles

### base

The foundational profile included by default. Provides:
- Read access to system directories (`/usr`, `/bin`, `/sbin`, `/opt`)
- Read access to temp directories (`/tmp`, `/var/folders`)
- Denies access to sensitive directories (`~/.ssh`, `~/.aws`, `~/.gnupg`)
- Basic environment variables (`TERM`, `PATH`, `HOME`, `USER`)
Always included (unless `inherit_base = false`). Provides:
- Read access to system directories (`/usr`, `/bin`, `/sbin`, `/Library`, `/System`)
- Read access to shell configs (`~/.zshrc`, `~/.bashrc`…)
- Write access to `/tmp` and session temp dir
- Basic env vars (`TERM`, `PATH`, `HOME`, `USER`, `SHELL`)

**Always denied** (even if you allow `~`):
- `~/.ssh`
- `~/.aws`
- `~/.docker/config.json`
- `~/Documents`, `~/Desktop`, `~/Downloads`

### online

Enables full network access.
Full network access.

```bash
sx --profile online
sx online -- curl https://example.com
```

### localhost

Allows network connections only to localhost (127.0.0.1).
127.0.0.1 only. For dev servers.

```bash
sx --profile localhost
sx localhost -- npm start
```

### rust

For Rust projects:
- Read access: `~/.cargo`, `~/.rustup`
- Write access: `~/.cargo/registry`
- Network domains: `crates.io`, `static.crates.io`
Rust/Cargo toolchain:
- Read/write: `~/.cargo`, `~/.rustup`
- Env: `CARGO_HOME`, `RUSTUP_HOME`

```bash
sx rust online -- cargo build
```

### bun

Bun runtime:
- Read/write: `~/.bun`
- Parent directory listing for module resolution (`/Users`, `~`)
- Env: `BUN_INSTALL`, `NODE_ENV`

```bash
sx --profile rust
sx bun online -- bun install
```

### claude

For Claude Code projects:
- Read/Write access: `~/.claude`
- Network domains: `api.anthropic.com`
- Passes: `ANTHROPIC_API_KEY`
Claude Code:
- Read/write: `~/.claude`, `~/.claude.json`
- Includes `online` network
- Env: `ANTHROPIC_API_KEY`

```bash
sx --profile claude
sx claude -- claude --dangerously-skip-permissions --continue
```

### gpg

For GPG signing:
- Read/Write access: `~/.gnupg`
GPG signing:
- Read/write: `~/.gnupg`

```bash
sx --profile gpg
sx gpg -- git commit -S -m "signed"
```

## Profile Composition
## Combining Profiles

Profiles can be combined. The order matters for network mode (last one wins):
Order matters for network mode (last wins). Filesystem paths merge.

```bash
# Rust project with full network access
# Rust with network
sx rust online -- cargo build

# Rust project with localhost only
sx rust localhost -- cargo test
# Rust offline (tests with cached deps)
sx rust -- cargo test

# GPG signing with network access
sx gpg online -- git commit -S -m "signed commit"
# Claude with GPG signing
sx claude gpg -- claude --dangerously-skip-permissions

# Bun with network
sx bun online -- bun install
```

## Custom Profiles

Create custom profiles in `~/.config/sx/profiles/` or `./profiles/`:
Create in `~/.config/sx/profiles/`:

```toml
# ~/.config/sx/profiles/mycompany.toml
Expand All @@ -86,33 +105,27 @@ network_mode = "online"
allow_read = ["/opt/mycompany"]
allow_write = ["~/.mycompany/cache"]

[network]
allow_domains = ["api.mycompany.com", "*.internal.mycompany.com"]

[shell]
pass_env = ["MYCOMPANY_TOKEN"]
```

Use it:

```bash
sx --profile mycompany
sx mycompany -- ./run.sh
```

## Profile Merging Rules

When multiple profiles are composed:

1. **Network mode**: Last profile with a network mode wins
2. **Filesystem paths**: Union of all paths (no duplicates)
3. **Network domains**: Union of all domains
4. **Environment variables**: Union of all pass/deny lists
## Project Profiles

## Project-Specific Profiles

Define profiles in `.sandbox.toml`:
In `.sandbox.toml`:

```toml
[sandbox]
profiles = ["rust", "localhost"]
```

## Merging Rules

1. **Network mode:** last profile with a mode wins
2. **Filesystem paths:** union (no duplicates)
3. **Env vars:** union of pass/deny lists
Loading