> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/eugene1g/agent-safehouse/llms.txt
> Use this file to discover all available pages before exploring further.

# Distribution

> Understanding dist/ artifacts, single-file dist/safehouse.sh, and generation process

## Overview

The `dist/` directory contains **packaged distribution artifacts** for consumers. These are **generated files**, not the source of truth.

<Warning>
  **Never hand-edit `dist/` files.** All changes must be made in `bin/` and `profiles/`, then regenerated via `./scripts/generate-dist.sh`.
</Warning>

***

## Distribution Artifacts

Running `./scripts/generate-dist.sh` produces five committed artifacts:

<CardGroup cols={2}>
  <Card title="dist/safehouse.sh" icon="file-code">
    Single-file executable containing embedded profiles + runtime shell logic. Portable and self-contained.
  </Card>

  <Card title="dist/Claude.app.sandboxed.command" icon="rocket">
    Single-file launcher for Claude Desktop. Fetches latest apps policy from GitHub at runtime.
  </Card>

  <Card title="dist/Claude.app.sandboxed-offline.command" icon="circle-dot">
    Offline launcher for Claude Desktop with embedded apps policy (no network required).
  </Card>

  <Card title="dist/profiles/safehouse.generated.sb" icon="shield">
    Static default policy template with `HOME_DIR` and workdir placeholders.
  </Card>

  <Card title="dist/profiles/safehouse-for-apps.generated.sb" icon="window-maximize">
    Static apps policy with `--enable=macos-gui,electron,all-agents,all-apps`.
  </Card>
</CardGroup>

***

## dist/safehouse.sh: Single-File Executable

### Purpose

The `dist/safehouse.sh` executable is a **self-contained, portable distribution** of Agent Safehouse. It embeds:

* All profile files (`profiles/**/*.sb`) as heredoc blocks
* Full runtime shell logic from `bin/safehouse.sh` and `bin/lib/*.sh`
* Metadata (embedded profiles last modified timestamp)

Consumers can download **one file** and run it without cloning the repo.

### Structure

<Steps>
  <Step title="Banner and Metadata">
    ```bash theme={null}
    #!/usr/bin/env bash
    # ---------------------------------------------------------------------------
    # Agent Safehouse Dist Binary (generated file)
    # Project: https://agent-safehouse.dev
    # Embedded Profiles Last Modified (UTC): 2026-03-09T12:34:56Z
    # Generated by: scripts/generate-dist.sh
    # ---------------------------------------------------------------------------
    set -euo pipefail
    ```
  </Step>

  <Step title="Profile Keys Array">
    ```bash theme={null}
    PROFILE_KEYS=(
      "profiles/00-base.sb"
      "profiles/10-system-runtime.sb"
      "profiles/20-network.sb"
      "profiles/30-toolchains/bun.sb"
      "profiles/30-toolchains/deno.sb"
      # ... all profile files in lexicographic order
    )
    ```
  </Step>

  <Step title="Embedded Profile Bodies">
    Each profile is embedded as a heredoc:

    ```bash theme={null}
    embedded_profile_body() {
      case "$1" in
        "profiles/00-base.sb")
          cat <<'__SAFEHOUSE_EMBEDDED_profiles_00_base_sb__'
    (version 1)
    ;; Base profile content...
    __SAFEHOUSE_EMBEDDED_profiles_00_base_sb__
          ;;
        "profiles/10-system-runtime.sb")
          cat <<'__SAFEHOUSE_EMBEDDED_profiles_10_system_runtime_sb__'
    ;; System runtime content...
    __SAFEHOUSE_EMBEDDED_profiles_10_system_runtime_sb__
          ;;
        # ... all profiles
      esac
    }
    ```
  </Step>

  <Step title="Inlined Runtime Logic">
    All shell logic from `bin/safehouse.sh`, `bin/lib/common.sh`, `bin/lib/policy/*.sh`, and `bin/lib/cli.sh` is concatenated inline.
  </Step>

  <Step title="Overridden Functions">
    Key functions are overridden to read from embedded profiles instead of filesystem:

    ```bash theme={null}
    append_profile() {
      local target="$1"
      local source="$2"
      local key content

      key="$(profile_key_from_source "$source")"
      if content="$(embedded_profile_body "$key" 2>/dev/null)"; then
        append_policy_chunk "$content"
        append_policy_chunk ""
        return
      fi

      # Fallback: read from disk (for --append-profile external files)
      if [[ -f "$source" ]]; then
        content="$(<"$source")"
        append_policy_chunk "$content"
        append_policy_chunk ""
        return
      fi

      echo "Missing profile module: ${source}" >&2
      exit 1
    }
    ```
  </Step>

  <Step title="Main Entrypoint">
    ```bash theme={null}
    main "$@"
    ```
  </Step>
</Steps>

### Usage

Identical to `bin/safehouse.sh`:

```bash theme={null}
# Download and run
curl -fsSL https://raw.githubusercontent.com/eugene1g/agent-safehouse/main/dist/safehouse.sh -o safehouse.sh
chmod +x safehouse.sh
./safehouse.sh --enable=docker,ssh -- myagent

# Or install globally
mv safehouse.sh /usr/local/bin/safehouse
safehouse --enable=docker,ssh -- myagent
```

***

## Static Policy Files

### dist/profiles/safehouse.generated.sb

Default policy template with:

* `HOME_DIR` placeholder: `"/__SAFEHOUSE_TEMPLATE_HOME__"`
* Workdir grant placeholder: `"/__SAFEHOUSE_TEMPLATE_WORKDIR__"`
* All agents enabled (`--enable=all-agents`)
* No optional integrations (use `--enable=...` when generating)

**Use case:** Consumers who want to manually replace placeholders and use the policy directly with `sandbox-exec`.

```bash theme={null}
# Generate policy with custom workdir
sed 's|/__SAFEHOUSE_TEMPLATE_HOME__|/Users/alice|g; s|/__SAFEHOUSE_TEMPLATE_WORKDIR__|/Users/alice/projects/myapp|g' \
  dist/profiles/safehouse.generated.sb > /tmp/policy.sb

sandbox-exec -f /tmp/policy.sb -- myagent
```

***

### dist/profiles/safehouse-for-apps.generated.sb

Apps policy template with:

* `HOME_DIR` placeholder: `"/__SAFEHOUSE_TEMPLATE_HOME__"`
* Workdir grant placeholder: `"/__SAFEHOUSE_TEMPLATE_WORKDIR__"`
* All agents + all apps enabled (`--enable=macos-gui,electron,all-agents,all-apps`)
* Electron and macOS GUI integrations (for Claude Desktop, VS Code, etc.)

**Use case:** Embedded in Claude Desktop launchers (`Claude.app.sandboxed.command`, `Claude.app.sandboxed-offline.command`).

***

## Generation Process

### Invoking the Generator

```bash theme={null}
./scripts/generate-dist.sh
```

<Info>
  Requires macOS with `sandbox-exec`. The generator invokes `bin/safehouse.sh` to produce static policies.
</Info>

### Generation Steps

<Steps>
  <Step title="Collect and Validate Profiles">
    ```bash theme={null}
    collect_profiles() {
      while IFS= read -r rel_path; do
        profile_files+=("$rel_path")
      done < <(
        cd "$ROOT_DIR"
        find profiles -type f -name '*.sb' | LC_ALL=C sort
      )
    }
    ```

    Validates required profiles (`00-base.sb`, `10-system-runtime.sb`, etc.) and counts by stage prefix.
  </Step>

  <Step title="Resolve Embedded Profiles Timestamp">
    ```bash theme={null}
    resolve_embedded_profiles_last_modified_utc() {
      # Prefer git commit metadata for deterministic output
      epoch="$(git -C "$ROOT_DIR" log -1 --format=%ct -- "${profile_files[@]}" 2>/dev/null || true)"

      if [[ ! "$epoch" =~ ^[0-9]+$ ]] || (( epoch <= 0 )); then
        # Fallback: latest filesystem mtime
        epoch="$(latest_embedded_profile_epoch_from_fs)"
      fi

      format_epoch_utc "$epoch"
    }
    ```

    Uses `git log` for deterministic timestamps across CI/machines. Falls back to filesystem mtime if git is unavailable.
  </Step>

  <Step title="Generate dist/safehouse.sh">
    ```bash theme={null}
    write_dist_script() {
      emit_banner "$embedded_profiles_last_modified_utc"
      emit_array_declaration "PROFILE_KEYS" "${profile_files[@]}"
      emit_embedded_profiles_function
      emit_safehouse_globals
      emit_inlined_runtime_sources
      emit_embedded_overrides
      echo 'main "$@"'
    }
    ```

    Concatenates banner, profile keys, embedded heredocs, runtime logic, and overrides into a single executable.
  </Step>

  <Step title="Generate Static Policies">
    ```bash theme={null}
    generate_static_policy_files() {
      # Create template directories
      mkdir -p "$template_home" "$template_workdir"

      # Generate default policy
      HOME="$template_home" "$GENERATOR" --enable=all-agents --workdir="" --output "$default_policy_path" >/dev/null

      # Generate apps policy
      HOME="$template_home" "$GENERATOR" --enable=macos-gui,electron,all-agents,all-apps --workdir="" --output "$apps_policy_path" >/dev/null

      # Rewrite HOME_DIR to template placeholder
      rewrite_static_policy_home_dir_literal "$default_policy_path"
      rewrite_static_policy_home_dir_literal "$apps_policy_path"

      # Append workdir grant template
      append_static_policy_workdir_grant "$default_policy_path"
      append_static_policy_workdir_grant "$apps_policy_path"
    }
    ```

    Invokes `bin/safehouse.sh` with template paths, then rewrites `HOME_DIR` and workdir placeholders.
  </Step>

  <Step title="Generate Claude Launchers">
    ```bash theme={null}
    write_claude_launcher "$launcher_path"
    write_claude_offline_launcher "$launcher_offline_path" "$apps_policy_path"
    ```

    Creates single-file launchers for Claude Desktop (online and offline variants).
  </Step>

  <Step title="Cleanup and Output">
    ```bash theme={null}
    cleanup_template_root
    printf '%s\n' "$output_path"
    printf '%s\n' "$launcher_path"
    printf '%s\n' "$launcher_offline_path"
    printf '%s\n' "$default_policy_path"
    printf '%s\n' "$apps_policy_path"
    ```
  </Step>
</Steps>

***

## Deterministic Output

### Why Determinism Matters

Dist artifacts are **committed to the repository**. Non-deterministic generation causes unnecessary diffs and merge conflicts.

### How Safehouse Achieves Determinism

<Accordion title="Lexicographic Profile Sorting">
  ```bash theme={null}
  find profiles -type f -name '*.sb' | LC_ALL=C sort
  ```

  Forces consistent ordering across different machines and locales.
</Accordion>

<Accordion title="Git-Based Timestamps">
  ```bash theme={null}
  epoch="$(git log -1 --format=%ct -- "${profile_files[@]}" 2>/dev/null)"
  ```

  Uses `git log` commit timestamp instead of filesystem mtime. Same commit = same timestamp.
</Accordion>

<Accordion title="Template Paths">
  ```bash theme={null}
  template_home="/__SAFEHOUSE_TEMPLATE_HOME__"
  template_workdir="/__SAFEHOUSE_TEMPLATE_WORKDIR__"
  ```

  Static placeholders instead of actual home directory paths. Ensures generated policies are portable.
</Accordion>

<Accordion title="Fixed HOME Environment">
  ```bash theme={null}
  HOME="$template_home" "$GENERATOR" --enable=all-agents --workdir="" --output "$default_policy_path"
  ```

  Overrides `HOME` to template path during generation. Output is independent of the generator's actual home directory.
</Accordion>

***

## CI/CD Integration

### Auto-Regeneration Workflow

Safehouse CI automatically regenerates `dist/` when profiles or runtime logic change:

```yaml theme={null}
# .github/workflows/dist-regen.yml
name: Regenerate Dist Artifacts

on:
  push:
    paths:
      - 'profiles/**/*.sb'
      - 'bin/**/*.sh'

jobs:
  regenerate:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - name: Regenerate dist
        run: ./scripts/generate-dist.sh
      - name: Commit changes
        run: |
          git config user.name "GitHub Actions"
          git config user.email "actions@github.com"
          git add dist/
          git commit -m "Regenerate dist artifacts" || exit 0
          git push
```

### Manual Regeneration

When contributing:

```bash theme={null}
# After editing profiles/ or bin/
./scripts/generate-dist.sh

# Commit both source and dist changes
git add profiles/ bin/ dist/
git commit -m "Add SSH integration profile"
```

<Warning>
  PRs that modify `profiles/` or `bin/` **must** include regenerated `dist/` files. CI will fail if `dist/` is out of sync.
</Warning>

***

## Inspecting Dist Artifacts

### Verify Embedded Profiles

```bash theme={null}
# List all embedded profile keys
grep -A 100 'PROFILE_KEYS=(' dist/safehouse.sh | grep '"profiles/'
```

### Extract a Single Embedded Profile

```bash theme={null}
# Extract docker.sb
dist/safehouse.sh # (source the file functions)
embedded_profile_body "profiles/55-integrations-optional/docker.sb"
```

### Compare Source vs Embedded

```bash theme={null}
# Source profile
cat profiles/60-agents/aider.sb

# Embedded profile
awk '/"profiles\/60-agents\/aider.sb"/,/^__SAFEHOUSE_EMBEDDED/ {print}' dist/safehouse.sh | head -n -1 | tail -n +3
```

***

## Key Takeaways

<CardGroup cols={2}>
  <Card title="Generated, Not Source" icon="cog">
    `dist/` artifacts are **generated files**. All changes must be made in `bin/` and `profiles/`, then regenerated.
  </Card>

  <Card title="Single-File Distribution" icon="file-zipper">
    `dist/safehouse.sh` embeds all profiles and runtime logic for portable, self-contained execution.
  </Card>

  <Card title="Deterministic Output" icon="check-double">
    Lexicographic sorting, git timestamps, and template paths ensure identical output across machines.
  </Card>

  <Card title="CI Auto-Regeneration" icon="robot">
    Safehouse CI auto-commits regenerated `dist/` when profiles or runtime logic change.
  </Card>
</CardGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Contributing" href="https://github.com/eugene1g/agent-safehouse/blob/main/CONTRIBUTING.md" icon="code-pull-request">
    Learn how to contribute profiles, runtime logic, and tests to Agent Safehouse.
  </Card>

  <Card title="Policy Architecture" href="/advanced/policy-architecture" icon="layer-group">
    Understand how profiles are assembled and concatenated at runtime.
  </Card>
</CardGroup>
