---
phase: 03-sandbox-aware-prompting
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- claudebox.sh
autonomous: true
requirements:
- AWARE-01
- AWARE-02
must_haves:
truths:
- "Claude inside the sandbox sees SANDBOX.md content describing its environment"
- "CLAUDE.md in ~/.claudebox/ exists after first launch with @SANDBOX.md import on line 1"
- "SANDBOX.md is overwritten on every launch with current content"
- "Existing user content in CLAUDE.md is preserved when import line is prepended"
artifacts:
- path: "claudebox.sh"
provides: "SANDBOX.md generation and CLAUDE.md import check"
contains: "SANDBOXEOF"
- path: "~/.claudebox/SANDBOX.md"
provides: "Sandbox context for Claude Code"
contains: "bubblewrap"
- path: "~/.claudebox/CLAUDE.md"
provides: "User-owned CLAUDE.md with managed import"
contains: "@SANDBOX.md"
key_links:
- from: "claudebox.sh"
to: "~/.claudebox/SANDBOX.md"
via: "heredoc write on every launch"
pattern: "cat > .*/SANDBOX.md"
- from: "~/.claudebox/CLAUDE.md"
to: "~/.claudebox/SANDBOX.md"
via: "@SANDBOX.md import on line 1"
pattern: "@SANDBOX.md"
- from: "bwrap --bind ~/.claudebox ~/.claude"
to: "Claude Code session"
via: "bind mount makes ~/.claudebox visible as ~/.claude"
pattern: "--bind.*\\.claudebox.*\\.claude"
---
Add sandbox-aware prompting to claudebox so Claude Code automatically knows it is running in a bwrap sandbox, how to install tools via comma and nix shell, and what host resources are unavailable by default.
Purpose: Claude Code sessions inside claudebox currently have no context about their sandboxed environment. This causes Claude to attempt operations that will fail (SSH git, gpg signing) and miss capabilities it has (comma, nix shell). Injecting this context eliminates friction.
Output: Modified claudebox.sh that generates SANDBOX.md and manages CLAUDE.md import on every launch.
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/03-sandbox-aware-prompting/03-CONTEXT.md
@.planning/phases/03-sandbox-aware-prompting/03-RESEARCH.md
@claudebox.sh
Task 1: Add SANDBOX.md generation and CLAUDE.md import check to claudebox.sh
claudebox.sh
- claudebox.sh (current state, integration point at line 102-103)
- .planning/phases/03-sandbox-aware-prompting/03-CONTEXT.md (locked decisions D-01 through D-08)
- .planning/phases/03-sandbox-aware-prompting/03-RESEARCH.md (implementation patterns, SANDBOX.md content structure)
Insert a new code block in claudebox.sh between line 102 (`mkdir -p "$HOME/.claudebox"`) and line 104 (`GIT_NAME=$(git config ...)`). The block does two things:
**Part 1: Write SANDBOX.md (per D-02 -- overwritten every launch)**
Use a single-quoted heredoc (`<< 'SANDBOXEOF'`) to write `$HOME/.claudebox/SANDBOX.md`. The content must follow D-04 (friendly guide tone, short prose paragraphs), D-05 (default restrictions with "by default" phrasing), and D-06 (git identity pre-configured, HTTPS preferred). Use this exact content:
```
# Sandbox Environment
You are running inside a bubblewrap (bwrap) sandbox managed by claudebox.
Your filesystem is isolated -- only the current working directory and
essential system paths are mounted.
## Installing Tools
You have two ways to install tools on the fly:
**Comma (preferred for quick one-off commands):**
`, ripgrep` runs ripgrep without permanent installation. Comma uses
nix-index to find the right package automatically.
**Nix shell (for persistent access within the session):**
`nix shell nixpkgs#python3 -c python3 script.py` runs a command with
a package available. To keep it in your PATH for the session:
`nix shell nixpkgs#python3` then use `python3` normally.
## Default Restrictions
By default, the following are not mounted into the sandbox:
- SSH keys (~/.ssh)
- GPG and age keys (~/.gnupg, age key files)
- Cloud credentials (~/.aws, ~/.config/gcloud)
- Tailscale state
If your setup has been customized, some of these may be available.
## Git
Your git identity (name and email) is pre-configured from the host.
The `safe.directory` setting trusts the mounted working directory.
For remote operations, prefer HTTPS URLs over SSH since SSH keys
are not available by default.
```
The heredoc line is: `cat > "$HOME/.claudebox/SANDBOX.md" << 'SANDBOXEOF'`
Closing delimiter is: `SANDBOXEOF` (flush-left, no indentation).
**Part 2: Ensure CLAUDE.md has @SANDBOX.md import (per D-03, D-07, D-08, AWARE-01)**
After writing SANDBOX.md, add the CLAUDE.md import check:
```bash
# Ensure CLAUDE.md has @SANDBOX.md import (D-03, D-08, AWARE-01)
CLAUDEMD="$HOME/.claudebox/CLAUDE.md"
if [[ ! -f "$CLAUDEMD" ]]; then
printf '%s\n' "@SANDBOX.md" > "$CLAUDEMD"
elif [[ "$(head -1 "$CLAUDEMD")" != "@SANDBOX.md" ]]; then
tmp=$(mktemp)
{ printf '%s\n' "@SANDBOX.md"; cat "$CLAUDEMD"; } > "$tmp"
mv "$tmp" "$CLAUDEMD"
fi
```
This creates CLAUDE.md with just the import if it does not exist (AWARE-01), or prepends the import if the first line does not match exactly `@SANDBOX.md` (D-08). Existing user content is preserved (D-03).
Add a section comment before the block: `# === Sandbox-aware prompting (AWARE-01, AWARE-02) ===`
cd /home/toph/code/tools/claudebox && nix build --no-link 2>&1 | tail -5 && echo "BUILD OK"
- claudebox.sh contains `cat > "$HOME/.claudebox/SANDBOX.md" << 'SANDBOXEOF'`
- claudebox.sh contains the closing `SANDBOXEOF` heredoc delimiter
- SANDBOX.md heredoc contains `# Sandbox Environment`
- SANDBOX.md heredoc contains `## Installing Tools`
- SANDBOX.md heredoc contains `## Default Restrictions`
- SANDBOX.md heredoc contains `By default, the following are not mounted into the sandbox:`
- SANDBOX.md heredoc contains `If your setup has been customized, some of these may be available.`
- SANDBOX.md heredoc contains `## Git`
- SANDBOX.md heredoc contains `prefer HTTPS URLs over SSH`
- claudebox.sh contains `CLAUDEMD="$HOME/.claudebox/CLAUDE.md"`
- claudebox.sh contains `printf '%s\n' "@SANDBOX.md" > "$CLAUDEMD"`
- claudebox.sh contains `head -1 "$CLAUDEMD"` for first-line check
- claudebox.sh contains `mv "$tmp" "$CLAUDEMD"` for atomic prepend
- The SANDBOX.md/CLAUDE.md block appears AFTER `mkdir -p "$HOME/.claudebox"` and BEFORE gitconfig generation
- `nix build` succeeds (shellcheck passes)
claudebox.sh contains SANDBOX.md heredoc generation and CLAUDE.md import check between mkdir and gitconfig blocks. nix build succeeds with shellcheck passing.
Task 2: Verify file generation behavior end-to-end
claudebox.sh
- claudebox.sh (the modified file from Task 1)
Run claudebox in dry-run mode to verify the script executes past the SANDBOX.md/CLAUDE.md generation without errors, then verify the files were created correctly.
**Step 1:** Clean slate test. Remove any existing SANDBOX.md and CLAUDE.md from ~/.claudebox/:
```bash
rm -f "$HOME/.claudebox/SANDBOX.md" "$HOME/.claudebox/CLAUDE.md"
```
**Step 2:** Run `claudebox --dry-run` (which executes all pre-bwrap logic including file generation, then prints the command and exits). This exercises the SANDBOX.md write and CLAUDE.md creation path (AWARE-01 -- first run, file does not exist).
**Step 3:** Verify SANDBOX.md was created with correct content:
- `head -1 ~/.claudebox/SANDBOX.md` should output `# Sandbox Environment`
- `grep -c "## " ~/.claudebox/SANDBOX.md` should be 4 (four H2 sections: Installing Tools, Default Restrictions, Git, plus the H1 counts differently -- actually grep for `^## ` to count H2s, expect 3: Installing Tools, Default Restrictions, Git)
**Step 4:** Verify CLAUDE.md was created with import line:
- `cat ~/.claudebox/CLAUDE.md` should output exactly `@SANDBOX.md`
**Step 5:** Idempotency test. Run `claudebox --dry-run` again. Verify:
- SANDBOX.md was overwritten (check timestamp or content is identical)
- CLAUDE.md still has exactly one `@SANDBOX.md` line on line 1 (no duplication)
**Step 6:** Prepend test. Add user content to CLAUDE.md, remove the import line, then run again:
```bash
printf '%s\n' "# My custom instructions" "Do cool stuff" > ~/.claudebox/CLAUDE.md
```
Run `claudebox --dry-run`. Verify:
- `head -1 ~/.claudebox/CLAUDE.md` outputs `@SANDBOX.md`
- `head -3 ~/.claudebox/CLAUDE.md` shows import line followed by the user's content (preserved per D-03)
cd /home/toph/code/tools/claudebox && rm -f "$HOME/.claudebox/SANDBOX.md" "$HOME/.claudebox/CLAUDE.md" && nix run . -- --dry-run --yes 2>/dev/null; head -1 "$HOME/.claudebox/SANDBOX.md" | grep -qF "# Sandbox Environment" && echo "SANDBOX.md OK" && cat "$HOME/.claudebox/CLAUDE.md" | head -1 | grep -qF "@SANDBOX.md" && echo "CLAUDE.md OK" && printf '%s\n' "# My stuff" > "$HOME/.claudebox/CLAUDE.md" && nix run . -- --dry-run --yes 2>/dev/null && head -1 "$HOME/.claudebox/CLAUDE.md" | grep -qF "@SANDBOX.md" && sed -n '2p' "$HOME/.claudebox/CLAUDE.md" | grep -qF "# My stuff" && echo "PREPEND OK"
- After first run with no existing files: ~/.claudebox/SANDBOX.md exists and starts with `# Sandbox Environment`
- After first run with no existing files: ~/.claudebox/CLAUDE.md exists and contains exactly `@SANDBOX.md`
- After second run: CLAUDE.md still has exactly one `@SANDBOX.md` on line 1 (no duplication)
- After removing import from CLAUDE.md and re-running: `@SANDBOX.md` is prepended, existing content preserved on subsequent lines
- grep -c "@SANDBOX.md" ~/.claudebox/CLAUDE.md returns 1 (exactly one import line)
SANDBOX.md generation, CLAUDE.md creation, CLAUDE.md import prepending, and idempotency all verified via dry-run execution.
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| Host filesystem -> sandbox | Files written to ~/.claudebox/ on host are exposed as ~/.claude/ inside sandbox |
| claudebox script -> user CLAUDE.md | Script modifies a user-owned file (prepend only) |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-03-01 | Tampering | SANDBOX.md content | accept | SANDBOX.md is informational only (context for Claude). Tampering has no security impact -- it cannot grant sandbox permissions. Overwritten every launch anyway (D-02). |
| T-03-02 | Tampering | CLAUDE.md user content | mitigate | claudebox only modifies line 1 (import line). Uses mktemp+mv for atomic write. Preserves all existing content per D-03. No truncation risk. |
| T-03-03 | Information Disclosure | SANDBOX.md reveals sandbox topology | accept | SANDBOX.md tells Claude what is NOT available (SSH, GPG, cloud creds). This is useful context, not a secret. Does not reveal host paths or configuration details beyond what Claude can already discover via `env` and filesystem exploration. |
| T-03-04 | Denial of Service | CLAUDE.md write failure | accept | If mktemp or mv fails, `set -euo pipefail` aborts the script. User sees clear error. No data loss -- original CLAUDE.md unchanged (mv is atomic). |
1. `nix build` succeeds (shellcheck validation)
2. `claudebox --dry-run` creates SANDBOX.md with correct content
3. `claudebox --dry-run` creates CLAUDE.md with @SANDBOX.md import on first run
4. Repeated runs do not duplicate the import line
5. Existing user content in CLAUDE.md is preserved when import is prepended
- claudebox.sh contains SANDBOX.md heredoc and CLAUDE.md import management code
- nix build passes shellcheck
- File generation works correctly for all three cases: first run (no files), repeat run (files exist), and recovery (import line missing)
- SANDBOX.md content matches D-04/D-05/D-06 requirements