Overview
The dist/ directory contains packaged distribution artifacts for consumers. These are generated files , not the source of truth.
Never hand-edit dist/ files. All changes must be made in bin/ and profiles/, then regenerated via ./scripts/generate-dist.sh.
Distribution Artifacts
Running ./scripts/generate-dist.sh produces five committed artifacts:
dist/safehouse.sh Single-file executable containing embedded profiles + runtime shell logic. Portable and self-contained.
dist/Claude.app.sandboxed.command Single-file launcher for Claude Desktop. Fetches latest apps policy from GitHub at runtime.
dist/Claude.app.sandboxed-offline.command Offline launcher for Claude Desktop with embedded apps policy (no network required).
dist/profiles/safehouse.generated.sb Static default policy template with HOME_DIR and workdir placeholders.
dist/profiles/safehouse-for-apps.generated.sb Static apps policy with --enable=macos-gui,electron,all-agents,all-apps.
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
Banner and Metadata
#!/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
Profile Keys Array
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
)
Embedded Profile Bodies
Each profile is embedded as a heredoc: 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
}
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.
Overridden Functions
Key functions are overridden to read from embedded profiles instead of filesystem: 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
}
Usage
Identical to bin/safehouse.sh:
# 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.
# 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
./scripts/generate-dist.sh
Requires macOS with sandbox-exec. The generator invokes bin/safehouse.sh to produce static policies.
Generation Steps
Collect and Validate Profiles
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.
Resolve Embedded Profiles Timestamp
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.
Generate dist/safehouse.sh
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.
Generate Static Policies
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.
Generate Claude Launchers
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).
Cleanup and Output
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 "
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
Lexicographic Profile Sorting
find profiles -type f -name '*.sb' | LC_ALL = C sort
Forces consistent ordering across different machines and locales.
epoch = "$( git log -1 --format=%ct -- "${ profile_files [ @ ]}" 2> /dev/null)"
Uses git log commit timestamp instead of filesystem mtime. Same commit = same timestamp.
template_home = "/__SAFEHOUSE_TEMPLATE_HOME__"
template_workdir = "/__SAFEHOUSE_TEMPLATE_WORKDIR__"
Static placeholders instead of actual home directory paths. Ensures generated policies are portable.
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.
CI/CD Integration
Auto-Regeneration Workflow
Safehouse CI automatically regenerates dist/ when profiles or runtime logic change:
# .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 "[email protected] "
git add dist/
git commit -m "Regenerate dist artifacts" || exit 0
git push
Manual Regeneration
When contributing:
# 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"
PRs that modify profiles/ or bin/ must include regenerated dist/ files. CI will fail if dist/ is out of sync.
Inspecting Dist Artifacts
Verify Embedded Profiles
# List all embedded profile keys
grep -A 100 'PROFILE_KEYS=(' dist/safehouse.sh | grep '"profiles/'
# Extract docker.sb
dist/safehouse.sh # (source the file functions)
embedded_profile_body "profiles/55-integrations-optional/docker.sb"
Compare Source vs Embedded
# 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
Generated, Not Source dist/ artifacts are generated files . All changes must be made in bin/ and profiles/, then regenerated.
Single-File Distribution dist/safehouse.sh embeds all profiles and runtime logic for portable, self-contained execution.
Deterministic Output Lexicographic sorting, git timestamps, and template paths ensure identical output across machines.
CI Auto-Regeneration Safehouse CI auto-commits regenerated dist/ when profiles or runtime logic change.
Next Steps
Contributing Learn how to contribute profiles, runtime logic, and tests to Agent Safehouse.
Policy Architecture Understand how profiles are assembled and concatenated at runtime.