# Deploying PIG PIG is one container plus a Postgres, behind any reverse proxy that terminates TLS. Nothing here is specific to a particular host. ## 1. DNS Point the apex and `www` at the machine. Both must resolve before the proxy can obtain a certificate. ``` A primeintellectgrowth.com -> A www.primeintellectgrowth.com -> ``` ## 2. Configuration ```bash cp .env.example .env # then edit ``` The values that must be set for a production start: | Variable | Why | |---|---| | `POSTGRES_PASSWORD` | Generate a fresh one; never reuse another service's | | `PIG_PUBLIC_URL` | The single origin the app is served from | | `SUPABASE_URL` / `SUPABASE_ANON_KEY` | Authentication. The app refuses to start in production without a Supabase URL, because it would otherwise serve the whole CRM unauthenticated | | `PIG_ADMIN_EMAILS` | Who may administer. **Every address here must already have an account** — an unregistered address listed as an admin is a standing offer of admin rights to whoever claims it first | Optional: `PRIME_API_KEY` (scope it to `Availability → Read` only), `PIGGY_ENABLED` + `ANTHROPIC_API_KEY`, and the Slack and Buzz credentials. ## 3. Start ```bash docker compose -p pig up -d --build docker compose -p pig exec app npx tsx packages/db/src/migrate.ts docker compose -p pig exec app npx tsx packages/db/src/seed/index.ts # optional ``` Use `-p pig`. A compose project that shares a name with a neighbouring stack will adopt its volumes, which is a memorable way to lose a database. ## 4. Reverse proxy See `Caddyfile.example`. Serve the app and API from the **same** origin. Two things that will otherwise cost you an hour: - **If other sites on the host use `bind
`, yours must too.** Caddy groups site blocks into servers by listen address. A block without `bind` lands in a *separate* server on `:443`, and the more specific listener wins for traffic arriving on that address — which is all public traffic after NAT. The symptom is a valid certificate, a 200 response, an empty body, and none of your headers. It looks like the app is broken; it is that the request never reached it. - **The CSP must carry the hash of the inline theme script** in `index.html`. That script sets light or dark before first paint so dark-mode users do not get a white flash. Editing it changes the hash and CSP will silently block it — the browser console prints the hash it expects. ## 5. Verify ```bash curl -s https://primeintellectgrowth.com/api/health # {"ok":true,"service":"pig","version":"0.1.0"} ``` ## Upgrading ```bash git pull docker compose -p pig up -d --build docker compose -p pig exec app npx tsx packages/db/src/migrate.ts ``` Migrations are additive and safe to re-run; Drizzle tracks what has been applied. Take a dump before a major upgrade anyway: ```bash docker compose -p pig exec db pg_dump -U pig pig | gzip > pig-$(date +%F).sql.gz ``` ## A note on the auth project PIG verifies JWTs but authorizes from its own `users` table. If the Supabase project is shared with another application, its users get **nothing** here until they are explicitly invited. That is deliberate, and it is why a valid token can still return `403 needs_profile`.