# CLAUDE.md ## Project Overview This repository contains a dual-mode setup script for creating OrbStack-based development sandboxes tailored for Claude Code with Elixir/Erlang, browser automation, and PostgreSQL. ## Repository Structure ``` setup_env.sh - Main script (macOS host mode + Linux VM provisioning mode) config.env.example - Example credentials file config.env - User credentials (gitignored, created on first run) .gitignore - Ignores config.env README.md - User-facing documentation 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) - **Language versions**: Configured at the top of `setup_env.sh` as variables (`ERLANG_VERSION`, `ELIXIR_VERSION`) - **PostgreSQL auth**: Peer for local socket, trust for localhost TCP (127.0.0.1), scram-sha-256 for host network (192.168.0.0/16) - **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` is created once and reused for all VMs ## 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 ### Testing There are no automated tests. To test changes: 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` ### Security Considerations - `config.env` is chmod 600 and gitignored - PostgreSQL remote access uses scram-sha-256 (not trust) - The script refuses to run as root inside the VM - VNC password is required (min 6 chars) - VNC binds to all interfaces (`-localhost no`) to allow connections from the macOS host — this is intentional for the OrbStack use case