sboxd 0.1.9

Policy-driven command runner for sandboxed dependency installation
Documentation
# Recipes

Common patterns for real-world sbox usage.

---

## CI pipeline: locked install with audit

Run `npm ci` in CI with strict policy: locked install, pre-flight audit, registry-only network, pinned image.

```yaml
version: 1

runtime:
  backend: podman
  rootless: true
  strict_security: true

workspace:
  root: .
  mount: /workspace
  writable: false
  writable_paths:
    - node_modules
  exclude_paths:
    - .npmrc
    - .netrc
    - ".ssh/*"
    - ".aws/*"

image:
  ref: node:22-bookworm-slim
  digest: sha256:<pinned>

environment:
  pass_through:
    - CI
    - TERM
  deny:
    - NPM_TOKEN
    - NODE_AUTH_TOKEN
    - GITHUB_TOKEN

caches:
  - name: npm-cache
    target: /root/.npm

profiles:
  install:
    mode: sandbox
    network: on
    network_allow:
      - "*.npmjs.org"
    writable: true
    role: install
    no_new_privileges: true
    require_pinned_image: true
    lockfile_files:
      - package-lock.json
    pre_run:
      - npm audit --audit-level=critical

dispatch:
  ci:
    match:
      - "npm ci"
    profile: install
```

Usage in CI:

```bash
sbox run -- npm ci
```

---

## Two-phase install: download then script execution

Download packages with network, run postinstall scripts without network.

```yaml
profiles:
  download:
    mode: sandbox
    network: on
    network_allow:
      - "*.npmjs.org"
    writable: true
    role: install
    no_new_privileges: true

  run-scripts:
    mode: sandbox
    network: off
    writable: true
    no_new_privileges: true

dispatch:
  npm-download:
    match:
      - "npm install --ignore-scripts*"
    profile: download

  npm-rebuild:
    match:
      - "npm rebuild*"
      - "npm run prepare*"
    profile: run-scripts
```

Usage:

```bash
sbox run -- npm install --ignore-scripts   # download only, network allowed
sbox run -- npm rebuild                    # run scripts, network off
```

---

## Keep packages out of the workspace

Redirect npm output to a cache volume so nothing is written to the project directory. Useful when the workspace is read-only or when you want clean installs every time.

```yaml
environment:
  set:
    npm_config_prefix: /var/tmp/sbox/npm-prefix
    npm_config_cache: /var/tmp/sbox/npm-cache

caches:
  - name: npm-prefix
    target: /var/tmp/sbox/npm-prefix
  - name: npm-cache
    target: /var/tmp/sbox/npm-cache

workspace:
  writable: false
  writable_paths: []   # nothing writable in the workspace
```

The installed binaries land in `/var/tmp/sbox/npm-prefix/bin` inside the container. To run them:

```bash
sbox run -- /var/tmp/sbox/npm-prefix/bin/my-tool
```

---

## Reusable dev session

Keep a container running between commands. Faster startup; useful for interactive development.

```yaml
runtime:
  reuse_container: true

profiles:
  default:
    mode: sandbox
    network: off
    writable: true
```

```bash
sbox run -- npm run build   # starts container, runs command, keeps container alive
sbox run -- npm run test    # reuses the same container
sbox clean                  # stops and removes the container
```

---

## Private registry with credentials

Mount an `.npmrc` with a token as a secret, without exposing it through the environment.

```yaml
secrets:
  - source: ~/.npmrc
    target: /root/.npmrc

workspace:
  exclude_paths:
    - .npmrc    # mask the workspace .npmrc (may not have credentials)

profiles:
  install:
    network: on
    network_allow:
      - npm.myprivateregistry.com
```

The secret is mounted read-only inside the container. The source file on the host is not modified.

---

## Route all execution through sbox

Prevent installed artifacts from running outside the sandbox. Add a `default` profile that sandboxes everything.

```yaml
profiles:
  default:
    mode: sandbox
    network: off
    writable: true
    no_new_privileges: true

dispatch:
  install:
    match:
      - "npm install*"
      - "npm ci"
    profile: install
  # Everything else goes through the default sandbox
```

```bash
sbox run -- node server.js       # sandboxed
sbox run -- npx eslint src/      # sandboxed
sbox run -- npm run build        # sandboxed
```

---

## Docker instead of Podman

```yaml
runtime:
  backend: docker
  rootless: false
```

With Docker, rootless mode typically requires extra setup. If running as a non-root user with Docker socket access, set `rootless: false` and ensure the socket is accessible.

---

## Pin image and verify signature

```bash
# Get the digest
podman pull node:22-bookworm-slim
podman inspect node:22-bookworm-slim --format '{{index .RepoDigests 0}}'
```

```yaml
image:
  ref: node:22-bookworm-slim
  digest: sha256:abc123...
  verify_signature: true
```

`verify_signature` uses `skopeo` and the system containers policy. Check readiness:

```bash
sbox doctor
```

---

## Transparent shims for a team

Install shims system-wide so every developer automatically sandboxes package manager commands:

```bash
sudo sbox shim --dir /usr/local/bin
```

Or per-user:

```bash
sbox shim --dir ~/.local/bin
# add to ~/.bashrc or ~/.zshrc:
export PATH="$HOME/.local/bin:$PATH"
```

Shims pass through to the real binary in directories without `sbox.yaml`. Only projects that have opted in (by having `sbox.yaml`) get sandboxed.

---

## Multiple images per profile

Override the image for a specific profile without changing the global image:

```yaml
image:
  ref: node:22-bookworm-slim

profiles:
  build:
    mode: sandbox
    image:
      ref: node:22-bookworm-slim
      digest: sha256:<pinned>

  lint:
    mode: sandbox
    image:
      ref: node:20-alpine    # different image for linting
```