diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 0d4fb1a..5bab821 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -89,43 +89,43 @@ | Requirement | Phase | Status | |-------------|-------|--------| -| SAND-01 | — | Pending | -| SAND-02 | — | Pending | -| SAND-03 | — | Pending | -| SAND-04 | — | Pending | -| SAND-05 | — | Pending | -| SAND-06 | — | Pending | -| SAND-07 | — | Pending | -| SAND-08 | — | Pending | -| SAND-09 | — | Pending | -| SAND-10 | — | Pending | -| SAND-11 | — | Pending | -| SAND-12 | — | Pending | -| SAND-13 | — | Pending | -| SAND-14 | — | Pending | -| SAND-15 | — | Pending | -| TOOL-01 | — | Pending | -| TOOL-02 | — | Pending | -| TOOL-03 | — | Pending | -| UX-01 | — | Pending | -| UX-02 | — | Pending | -| UX-03 | — | Pending | -| UX-04 | — | Pending | -| UX-05 | — | Pending | -| UX-06 | — | Pending | -| AWARE-01 | — | Pending | -| AWARE-02 | — | Pending | -| GIT-01 | — | Pending | -| GIT-02 | — | Pending | -| NIX-01 | — | Pending | -| NIX-02 | — | Pending | -| NIX-03 | — | Pending | +| SAND-01 | Phase 1 | Pending | +| SAND-02 | Phase 1 | Pending | +| SAND-03 | Phase 1 | Pending | +| SAND-04 | Phase 1 | Pending | +| SAND-05 | Phase 1 | Pending | +| SAND-06 | Phase 1 | Pending | +| SAND-07 | Phase 1 | Pending | +| SAND-08 | Phase 1 | Pending | +| SAND-09 | Phase 1 | Pending | +| SAND-10 | Phase 1 | Pending | +| SAND-11 | Phase 1 | Pending | +| SAND-12 | Phase 1 | Pending | +| SAND-13 | Phase 1 | Pending | +| SAND-14 | Phase 1 | Pending | +| SAND-15 | Phase 1 | Pending | +| TOOL-01 | Phase 1 | Pending | +| TOOL-02 | Phase 1 | Pending | +| TOOL-03 | Phase 1 | Pending | +| UX-01 | Phase 2 | Pending | +| UX-02 | Phase 2 | Pending | +| UX-03 | Phase 2 | Pending | +| UX-04 | Phase 2 | Pending | +| UX-05 | Phase 2 | Pending | +| UX-06 | Phase 1 | Pending | +| AWARE-01 | Phase 3 | Pending | +| AWARE-02 | Phase 3 | Pending | +| GIT-01 | Phase 1 | Pending | +| GIT-02 | Phase 1 | Pending | +| NIX-01 | Phase 1 | Pending | +| NIX-02 | Phase 1 | Pending | +| NIX-03 | Phase 1 | Pending | **Coverage:** - v1 requirements: 31 total -- Mapped to phases: 0 -- Unmapped: 31 ⚠️ +- Mapped to phases: 31 +- Unmapped: 0 --- *Requirements defined: 2026-04-09* -*Last updated: 2026-04-09 after initial definition* +*Last updated: 2026-04-09 after roadmap creation* diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md new file mode 100644 index 0000000..56e15d0 --- /dev/null +++ b/.planning/ROADMAP.md @@ -0,0 +1,73 @@ +# Roadmap: claudebox + +## Overview + +claudebox is a Nix-packaged bwrap sandbox wrapper for Claude Code. The roadmap moves from a working sandbox (Phase 1) through CLI polish (Phase 2) to sandbox-aware prompting (Phase 3). Phase 1 is the bulk of the work -- once Claude runs inside bwrap with env isolation, filesystem isolation, and tool provisioning, the remaining phases add UX and developer experience improvements. + +## Phases + +**Phase Numbering:** +- Integer phases (1, 2, 3): Planned milestone work +- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED) + +Decimal phases appear between their surrounding integers in numeric order. + +- [ ] **Phase 1: Minimal Viable Sandbox** - Working claudebox command that launches Claude in bwrap with full isolation and tool provisioning +- [ ] **Phase 2: Env Audit and CLI Polish** - Pre-launch env review, --yes, --dry-run, and --check flags +- [ ] **Phase 3: Sandbox-Aware Prompting** - Injected CLAUDE.md so Claude knows its capabilities and constraints + +## Phase Details + +### Phase 1: Minimal Viable Sandbox +**Goal**: User can run `claudebox` in any project directory and get a fully functional Claude Code session with secrets invisible +**Depends on**: Nothing (first phase) +**Requirements**: SAND-01, SAND-02, SAND-03, SAND-04, SAND-05, SAND-06, SAND-07, SAND-08, SAND-09, SAND-10, SAND-11, SAND-12, SAND-13, SAND-14, SAND-15, TOOL-01, TOOL-02, TOOL-03, GIT-01, GIT-02, NIX-01, NIX-02, NIX-03, UX-06 +**Success Criteria** (what must be TRUE): + 1. Running `nix run` or `nix profile install` produces a working `claudebox` command + 2. `claudebox` launches Claude Code inside bwrap; `env` inside the sandbox shows only allowlisted variables (no SSH_AUTH_SOCK, AWS_PROFILE, etc.) + 3. Secret paths (~/.ssh, ~/.gnupg, ~/.aws, ~/.config/gcloud, age keys, /var/lib/tailscale) are not visible inside the sandbox + 4. Claude can run `curl https://example.com`, `git status`, `, jq --help` (comma), and `nix shell nixpkgs#python3 -c python3 --version` inside the sandbox + 5. Ctrl+C terminates the session cleanly; exit code from Claude passes through to the caller +**Plans**: TBD + +Plans: +- [ ] 01-01: TBD +- [ ] 01-02: TBD +- [ ] 01-03: TBD + +### Phase 2: Env Audit and CLI Polish +**Goal**: User can review exactly what enters the sandbox before launch, and has diagnostic tools for troubleshooting +**Depends on**: Phase 1 +**Requirements**: UX-01, UX-02, UX-03, UX-04, UX-05 +**Success Criteria** (what must be TRUE): + 1. Running `claudebox` without `--yes` prints all env vars being passed into the sandbox and prompts for confirmation before proceeding + 2. Running `claudebox --yes` or `claudebox -y` skips the env audit and launches immediately + 3. Running `claudebox --dry-run` prints the full bwrap command without executing it + 4. Running `claudebox --check` reports whether bwrap exists, required Nix packages are available, and ~/.claudebox exists +**Plans**: TBD + +Plans: +- [ ] 02-01: TBD + +### Phase 3: Sandbox-Aware Prompting +**Goal**: Claude inside the sandbox knows it is sandboxed, how to install tools, and what is unavailable +**Depends on**: Phase 1 +**Requirements**: AWARE-01, AWARE-02 +**Success Criteria** (what must be TRUE): + 1. First run of `claudebox` creates a default CLAUDE.md in ~/.claudebox/ if none exists + 2. The injected CLAUDE.md tells Claude it is in a bwrap sandbox, how to use comma (`, `) and `nix shell` for tool installation, and that SSH/GPG/cloud credentials are unavailable +**Plans**: TBD + +Plans: +- [ ] 03-01: TBD + +## Progress + +**Execution Order:** +Phases execute in numeric order: 1 -> 2 -> 3 + +| Phase | Plans Complete | Status | Completed | +|-------|----------------|--------|-----------| +| 1. Minimal Viable Sandbox | 0/3 | Not started | - | +| 2. Env Audit and CLI Polish | 0/1 | Not started | - | +| 3. Sandbox-Aware Prompting | 0/1 | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md new file mode 100644 index 0000000..f8422f1 --- /dev/null +++ b/.planning/STATE.md @@ -0,0 +1,59 @@ +# Project State + +## Project Reference + +See: .planning/PROJECT.md (updated 2026-04-09) + +**Core value:** Secrets never enter the Claude Code environment +**Current focus:** Phase 1 - Minimal Viable Sandbox + +## Current Position + +Phase: 1 of 3 (Minimal Viable Sandbox) +Plan: 0 of 3 in current phase +Status: Ready to plan +Last activity: 2026-04-09 -- Roadmap created + +Progress: [░░░░░░░░░░] 0% + +## Performance Metrics + +**Velocity:** +- Total plans completed: 0 +- Average duration: - +- Total execution time: 0 hours + +**By Phase:** + +| Phase | Plans | Total | Avg/Plan | +|-------|-------|-------|----------| +| - | - | - | - | + +**Recent Trend:** +- Last 5 plans: - +- Trend: - + +*Updated after each plan completion* + +## Accumulated Context + +### Decisions + +Decisions are logged in PROJECT.md Key Decisions table. +Recent decisions affecting current work: + +- None yet. + +### Pending Todos + +None yet. + +### Blockers/Concerns + +- Research flags: verify `comma-with-db` packaging in current nix-index-database flake, verify `--clearenv` in nixpkgs bwrap version, test daemon socket bind-mount behavior + +## Session Continuity + +Last session: 2026-04-09 +Stopped at: Roadmap created, ready to plan Phase 1 +Resume file: None diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d9d70c3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,135 @@ + +## Project + +**claudebox** + +A Nix derivation that produces a `claudebox` wrapper script for Claude Code. It runs Claude inside a bubblewrap sandbox with an allowlisted environment, explicit filesystem mounts, and a minimal PATH — keeping SSH keys, GPG/age secrets, cloud tokens, and Tailscale state completely invisible to the AI agent. + +**Core Value:** Secrets never enter the Claude Code environment. If a secret is accessible inside the sandbox, it's a bug. + +### Constraints + +- **Stack**: Nix derivation + shell script — no Docker, no systemd, no external dependencies beyond nixpkgs +- **Sandbox**: Own bwrap call — not delegating to Claude Code's `--sandbox` or Nix's build sandbox +- **Env model**: Allowlist, not denylist — start empty, add explicitly + + + +## Technology Stack + +## Recommended Stack +### Core: Nix Derivation via `writeShellApplication` +| Technology | Version | Purpose | Why | Confidence | +|------------|---------|---------|-----|------------| +| `writeShellApplication` | nixpkgs stable | Produce the `claudebox` wrapper script | Generates a shellcheck-validated bash script in the Nix store with runtime PATH wired to declared `runtimeInputs`. Superior to `writeShellScriptBin` because it runs shellcheck at build time and sets `set -euo pipefail` automatically. | HIGH | +| `bubblewrap` (bwrap) | 0.9.x+ | Sandbox runtime | Unprivileged user-namespace sandbox. In nixpkgs as `bubblewrap`. No setuid needed on NixOS (user namespaces enabled by default). | HIGH | +| `claude-code` | CLI | The wrapped tool | Provided by Anthropic's npm/standalone installer. Assumed pre-installed or passed as input. | HIGH | +### Runtime Dependencies (runtimeInputs) +| Package | Purpose | Why This One | Confidence | +|---------|---------|--------------|------------| +| `bubblewrap` | Sandbox | The whole point | HIGH | +| `coreutils` | Basic shell utils | `env`, `cat`, `echo`, `mkdir`, etc. | HIGH | +| `git` | Version control | Claude Code requires git for repo operations | HIGH | +| `curl` | HTTP requests | Claude Code's MCP and tool use | HIGH | +| `jq` | JSON processing | Env audit display, config manipulation | HIGH | +| `ripgrep` | Search | Claude Code's preferred grep | HIGH | +| `fd` | File finding | Claude Code's preferred find | HIGH | +| `nix` | Package manager | Required for `nix shell` inside sandbox | HIGH | +| `comma` | On-demand packages | Runs `nix shell nixpkgs# -c ` via `, ` syntax | HIGH | +| `nix-index` | Package database | Required by comma to resolve command -> package mapping | HIGH | +| `bash` | Shell | bwrap needs a shell to exec into | HIGH | +| `nodejs` | Runtime | Claude Code is a Node.js application | HIGH | +### NOT in runtimeInputs (Important) +| Package | Why Excluded | +|---------|-------------| +| `gnupg` | Secret material -- explicitly hidden | +| `openssh` | Secret material -- explicitly hidden | +| `age` / `agenix` | Secret material -- explicitly hidden | +| `tailscale` | Infrastructure access -- explicitly hidden | +## Key Nix Functions +### `writeShellApplication` -- Use This +### Why NOT These Alternatives +| Alternative | Why Not | +|-------------|---------| +| `writeShellScriptBin` | No shellcheck, no automatic `set -euo pipefail`, no `runtimeInputs` wiring. You'd have to manually construct PATH. | +| `makeWrapper` / `wrapProgram` | Designed for wrapping existing binaries with env vars/flags. Overkill and wrong abstraction -- we're writing a new script, not patching an existing binary. | +| `symlinkJoin` + `makeWrapper` | Pattern for combining multiple derivations. Not needed -- we have one script. | +| `stdenv.mkDerivation` | Too heavy. `writeShellApplication` is a specialized shortcut for exactly this use case. | +| `runCommand` / `writeScript` | Lower-level, no shellcheck, no runtimeInputs. | +## Bubblewrap Flags +### Namespace Isolation +### Filesystem Mounts +### Mount Ordering Matters +### Environment Handling +### Process Execution +## Comma and nix-index +### How Comma Works +### Packaging in nixpkgs +- `pkgs.comma` -- the comma binary itself +- `pkgs.nix-index` -- the indexer that builds/queries the database +- Database: comma needs a pre-built index. Two options: +### For claudebox +- Mount `~/.cache/nix-index` read-only into the sandbox (if using nix-index-database on the host) +- Or use the `nix-index-database` flake's `comma-with-db` package which bundles the database +## Flake Structure +### Why `builtins.readFile` for the Script Body +- Shell syntax highlighting in editors +- Shellcheck can run independently +- Easier to iterate on the script without touching Nix expressions +- `writeShellApplication` still runs shellcheck on it at build time +## PATH Construction Inside Sandbox +## Nix Store Access Inside Sandbox +## Testing Strategy +## Sources +- Training data knowledge of nixpkgs `writeShellApplication` (stable API since 2022) +- Training data knowledge of bubblewrap (stable API, project is mature) +- Training data knowledge of comma/nix-index-database (nix-community project) +- **All versions should be verified against current nixpkgs before implementation** +## Verification Checklist (for implementation phase) +- [ ] Confirm `bubblewrap` version in current nixpkgs channel +- [ ] Confirm `comma` and `nix-index-database` flake are current and compatible +- [ ] Test Nix daemon socket access through bwrap bind mount +- [ ] Test mount ordering with CWD under $HOME +- [ ] Confirm `--clearenv` + `--setenv` pattern works with Claude Code (it may need vars we haven't listed) +- [ ] Check if Claude Code needs `~/.local` or `~/.config` beyond `~/.claude` + + + +## Conventions + +Conventions not yet established. Will populate as patterns emerge during development. + + + +## Architecture + +Architecture not yet mapped. Follow existing patterns found in the codebase. + + + +## Project Skills + +No project skills found. Add skills to any of: `.claude/skills/`, `.agents/skills/`, `.cursor/skills/`, or `.github/skills/` with a `SKILL.md` index file. + + + +## GSD Workflow Enforcement + +Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync. + +Use these entry points: +- `/gsd-quick` for small fixes, doc updates, and ad-hoc tasks +- `/gsd-debug` for investigation and bug fixing +- `/gsd-execute-phase` for planned phase work + +Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it. + + + + + +## Developer Profile + +> Profile not yet configured. Run `/gsd-profile-user` to generate your developer profile. +> This section is managed by `generate-claude-profile` -- do not edit manually. +