claudebox/.planning/phases/02-env-audit-and-cli-polish/02-01-PLAN.md
Christopher Mühl c5e8cca867 feat(05-01): rewrite mount architecture with per-project instance isolation
- Replace --bind ~/.claudebox + --symlink with direct --bind ~/.claude ~/.claude
- Add compute_canonical_root() function using git rev-parse --git-common-dir
- Add per-project INSTANCE_DIR via sha256sum[:16] of canonical git root
- Overlay projects/ with per-project hash dir for isolated conversation history
- Overlay history.jsonl and SANDBOX.md as file-level bind mounts
- Update credential mount target from ~/.claudebox to ~/.claude
- Add CLAUDE_JSON_FILE (~/.claude.json) detection and conditional bind mount
- Remove stale CLAUDE.md injection logic (D-06: user's real CLAUDE.md used)
- Update dry-run block and print_audit to reflect new mount layout
- Update SANDBOX.md heredoc to remove ~/.claudebox reference
2026-04-13 09:00:53 +00:00

12 KiB

phase plan type wave depends_on files_modified autonomous requirements must_haves
02-env-audit-and-cli-polish 01 execute 1
claudebox.sh
true
UX-03
UX-04
UX-05
truths artifacts key_links
claudebox --check reports pass/fail for bwrap, claude, git, nix, ~/.claudebox, and ANTHROPIC_API_KEY
claudebox --dry-run prints the full bwrap command with all flags and exits without executing
claudebox --yes -y flags are recognized and stored for Plan 02 audit skip
Unknown flags are passed through to claude in CLAUDE_ARGS array
Flag parsing handles multiple claudebox flags in any order
path provides contains
claudebox.sh Refactored flag parsing, --check, --dry-run CHECK_MODE|DRY_RUN|SKIP_AUDIT|CLAUDE_ARGS
from to via pattern
claudebox.sh flag parsing SANDBOX_CMD construction CLAUDE_ARGS array replaces raw $@ CLAUDE_ARGS
Refactor claudebox.sh flag parsing to support multiple flags, then add --check and --dry-run early-exit modes.

Purpose: Foundation for all Phase 2 CLI flags. The current single-flag parser (for/shift/break) cannot handle multiple claudebox flags. This plan refactors it to a while/shift loop that collects claudebox flags and accumulates remaining args in CLAUDE_ARGS. Then adds two diagnostic modes that exit before sandbox launch.

Output: claudebox.sh with working --check, --dry-run, and flag scaffolding for --yes/-y.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/02-env-audit-and-cli-polish/02-CONTEXT.md @.planning/phases/02-env-audit-and-cli-polish/02-RESEARCH.md

@claudebox.sh @flake.nix

Task 1: Refactor flag parsing to while/shift with CLAUDE_ARGS accumulator claudebox.sh claudebox.sh Replace the current flag parsing block (lines 1-9) with a while/shift loop that handles multiple claudebox flags. Define these variables at the top of the script:
SKIP_AUDIT=false
DRY_RUN=false
CHECK_MODE=false
SHELL_MODE=false
CLAUDE_ARGS=()

while (( $# > 0 )); do
  case "$1" in
    --yes|-y) SKIP_AUDIT=true ;;
    --dry-run) DRY_RUN=true ;;
    --check) CHECK_MODE=true ;;
    --shell) SHELL_MODE=true ;;
    --) shift; CLAUDE_ARGS+=("$@"); break ;;
    *) CLAUDE_ARGS+=("$1") ;;
  esac
  shift
done

Per D-08, --yes/-y sets SKIP_AUDIT=true (consumed by Plan 02's audit display).

Then update the SANDBOX_CMD construction (currently lines 70-74) to use CLAUDE_ARGS instead of $@:

if [[ "$SHELL_MODE" == true ]]; then
  SANDBOX_CMD=("$SANDBOX_BASH" "${CLAUDE_ARGS[@]}")
else
  SANDBOX_CMD=("$CLAUDE_BIN" --dangerously-skip-permissions "${CLAUDE_ARGS[@]}")
fi

This ensures unknown flags like --model sonnet pass through to claude correctly. cd /home/toph/code/tools/claudebox && grep -q 'SKIP_AUDIT=false' claudebox.sh && grep -q 'DRY_RUN=false' claudebox.sh && grep -q 'CHECK_MODE=false' claudebox.sh && grep -q 'CLAUDE_ARGS' claudebox.sh && grep -q 'while (( $# > 0 ))' claudebox.sh && echo "PASS: flag parsing refactored" <acceptance_criteria> - claudebox.sh contains SKIP_AUDIT=false variable declaration - claudebox.sh contains DRY_RUN=false variable declaration - claudebox.sh contains CHECK_MODE=false variable declaration - claudebox.sh contains SHELL_MODE=false variable declaration - claudebox.sh contains CLAUDE_ARGS=() array declaration - claudebox.sh contains while (( $# > 0 )) loop (not for arg in) - claudebox.sh contains CLAUDE_ARGS+=("$1") for unknown flag accumulation - SANDBOX_CMD uses "${CLAUDE_ARGS[@]}" not "$@" - The old for arg in "$@" parsing block is removed </acceptance_criteria> Flag parsing handles --yes, -y, --dry-run, --check, --shell, and passes unknown flags to CLAUDE_ARGS. SANDBOX_CMD uses CLAUDE_ARGS.

Task 2: Add --dry-run mode that prints full bwrap command and exits claudebox.sh claudebox.sh Per D-09 (UX-04), add a --dry-run handler after the SANDBOX_CMD construction and before the `exec bwrap` line. When DRY_RUN=true, print the complete bwrap invocation in a readable multiline format to stderr, then exit 0. Do NOT prompt for confirmation (--dry-run implies --yes per research open question 1).

Insert this block right before exec bwrap:

# --dry-run: print the bwrap command without executing (D-09, UX-04)
if [[ "$DRY_RUN" == true ]]; then
  {
    echo "bwrap \\"
    echo "  --clearenv \\"
    # Print each ENV_ARGS entry
    local_i=0
    while (( local_i < ${#ENV_ARGS[@]} )); do
      printf '  %s %s %q \\\n' "${ENV_ARGS[$local_i]}" "${ENV_ARGS[$((local_i+1))]}" "${ENV_ARGS[$((local_i+2))]}"
      (( local_i += 3 ))
    done
    echo "  --tmpfs / \\"
    echo "  --proc /proc \\"
    echo "  --dev /dev \\"
    echo "  --tmpfs /tmp \\"
    echo "  --ro-bind /nix/store /nix/store \\"
    echo "  --bind /nix/var/nix /nix/var/nix \\"
    echo "  --ro-bind /etc/resolv.conf /etc/resolv.conf \\"
    echo "  --ro-bind /etc/ssl /etc/ssl \\"
    echo "  --ro-bind /etc/static /etc/static \\"
    echo "  --ro-bind /etc/passwd /etc/passwd \\"
    echo "  --ro-bind /etc/group /etc/group \\"
    echo "  --ro-bind /etc/hosts /etc/hosts \\"
    echo "  --ro-bind /etc/nsswitch.conf /etc/nsswitch.conf \\"
    echo "  --ro-bind /etc/nix /etc/nix \\"
    printf '  --symlink %q /usr/bin/env \\\n' "$(readlink -f "$(command -v env)")"
    echo "  --tmpfs $HOME \\"
    echo "  --bind $HOME/.claudebox $HOME/.claude \\"
    printf '  --ro-bind %q %s/.gitconfig \\\n' "$GITCONFIG_TMP" "$HOME"
    echo "  --bind $CWD $CWD \\"
    echo "  --chdir $CWD \\"
    printf '  -- %s\n' "${SANDBOX_CMD[*]}"
  } >&2
  exit 0
fi

Note: The ENV_ARGS array is structured as triplets (--setenv key value), so iterate in steps of 3. Use printf %q for values that might contain special chars. The filesystem mount flags mirror the actual exec bwrap call exactly -- if the bwrap call changes, this must be updated too.

Important: Since writeShellApplication uses set -euo pipefail and shellcheck, avoid using local outside functions. Use a unique variable name like dry_run_i instead of local_i for the loop counter. cd /home/toph/code/tools/claudebox && grep -q 'DRY_RUN.*true' claudebox.sh && grep -q 'dry.run' claudebox.sh && grep -c 'exit 0' claudebox.sh | grep -q '[1-9]' && echo "PASS: dry-run handler present" <acceptance_criteria> - claudebox.sh contains an if [[ "$DRY_RUN" == true ]] block - The dry-run block prints bwrap \ as first line to stderr - The dry-run block prints --clearenv \ on its own line - The dry-run block iterates ENV_ARGS in steps of 3 to print --setenv triplets - The dry-run block prints all filesystem mount flags matching the actual exec bwrap call - The dry-run block ends with exit 0 - All dry-run output goes to stderr (>&2) - nix build succeeds (shellcheck passes) </acceptance_criteria> Running claudebox --dry-run prints the full bwrap command in multiline format to stderr and exits 0 without launching the sandbox.

Task 3: Add --check mode that verifies prerequisites and exits claudebox.sh claudebox.sh Per D-10 (UX-05), add a --check handler as an early exit right after flag parsing and before any binary resolution or env construction. When CHECK_MODE=true, run diagnostic checks and exit.

Insert this block immediately after the flag parsing while/shift loop:

# --check: verify prerequisites and exit (D-10, UX-05)
if [[ "$CHECK_MODE" == true ]]; then
  pass=true
  green=$'\033[32m' red=$'\033[31m' yellow=$'\033[33m' reset=$'\033[0m'

  check_cmd() {
    if command -v "$1" &>/dev/null; then
      echo "${green}OK${reset}    $1" >&2
    else
      echo "${red}FAIL${reset}  $1 -- not found" >&2
      pass=false
    fi
  }

  echo "claudebox prerequisites:" >&2
  echo "" >&2
  check_cmd bwrap
  check_cmd claude
  check_cmd git
  check_cmd curl
  check_cmd nix

  if [[ -d "$HOME/.claudebox" ]]; then
    echo "${green}OK${reset}    ~/.claudebox exists" >&2
  else
    echo "${red}FAIL${reset}  ~/.claudebox -- not found (will be created on first run)" >&2
  fi

  if [[ -v ANTHROPIC_API_KEY ]]; then
    echo "${green}OK${reset}    ANTHROPIC_API_KEY is set" >&2
  else
    echo "${yellow}WARN${reset}  ANTHROPIC_API_KEY is not set" >&2
  fi

  echo "" >&2
  if [[ "$pass" == true ]]; then
    echo "${green}All checks passed.${reset}" >&2
    exit 0
  else
    echo "${red}Some checks failed.${reset}" >&2
    exit 1
  fi
fi

The check verifies: bwrap, claude, git, curl, nix (required binaries), ~/.claudebox directory, and ANTHROPIC_API_KEY (warn only, not a hard failure). Exit 0 if all required checks pass, exit 1 if any fail. All output to stderr per D-07. cd /home/toph/code/tools/claudebox && grep -q 'CHECK_MODE.*true' claudebox.sh && grep -q 'check_cmd' claudebox.sh && grep -q 'prerequisites' claudebox.sh && echo "PASS: check handler present" <acceptance_criteria> - claudebox.sh contains an if [[ "$CHECK_MODE" == true ]] block - The check block is positioned BEFORE binary resolution (command -v bash, command -v claude) - The check block tests for: bwrap, claude, git, curl, nix via command -v - The check block tests for $HOME/.claudebox directory existence - The check block tests for ANTHROPIC_API_KEY with WARN (not FAIL) - The check block prints colored OK/FAIL/WARN indicators to stderr - The check block exits 0 on all-pass, exits 1 on any FAIL - nix build succeeds (shellcheck passes) </acceptance_criteria> Running claudebox --check prints pass/fail for each prerequisite to stderr and exits with appropriate code.

<threat_model>

Trust Boundaries

Boundary Description
host env -> --dry-run output Env var values printed to stderr may contain secrets

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-02-01 Information Disclosure --dry-run printing env values accept --dry-run shows env values unmasked because the user explicitly asked to see the full command. Plan 02 adds masking for the audit display. The dry-run user is debugging, not reviewing for leaks.
T-02-02 Information Disclosure --check printing ANTHROPIC_API_KEY status mitigate Only print presence/absence, never the value. Use [[ -v ANTHROPIC_API_KEY ]] not echo of value.
</threat_model>
1. `grep -c 'CLAUDE_ARGS' claudebox.sh` returns >= 3 (declaration + accumulation + usage) 2. `grep 'DRY_RUN\|CHECK_MODE\|SKIP_AUDIT' claudebox.sh` shows all three flag variables 3. `nix build` succeeds (shellcheck validation)

<success_criteria>

  • Flag parsing supports --yes, -y, --dry-run, --check, --shell, and -- separator
  • Unknown flags accumulate in CLAUDE_ARGS and pass through to claude
  • --check exits early with colored diagnostic output
  • --dry-run prints full bwrap command and exits
  • shellcheck passes via nix build </success_criteria>
After completion, create `.planning/phases/02-env-audit-and-cli-polish/02-01-SUMMARY.md`