# Bootstrapping ecomm Bringing an environment from **empty persistence** to "first merchant, first storefront" through the product flows alone (SD-0001 BUC-5). Empty is a working state (INV-1): the app applies its own schema migrations at startup; there is no seed step. This document grows per environment. SLICE-1 shipped the **localhost** section; SLICE-4 adds **pre-production (PPE)**. The production section lands with the prod stand-up. ## Localhost ### Prerequisites - **Docker** (Desktop or Engine) with Compose v2 — the one external prerequisite. The dev datastore is a single PostgreSQL container; the app itself runs natively for a fast dev loop (D-7). - **Python 3.13** and **Node 20+** on your PATH. `scripts/dev.sh` creates the backend venv and installs frontend deps on first run. ### Bring-up From a clean checkout: ```bash scripts/dev.sh ``` This: 1. starts the dev Postgres container and waits for it to report healthy (the script owns the container's lifecycle); 2. creates the backend virtualenv and installs dependencies (first run only); 3. installs the frontend's npm dependencies (first run only); 4. runs the FastAPI backend on **:8000** — which connects to the empty database and applies all pending migrations itself (INV-7) — and the Vite dev server on **:5173**, proxying `/api` to the backend. Press **Ctrl-C** to stop the backend and Vite. The database container keeps running. ### Verify ```bash curl http://localhost:8000/healthz ``` Expected: `{"status":"ok"}` (HTTP 200) — the process is up, the database is reachable, and migrations are current. Open to see the app shell. ### Reset to empty (rehearse the bootstrap) ```bash docker compose down -v ``` This removes the container and its named volume, returning persistence to empty. The next `scripts/dev.sh` re-migrates from scratch — exactly the day-one state every environment starts from (BUC-5a). ### Configuration `scripts/dev.sh` sets `ECOMM_DATABASE_URL` to the local compose DSN (`postgresql://ecomm:ecomm@localhost:5432/ecomm`). No deployment shape is baked into the app (INV-8); deployed environments supply this and other config from Secret Manager (see PPE below). ## Pre-production (PPE) PPE is stood up and deployed through the **launch-app / flotilla-core** suite only (handbook §8.5 — provisioning is an operator-run gesture). The app's deployment record is `deployment.toml` at this repo's root; flotilla-core consumes it. ### One-time provisioning (operator-run, in order) 1. **Cloud foundation** — `scaffold-gcp-project`: the GCP project, its dedicated `--no-activate` gcloud config, billing, core APIs, ADC quota pin. 2. **Managed database** — `provision-datastore` (launch-app §6.6a, built for ecomm's D-7/D-8): Cloud SQL for PostgreSQL 16, private-IP-only, automated backups + point-in-time recovery; writes the full DSN to Secret Manager as `/ecomm-ppe-database-url`. Bound on the deployment record as `ECOMM_DATABASE_URL`. 3. **Secrets** (references only — bytes go in via stdin, never through a session): `ECOMM_SESSION_SECRET` (fresh random), `ECOMM_SMTP_PASSWORD` (the shared Wiggleverse relay credential), and flotilla's own Gitea read token for this private repo. 4. **VM + edge** — `provision-vm`: the e2-micro, IAP-only SSH, nginx + Cloudflare origin TLS, the systemd unit, DNS for `ecomm-ppe.wiggleverse.org`. 5. **Deployment record** — `flotilla-core deployment scaffold ecomm … -o deployment.toml`, validate, commit it here, `deployment import --reconcile`. ### Deploy Each release is a git tag `v` matching the repo-root `VERSION` file (the pin). The one gesture, from the flotilla-core repo's venv: ```bash CLOUDSDK_ACTIVE_CONFIG_NAME=ecomm .venv/bin/flotilla-core deploy ecomm ``` Nine phases, fail-stop; it ends by polling `https://ecomm-ppe.wiggleverse.org/healthz` and requiring `status == "ok"` and `version == `. Watch it yourself the same way: ```bash curl https://ecomm-ppe.wiggleverse.org/healthz ``` ### Configuration surface (PPE values) | Env var | Kind | Value | | --- | --- | --- | | `ECOMM_DATABASE_URL` | secret ref | `/ecomm-ppe-database-url` (from provision-datastore) | | `ECOMM_SESSION_SECRET` | secret ref | `/ecomm-ppe-session-secret` | | `ECOMM_SMTP_PASSWORD` | secret ref | the shared Wiggleverse relay credential | | `ECOMM_MAILER` | overlay | `smtp` | | `ECOMM_COOKIE_SECURE` | overlay | `1` | | `ECOMM_SMTP_HOST` / `_PORT` / `_USER` / `_FROM` | overlay | the relay coordinates (non-secret) | ### The rehearsal (PUC-11) From empty persistence: the deploy migrates the schema at startup (INV-7); then a real sign-up → one-time code arriving by **real email** → create storefront → admin, through the public flows alone. Record each rehearsal here when it happens. **Rehearsals:** - **2026-06-11 — PPE first bootstrap (v0.4.0, session 0024): ✅** First-ever ecomm deploy (`flotilla-core deploy ecomm`, 9/9 phases green, deploys.id=40) onto a freshly provisioned environment — project `wiggleverse-ecomm`, Cloud SQL `ecomm-ppe-pg` (provisioned by launch-app `provision-datastore`, first run), VM `ecomm-ppe`. The app self-migrated the empty database at startup; the operator then walked sign-up → real emailed code (Gmail relay) → create storefront → honestly-empty admin in a browser, through the public flows alone. Findings captured: OTC email branding (#16); identity should outgrow ecomm (engineering#49/#50). ## Production Lands with the prod stand-up — the **identical gesture** on a prod deployment record (BUC-5a): same provisioning skills, same deploy, same rehearsal.