# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Critical Rules - **NEVER build, test, or run anything locally.** Always use Docker for all build/test/run operations. - **`websitebox-brief (2).md` is the source of truth** for all project decisions and specifications. Always defer to it. - **Do not load `websitebox-diagram (1).jsx`** unless the user explicitly asks for it — it is reference material only. - **Keep `guide.md` in sync.** Any change that alters the user-facing flow — new commands, renamed scripts, changed setup wizard prompts, added/removed steps, new dependencies, modified ports, updated troubleshooting — **must** also be reflected in `guide.md`. This is a beginner-facing deployment guide in ProjectPublic markup format. If you're unsure whether a change affects the guide, check. Treat guide.md as a living document that must always match the actual codebase. ## Project Overview WebsiteBox is a Docker-based, self-hosted WordPress deployment system. Users provision a VPS, run an install script, and get a working SSL-secured WordPress portfolio site with `docker compose up -d`. The project prioritizes zero-trust transparency, minimal CLI interaction, and VPS-agnostic deployment. ## Architecture Four-container Docker Compose stack on a single Docker network (`websitebox_internal`): - **nginx** (custom `nginx:alpine`) — Reverse proxy, SSL termination via Let's Encrypt. Only container exposing ports (80, 443). Entrypoint auto-acquires SSL certs on first boot. - **wordpress** (custom `wordpress:php8.2-fpm-alpine`) — PHP-FPM with WP-CLI bundled. First-boot entrypoint installs WordPress core, GeneratePress theme, child theme, and plugins (Age Gate, Wordfence, UpdraftPlus) via WP-CLI. - **db** (`mariadb:11`) — Internal only, never exposed to host. - **certbot** (`certbot/certbot`) — Renewal loop every 12h, signals nginx via shared volume file trigger (no Docker socket mount). All persistent data uses **bind mounts** under `./websitebox-data/` (not named Docker volumes): - `wordpress/`, `database/`, `certs/`, `certbot-webroot/`, `certbot-signal/`, `backups/` ## Key Design Decisions - **Restart policy**: `restart: unless-stopped` on all containers (not `always`). - **SSL bootstrap**: Handled entirely in nginx entrypoint — no separate ssl-init.sh script. Nginx detects missing certs, serves HTTP for ACME challenge, acquires certs, reloads with SSL. - **Certbot-to-nginx reload**: Certbot writes trigger file to shared `certbot-signal/` volume; nginx background loop detects and reloads. No Docker socket mounting. - **WordPress setup**: All via WP-CLI in container entrypoint, not the mu-plugin. Uses marker files (`.websitebox-setup-complete` / `.websitebox-setup-partial`) for idempotency. - **Backup path**: UpdraftPlus writes to `/var/backups/websitebox` (mapped to `./websitebox-data/backups/`), deliberately outside `/var/www/html` to avoid nested bind mount conflicts. - **XML-RPC disable**: In mu-plugin via `add_filter('xmlrpc_enabled', '__return_false')`, NOT in wp-config.php (plugin API not loaded at wp-config time). - **WP-CLI**: Bundled in WordPress Docker image. Run commands as `www-data`, never use `--allow-root`. - **Table prefix**: `wbox_` (not default `wp_`). - **wp-config**: Sets `DISALLOW_FILE_EDIT` (blocks theme/plugin editor) but NOT `DISALLOW_FILE_MODS` (would prevent plugin/theme updates via admin GUI). ## User Flow 1. `curl -fsSL /install.sh | bash` — installs Docker, clones repo, runs setup wizard 2. `setup.sh` — interactive wizard, generates `.env`, creates `websitebox-data/` structure 3. User configures DNS A record 4. `docker compose up -d` — everything self-configures ## File Structure Conventions - `nginx/entrypoint.sh` — SSL auto-bootstrap logic - `wordpress/entrypoint.sh` — First-boot WP-CLI setup (theme/plugin install, site config) - `wordpress/wp-content/mu-plugins/websitebox-setup.php` — Lightweight status checker + XML-RPC disable (not the installer) - `wordpress/wp-content/themes/websitebox/` — Child theme (parent: GeneratePress) - `scripts/` — `backup.sh`, `update.sh`, `ssl-renew.sh`, `healthcheck.sh` - `.env` / `.env.example` — Configuration (gitignored / template) ## Commands ```bash # Start the stack docker compose up -d # Rebuild after Dockerfile changes docker compose up -d --build # View logs docker compose logs -f [service] # Restart a specific service docker compose restart wordpress # Re-run first-boot setup (force clean) docker compose exec wordpress rm /var/www/html/.websitebox-setup-complete /var/www/html/.websitebox-setup-partial docker compose restart wordpress # Update WebsiteBox ./scripts/update.sh # Manual backup ./scripts/backup.sh ``` ## Healthchecks | Service | Check | Retries | |---------|-------|---------| | nginx | `curl -f http://localhost/nginx-health` | 3 | | wordpress | `php-fpm-healthcheck` | 3 | | db | `healthcheck --su-mysql --connect --innodb_initialized` | 5 | WordPress depends on db with `condition: service_healthy`. ## Error Handling Patterns - WordPress entrypoint: MariaDB retry loop (30 attempts, 2s apart). On partial failure, creates `.websitebox-setup-partial` marker and starts PHP-FPM anyway. All WP-CLI commands are idempotent — safe to re-run entire sequence. - nginx entrypoint: On SSL failure, serves HTTP placeholder explaining DNS isn't ready. User retries with `docker compose restart nginx`. - install.sh: Uses `sg docker` for immediate Docker group activation without logout/login. ## Marketing Site & License Keys (separate repo) The marketing site (makeyourown.website) and license key validation server live in a **separate repository**: `https://git.constantprojects.xyz/tankadmin/websites.git` (local: `~/claude/projects/websites`). This is business infrastructure that runs on its own VPS, independent of any WebsiteBox test/product deployments. ### License key validation (client-side) - `install.sh` prompts for a license key as the very first step (before any system changes) - `setup.sh` has a secondary check (format-only if `.license-key` file exists, server validation if missing) - **Endpoint:** `POST https://makeyourown.website/api/validate` with body `key=WBOX-XXXX-XXXX-XXXX-XXXX` - **Key format:** `WBOX-XXXX-XXXX-XXXX-XXXX` (uppercase alphanumeric, no ambiguous chars 0/O/1/I/L) - **Behavior:** Burn-on-use — keys are consumed on first activation - **Storage:** `.license-key` file in project root (gitignored, chmod 600) ### What lives where | Concern | Location | |---------|----------| | Key check in install flow | This repo: `install.sh`, `setup.sh` | | Keyserver, key generation, marketing site | `websites` repo: `services/keyserver/`, `scripts/generate-keys.sh`, `sites/makeyourown.website/` | | Key storage (keys.txt) | `websites` VPS: `services/keyserver/keys.txt` | ### Business rules - WebsiteBox is **not free** and **not "open source"** — code is auditable, buyers can read every line before purchasing - Do not say "free" or "open source" in marketing content - Pricing tiers: Standard $49 one-time, Lifetime Updates $149 one-time, Managed $25/month (all placeholder amounts) ## Non-Goals (v1) Payment processing, multi-site WordPress, automatic VPS provisioning, built-in CDN, email server (SMTP relay config only).