# Testing Preview

> Test the preview proxy feature end-to-end. Use when verifying preview URL changes, auth changes on the web proxy route, or networking-related fixes.

- Skill: `michaelshimeles/testing-preview` (Agent Skill)
- Install (CLI): `npx skillmds@latest add michaelshimeles/testing-preview`
- Raw SKILL.md: https://api.skillmd.com/api/skills/michaelshimeles/testing-preview/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: michaelshimeles (https://skillmd.com/u/michaelshimeles)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/michaelshimeles/testing-preview

---


# Testing the Preview Feature

## What It Does

The preview proxy exposes a port running inside a guest Firecracker VM at a public URL. Two implementations exist:

1. **Path-based** (`/v1/machines/{id}/web/{port}/{path...}`) — works over SSH tunnel and without wildcard DNS
2. **Subdomain-based** (`<id>--<port>.<PreviewBase>`) — requires Caddy on-demand TLS + wildcard DNS

## Prerequisites

- nehemiahd must be running with `NEHEMIAH_NET=1` (enables guest networking via tap/bridge/DHCP)
- A test VM must be created with `net: true` (ensures DHCP lease is assigned)
- An HTTP server must be running inside the guest on a known port

## How to Set Up a Test VM

```bash
# Build and run nehemiahd (with auth to test the auth bypass)
cd nehemiahd && go build -o /tmp/nehemiahd .
sudo NEHEMIAH_NET=1 NEHEMIAH_JAILER=0 NEHEMIAH_TOKEN=test-token /tmp/nehemiahd &

# Create a VM with networking
curl -s http://localhost:8080/v1/machines -X POST \
  -H "Authorization: Bearer test-token" \
  -d '{"template":"python","ttl_seconds":900,"net":true}'

# Start an HTTP server inside the guest (via WebSocket TTY)
python3 -c "
import websocket, time
ws = websocket.create_connection('ws://localhost:8080/v1/machines/MACHINE_ID/tty',
    header=['Authorization: Bearer test-token'])
time.sleep(0.5)
ws.send(b'cd / && python3 -m http.server 8000 --bind 0.0.0.0 &\n')
time.sleep(2)
ws.close()
"
```

## Key Test Cases

The server above is started from `/` (`cd /`), so `http.server` serves the guest's
root filesystem — that makes the sub-path test below resolve.

1. **Preview without auth**: `curl http://localhost:8080/v1/machines/{id}/web/8000/` should return content (the `/` directory listing; no auth header needed)
2. **Other routes still require auth**: `curl http://localhost:8080/v1/machines/{id}` should return 401
3. **Sub-path routing**: `curl http://localhost:8080/v1/machines/{id}/web/8000/etc/` should show the guest's `/etc` directory listing (proves sub-paths are proxied through)
4. **Via Vite proxy**: `curl http://localhost:5173/boring/v1/machines/{id}/web/8000/` should work

## Architecture Notes

- The web proxy route is intentionally unauthenticated — preview URLs are opened via `window.open` in new browser tabs which can't add Authorization headers
- The machine ID acts as the access token (unguessable)
- `machineIP()` resolves guest IP: first checks `driver.ip` (for forks), then falls back to DHCP lease file (`/var/lib/misc/dnsmasq.leases`)
- Guest MAC is derived from machine ID via SHA1: `guestMAC(id) → 06:00:XX:XX:XX:XX`

## Common Failure Modes

- **"this computer isn't on the network"**: NEHEMIAH_NET not set, or machine created without net=true (for snapshot-eligible templates)
- **"nothing is listening on port X"**: Server not started in guest, or bound to 127.0.0.1 instead of 0.0.0.0
- **401 on preview URL**: The route might have been accidentally wrapped in `s.auth()` again
- **Machine TTL expired**: Default TTL is short; use 900s for testing

## Devin Secrets Needed

- None required for local testing (NEHEMIAH_TOKEN is set at runtime for test isolation)

