README.md: - Add skip parameters example (-SkipVNC, -SkipOllama) - Document VNC password prompt and minimum length - Update requirements to show ISO creation fallbacks CLAUDE.md: - Add Windows script editing section - Add Windows security patterns section - Add Windows testing instructions - Update VNC password minimum from 6 to 8 chars - Document checksum verification for Windows Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
376 lines
11 KiB
Markdown
376 lines
11 KiB
Markdown
# Secure AI Coding Sandboxes
|
|
|
|
Disposable, isolated Linux VMs for running Claude Code with `--dangerously-skip-permissions`. One command creates a fully provisioned environment. Blow it away and recreate it in minutes.
|
|
|
|
The VM is a real Linux machine with its own filesystem, network, and process space — complete isolation from your host with easy access for development.
|
|
|
|
| Platform | Script | Isolation Level |
|
|
|----------|--------|-----------------|
|
|
| **macOS** | `setup_env.sh` (OrbStack) | Full VM isolation |
|
|
| **Windows** | `setup_env_windows.ps1` (Hyper-V) | Full VM isolation |
|
|
|
|
## Why This Exists
|
|
|
|
Running `claude --dangerously-skip-permissions` on your host machine means Claude can execute arbitrary commands, install packages, modify system files, and access everything on your disk. That's powerful for autonomous coding but risky on a machine with your SSH keys, credentials, and personal files.
|
|
|
|
These scripts create throwaway VMs where Claude can run unrestricted:
|
|
|
|
- **Isolated filesystem** — Claude can't touch your host files, keys, or configs
|
|
- **Isolated network** — services run on their own IP, no port conflicts with your host
|
|
- **Disposable** — delete and recreate in one command
|
|
- **Multiple VMs** — run separate sandboxes per project with shared git credentials
|
|
- **Full access from host** — edit files in your editor, browse databases, view running apps
|
|
|
|
## Quick Start (macOS)
|
|
|
|
```bash
|
|
# Create a sandbox, SSH in, run Claude unrestricted
|
|
./setup_env.sh my-project
|
|
ssh my-project@orb
|
|
claude --dangerously-skip-permissions
|
|
```
|
|
|
|
If anything goes wrong: `orb delete my-project && ./setup_env.sh my-project`
|
|
|
|
### macOS Setup
|
|
|
|
```bash
|
|
# Create and provision a VM (one command from macOS)
|
|
./setup_env.sh my-sandbox
|
|
```
|
|
|
|
On first run, you'll be prompted for git commit author name and email. These are saved to `config.env` and reused for all future VMs. You'll also get an interactive checklist to select which components to install. If you select VNC, you'll be prompted for a VNC password (this is never stored and must be entered each time).
|
|
|
|
```bash
|
|
# Create additional VMs — reuses config.env, shows component picker
|
|
./setup_env.sh my-other-project
|
|
./setup_env.sh elixir-playground
|
|
```
|
|
|
|
When run manually inside a VM, you're prompted for each component individually:
|
|
|
|
```bash
|
|
# Inside a VM — interactive, prompts per component
|
|
./setup_env.sh
|
|
|
|
# Inside a VM — accept all without prompting
|
|
./setup_env.sh -y
|
|
./setup_env.sh --yes
|
|
```
|
|
|
|
### macOS Requirements
|
|
|
|
- macOS with Apple Silicon (ARM64)
|
|
- [OrbStack](https://orbstack.dev) installed (`brew install orbstack`)
|
|
|
|
## Quick Start (Windows)
|
|
|
|
```powershell
|
|
# Run as Administrator
|
|
.\setup_env_windows.ps1 -VMName my-project
|
|
|
|
# Connect via SSH (after provisioning)
|
|
ssh -i $env:USERPROFILE\.ssh\id_ed25519_my-project dev@my-project.local
|
|
|
|
# Run Claude unrestricted
|
|
claude --dangerously-skip-permissions
|
|
```
|
|
|
|
If anything goes wrong: `Remove-VM -Name my-project -Force` then run the script again.
|
|
|
|
### Windows Setup
|
|
|
|
```powershell
|
|
# Create and provision a VM (run as Administrator)
|
|
.\setup_env_windows.ps1 -VMName my-sandbox
|
|
|
|
# Customize resources
|
|
.\setup_env_windows.ps1 -VMName my-sandbox -MemoryGB 16 -DiskGB 100 -CPUs 8
|
|
|
|
# Skip optional components
|
|
.\setup_env_windows.ps1 -VMName my-sandbox -SkipVNC -SkipOllama
|
|
```
|
|
|
|
On first run, you'll be prompted for:
|
|
- Git commit author name and email (saved to `config.env`)
|
|
- VM user password (min 8 chars, not stored)
|
|
- VNC password if VNC is enabled (min 8 chars, not stored)
|
|
|
|
### Windows Requirements
|
|
|
|
- Windows 10/11 Pro, Enterprise, or Education (Hyper-V not available on Home)
|
|
- Hyper-V enabled: `Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All`
|
|
- Administrator privileges
|
|
- One of: Windows ADK, WSL with genisoimage, or Windows 10+ (IMAPI2 fallback)
|
|
|
|
### Why Hyper-V (not WSL2)?
|
|
|
|
WSL2 is convenient but provides weaker isolation:
|
|
- Shares kernel with all WSL2 instances
|
|
- Host filesystem mounted by default
|
|
- Can launch Windows executables from Linux
|
|
- Network traffic bypasses Windows firewall
|
|
|
|
Hyper-V provides **maximum security**:
|
|
- Separate kernel per VM
|
|
- Complete filesystem isolation
|
|
- Own network stack
|
|
- No Windows integration by default
|
|
|
|
## What Gets Installed
|
|
|
|
All components are optional — deselect what you don't need in the interactive picker.
|
|
|
|
| Tool | Version | Purpose |
|
|
|------|---------|---------|
|
|
| mise | latest | Version manager for runtimes |
|
|
| Node.js | LTS | JavaScript runtime |
|
|
| Erlang | latest | BEAM VM |
|
|
| Elixir | latest | Elixir language |
|
|
| Chromium | system | Browser automation target |
|
|
| Playwright | latest | Browser testing framework |
|
|
| PostgreSQL | system default | Database |
|
|
| Ollama | latest | Local LLM inference |
|
|
| Claude Code | latest | AI coding assistant (Anthropic) |
|
|
| OpenCode | latest | Open-source AI coding assistant (multi-provider) |
|
|
| Tidewave | latest | Elixir/Phoenix MCP server for AI tools |
|
|
| yq | latest | YAML processor |
|
|
| watchexec | latest | File watcher (via cargo) |
|
|
| TigerVNC + XFCE | system | VNC access for browser login flows |
|
|
|
|
## Connecting from macOS
|
|
|
|
### SSH
|
|
|
|
```bash
|
|
# Shell into the VM (no key setup required)
|
|
ssh my-sandbox@orb
|
|
|
|
# Run a command directly
|
|
ssh my-sandbox@orb -- ls ~/projects
|
|
|
|
# Run Claude unrestricted
|
|
ssh my-sandbox@orb -- claude --dangerously-skip-permissions
|
|
```
|
|
|
|
OrbStack handles SSH key configuration automatically.
|
|
|
|
### Editing Files
|
|
|
|
Your editor connects to the VM over SSH. You edit files as if they were local — full LSP support, syntax highlighting, file tree, integrated terminal.
|
|
|
|
**Zed:**
|
|
1. `Cmd+Shift+P` -> "Open Remote Folder"
|
|
2. Enter: `my-sandbox@orb:~/projects`
|
|
|
|
**VS Code / Cursor:**
|
|
1. Install the "Remote - SSH" extension
|
|
2. `Cmd+Shift+P` -> "Remote-SSH: Connect to Host"
|
|
3. Enter: `my-sandbox@orb`
|
|
4. Open folder: `~/projects`
|
|
|
|
**Finder (direct filesystem access):**
|
|
```
|
|
/Volumes/OrbStack/my-sandbox/home/<user>/
|
|
```
|
|
|
|
### Viewing Running Apps
|
|
|
|
Services running in the VM are accessible from your Mac via `<vm-name>.orb.local`:
|
|
|
|
```bash
|
|
# Phoenix/Rails/Next.js dev server running on port 4000 inside the VM
|
|
open http://my-sandbox.orb.local:4000
|
|
|
|
# Or use port forwarding if the app only binds to localhost
|
|
ssh -L 4000:localhost:4000 my-sandbox@orb
|
|
```
|
|
|
|
PostgreSQL, Redis, or any service listening on the VM's interfaces is reachable at `my-sandbox.orb.local:<port>` from your Mac — no extra configuration.
|
|
|
|
## VNC (Browser Access)
|
|
|
|
For tasks requiring a visible browser (e.g., Claude Code OAuth login):
|
|
|
|
```bash
|
|
# Start VNC server inside the VM
|
|
ssh my-sandbox@orb -- vnc-start
|
|
|
|
# Connect from macOS (opens Screen Sharing.app)
|
|
open vnc://my-sandbox.orb.local:5901
|
|
|
|
# Stop when done (saves resources)
|
|
ssh my-sandbox@orb -- vnc-stop
|
|
```
|
|
|
|
### macOS Screen Sharing (built-in)
|
|
|
|
```bash
|
|
open vnc://my-sandbox.orb.local:5901
|
|
```
|
|
|
|
Enter your VNC password when prompted.
|
|
|
|
### RealVNC Viewer
|
|
|
|
1. Download from https://www.realvnc.com/en/connect/download/viewer/
|
|
2. Enter address: `my-sandbox.orb.local:5901`
|
|
3. When prompted for credentials, enter your VNC password (username can be left blank)
|
|
|
|
### TigerVNC Viewer
|
|
|
|
```bash
|
|
# Install via Homebrew
|
|
brew install tiger-vnc
|
|
|
|
# Connect
|
|
vncviewer my-sandbox.orb.local:5901
|
|
```
|
|
|
|
Enter your VNC password when prompted.
|
|
|
|
### Connection details for any VNC client
|
|
|
|
- **Host**: `my-sandbox.orb.local`
|
|
- **Port**: `5901` (display `:1`)
|
|
- **Password**: the VNC password from `config.env`
|
|
- **Resolution**: 1280x800 (configurable in `~/bin/vnc-start`)
|
|
|
|
## PostgreSQL
|
|
|
|
A superuser matching your Linux username and a `dev` database are created automatically.
|
|
|
|
**Auth model:**
|
|
- Local socket (`psql dev`): peer auth (OS username must match PG role)
|
|
- Localhost TCP (127.0.0.1): scram-sha-256 (password required)
|
|
- Host network (192.168.0.0/16, i.e., from macOS): scram-sha-256
|
|
|
|
### From inside the VM
|
|
|
|
```bash
|
|
psql dev # Connect to dev database
|
|
psql -l # List databases
|
|
createdb myapp_dev # Create a new database
|
|
```
|
|
|
|
### From macOS
|
|
|
|
```bash
|
|
# Direct (requires: brew install libpq)
|
|
psql -h my-sandbox.orb.local -U <user> -d dev
|
|
```
|
|
|
|
### Connection strings
|
|
|
|
```
|
|
# Elixir/Phoenix
|
|
postgres://<user>@my-sandbox.orb.local/myapp_dev
|
|
|
|
# Generic
|
|
host=my-sandbox.orb.local port=5432 dbname=dev user=<user>
|
|
```
|
|
|
|
### DataGrip
|
|
|
|
1. New Data Source -> PostgreSQL
|
|
2. **SSH/SSL** tab: Check "Use SSH tunnel", Host: `my-sandbox@orb`, Auth: Key pair
|
|
3. **General** tab: Host: `localhost`, Port: `5432`, User: your VM username, Database: `dev`, No password
|
|
4. Test Connection -> Apply
|
|
|
|
### DBeaver
|
|
|
|
1. New Database Connection -> PostgreSQL
|
|
2. **SSH** tab: Check "Use SSH Tunnel", Host: `my-sandbox.orb.local`, Port: `22`, Auth: Public Key
|
|
3. **Main** tab: Host: `localhost`, Port: `5432`, Database: `dev`, Username: your VM username, no password
|
|
4. Test Connection -> Finish
|
|
|
|
### Creating additional databases
|
|
|
|
```bash
|
|
# From inside the VM
|
|
createdb myapp_dev
|
|
createdb myapp_test
|
|
|
|
# From macOS
|
|
ssh my-sandbox@orb -- createdb myapp_dev
|
|
```
|
|
|
|
## Claude Code Plugins
|
|
|
|
The script installs these plugins at user scope:
|
|
|
|
**Anthropic marketplace** (`anthropics/claude-code`):
|
|
- code-review, code-simplifier, feature-dev, pr-review-toolkit, security-guidance, frontend-design
|
|
|
|
**Superpowers marketplace** (`obra/superpowers`):
|
|
- double-shot-latte, elements-of-style, superpowers, superpowers-chrome, superpowers-lab
|
|
|
|
**MCP Servers**:
|
|
- `playwright` - Browser automation and screenshots
|
|
- `superpowers-chrome` - Direct Chrome/Chromium control (headless)
|
|
|
|
## Configuration
|
|
|
|
All tools are configured to use the latest versions by default. Erlang and Elixir versions can be customized in `setup_env.sh`:
|
|
|
|
```bash
|
|
ERLANG_VERSION="latest" # or pin to specific version like "28.3.1"
|
|
ELIXIR_VERSION="latest" # or pin to specific version like "1.19.5-otp-28"
|
|
```
|
|
|
|
### Shared credentials
|
|
|
|
Git credentials (name, email) are stored in `config.env` (gitignored). VNC passwords are never stored and must be entered each time you create a VM with VNC enabled.
|
|
|
|
To reset git credentials:
|
|
|
|
```bash
|
|
rm config.env
|
|
./setup_env.sh my-sandbox # will prompt again
|
|
```
|
|
|
|
Or copy the example and edit:
|
|
|
|
```bash
|
|
cp config.env.example config.env
|
|
# Edit config.env with your values
|
|
```
|
|
|
|
## Managing VMs
|
|
|
|
```bash
|
|
# List all VMs
|
|
orb list
|
|
|
|
# Delete a VM (instant cleanup)
|
|
orb delete my-sandbox
|
|
|
|
# Stop a VM (preserves state, frees resources)
|
|
orb stop my-sandbox
|
|
|
|
# Start a stopped VM
|
|
orb start my-sandbox
|
|
|
|
# Nuclear option — delete and recreate
|
|
orb delete my-sandbox && ./setup_env.sh my-sandbox
|
|
```
|
|
|
|
## Idempotency
|
|
|
|
The VM provisioning script is safe to run multiple times. It checks for existing installations before re-installing and avoids appending duplicate configuration lines.
|
|
|
|
## Logs
|
|
|
|
Each provisioning run creates a log file at `/tmp/setup_env_<timestamp>.log` inside the VM with detailed output from package installations and any errors. Log files are created with mode 600 (owner-only access).
|
|
|
|
## Security
|
|
|
|
The script includes several security hardening measures:
|
|
|
|
- **Input validation**: VM names, VNC passwords, and git credentials are validated against strict patterns
|
|
- **No credential storage**: VNC passwords are prompted each time and never written to disk
|
|
- **Safe credential passing**: Values are base64-encoded when passed to the VM to prevent shell injection
|
|
- **Config file security**: `config.env` is created with mode 600 and parsed safely (not sourced)
|
|
- **Secure temp files**: Uses `mktemp` with restrictive permissions for all temporary files
|
|
- **Host filesystem isolation**: macOS home directory access is disabled inside the VM
|
|
- **PostgreSQL hardening**: Uses peer auth for local sockets, scram-sha-256 for network connections
|