# CLAUDE.md ## Project Memory This project uses three documentation files as persistent memory. **You must keep these files up to date when making changes:** | File | Purpose | Update When | |------|---------|-------------| | `CLAUDE.md` | Technical context for Claude Code sessions | Adding new patterns, conventions, or implementation details | | `CHANGELOG.md` | Version history following Keep a Changelog format | Every commit (add entry, bump version) | | `README.md` | User-facing documentation | Changing user-visible behavior, adding features, or modifying usage | ### Versioning Rules - Use semantic versioning (MAJOR.MINOR.PATCH) - Increment PATCH for bug fixes and minor updates - Increment MINOR for new features and enhancements - Increment MAJOR for breaking changes - Tag every commit with its version: `git tag -a vX.Y.Z -m "Description"` ## Project Overview This repository provides scripts for creating isolated development sandboxes for Claude Code on macOS (OrbStack) and Windows (Hyper-V). Both platforms offer full VM isolation for safely running AI coding assistants with elevated permissions. **Supported platforms:** - **macOS**: OrbStack VMs (ARM64 Apple Silicon) - **Windows**: Hyper-V VMs (maximum security, stronger than WSL2) ## Repository Structure ``` setup_env.sh - macOS script (OrbStack host mode + Linux VM provisioning) setup_env_windows.ps1 - Windows script (Hyper-V with cloud-init) WINDOWS_PLAN.md - Windows implementation plan and security rationale config.env.example - Example credentials file config.env - User credentials (gitignored, created on first run) .gitignore - Ignores config.env README.md - User-facing documentation CHANGELOG.md - Version history (Keep a Changelog format) CLAUDE.md - This file (context for Claude Code sessions) ``` ## How the Script Works The script has two modes, detected via `uname -s`: - **Darwin (macOS)**: Orchestrator mode. Checks for OrbStack, reads/creates `config.env`, creates a VM, copies itself in, runs itself inside the VM with `--non-interactive`. - **Linux (VM)**: Provisioning mode. Installs all tools, configures PostgreSQL, sets up VNC, installs Claude Code plugins. This means `./setup_env.sh my-vm` on macOS does everything end-to-end. ## Key Technical Details - **Target environment**: OrbStack Ubuntu VM on macOS Apple Silicon (ARM64) - **Version manager**: mise (manages Node.js, Erlang, Elixir) - **Versions**: All tools use latest by default. Erlang/Elixir versions can be configured at the top of `setup_env.sh` (`ERLANG_VERSION`, `ELIXIR_VERSION`) - set to "latest" or pin to specific versions - **PostgreSQL auth**: Peer for local socket, scram-sha-256 for all TCP connections (localhost and network) - **Browser**: Chromium (no Chrome ARM64 Linux builds exist), symlinked to `google-chrome` - **VNC**: TigerVNC + XFCE on display :1 (port 5901), controlled via `vnc-start`/`vnc-stop` helpers in `~/bin` - **Shared credentials**: `config.env` stores git name/email only; VNC password is prompted each time (never stored) ## Working on This Project ### Editing setup_env.sh - The macOS host-mode block is at the top (inside the `if [[ "$(uname -s)" == "Darwin" ]]` block) - The Linux VM-mode block is everything after that conditional - Version numbers are defined as variables at the top (`ERLANG_VERSION`, `ELIXIR_VERSION`) - All installation steps must be idempotent (safe to run multiple times) - Use `log_info`, `log_warn`, `log_error` helpers for output - New apt packages go in the base dependencies section - New Claude plugins go in the appropriate array (`ANTHROPIC_PLUGINS` or `SUPERPOWERS_PLUGINS`) - Redirect verbose output to `$LOG_FILE`, show only meaningful progress to the user - The `LOG_FILE` variable is only set in VM mode (not available in macOS host mode) - Each optional component is wrapped in `prompt_install "Name" "Description"` — this handles both interactive prompts and `--yes`/`--non-interactive` auto-accept - Track dependency flags (`INSTALLED_NODE`, `INSTALLED_CHROMIUM`) to skip dependent components gracefully ### Script Conventions - `set -euo pipefail` is enforced - handle potential failures with `|| true` or explicit checks - Use `command_exists` to check for already-installed tools - Use `apt-get` (not `apt`) for scripting reliability - Quote all variable expansions - Use `find` for PostgreSQL config paths (version-agnostic) - The `--non-interactive` flag is for VM mode (implies `--yes`); macOS mode always uses config.env - `--yes`/`-y` accepts all components without prompting but still allows interactive credential entry - `MISE_GLOBAL_CONFIG_FILE` and `MISE_CONFIG_DIR` are set to prevent OrbStack host-mount config pollution ### Security Patterns (macOS/Linux) - **Input validation**: Use `validate_vm_name()`, `validate_vnc_password()`, `validate_safe_input()` for all user inputs - **Safe config loading**: Use `load_config_safely()` which parses key=value pairs without shell execution (never `source`) - **Credential passing**: Base64-encode values before passing to VM via `orb run` to prevent shell injection - **Download helper**: Use `download_verified_binary()` for binary downloads (supports optional checksum verification) - **Temp files**: Always use `mktemp` with restrictive permissions (`chmod 600`/`700`) - **Symlinks**: Check with `[ ! -e path ]` (not `[ ! -L path ]`) to avoid overwriting existing files - **Architecture detection**: Use `detect_architecture()` for portable binary downloads ### Editing setup_env_windows.ps1 - PowerShell script for Windows Hyper-V VM creation - Uses Ubuntu cloud images with cloud-init for automated provisioning - Supports ARM64 architecture detection for Windows on ARM - Skip parameters: `-SkipVNC`, `-SkipPostgreSQL`, `-SkipOllama`, `-SkipPlaywright` - ISO creation fallback chain: oscdimg → WSL genisoimage → IMAPI2 COM - Log file created at `$env:TEMP\setup_env_windows_.log` ### Security Patterns (Windows) - **Input validation**: Use `Test-GitName`, `Test-GitEmail`, `Test-VMPassword`, `Test-VNCPassword` for all user inputs - **Config file security**: ACL set to owner-only before writing content (no race condition) - **Config validation**: Loaded config.env values are re-validated to detect tampering - **Checksum verification**: Ubuntu image verified against SHA256 checksums from Canonical - **Cleanup on failure**: `Invoke-Cleanup` removes partial VM, disk, and ISO on errors - **Hosts file backup**: Created before modification, removed on success, kept on failure - **Cloud-init ISO**: Contains passwords in plaintext; user reminded to remove after first boot ### Testing There are no automated tests. To test changes: **macOS:** 1. Remove an existing test VM: `orb delete test-sandbox` 2. Run: `./setup_env.sh test-sandbox` 3. Verify provisioning completes 4. Run again to verify idempotency: `ssh test-sandbox@orb -- bash /tmp/setup_env.sh --non-interactive` (will need env vars) 5. Test VNC: `ssh test-sandbox@orb -- vnc-start`, then `open vnc://test-sandbox.orb.local:5901` 6. Clean up: `orb delete test-sandbox` **Windows:** 1. Remove existing test VM: `Stop-VM -Name test-sandbox -Force; Remove-VM -Name test-sandbox -Force` 2. Run: `.\setup_env_windows.ps1 -VMName test-sandbox` 3. Verify provisioning completes (check `/var/log/provision.log` in VM) 4. Test SSH: `ssh -i $env:USERPROFILE\.ssh\id_ed25519_test-sandbox dev@test-sandbox.local` 5. Clean up: `Stop-VM -Name test-sandbox -Force; Remove-VM -Name test-sandbox -Force` ### Security Considerations - `config.env` is chmod 600 / owner-only ACL and gitignored (stores git name/email only, never passwords) - PostgreSQL uses scram-sha-256 for all TCP connections (peer auth for local socket) - The script refuses to run as root inside the VM - VNC password is required (min 8 chars), validated to block shell metacharacters, never stored - VNC binds to all interfaces (`-localhost no`) to allow connections from the host — this is intentional and documented - All user inputs are validated before use to prevent command injection - External scripts (mise, ollama) are downloaded to temp files and validated before execution - Host filesystem access is disabled inside the VM for isolation - Windows: Ubuntu image downloads are verified against SHA256 checksums