Files
pig/deploy/README.md
T
karti c747eb2aa7 Add deployment: Dockerfile, compose, proxy config, and docs
One container plus a Postgres behind any TLS-terminating proxy. Nothing is
specific to a particular host.

The app and API are served from a SINGLE origin. This is not tidiness: browser
auth sessions live in per-origin storage, so splitting them across two
hostnames makes sign-in loop in a way that presents as a server fault. The
short alias redirects rather than serving a second origin.

Two safety properties verified by running the image, not by reading the code:

- With NODE_ENV=production and no SUPABASE_URL, the process refuses to start
  and says why. Serving the whole CRM unauthenticated is a worse outcome than
  failing to deploy, so the failure is deliberate and loud.
- In production the development auth bypass does not apply: an unauthenticated
  request to /api/dashboard returns 401 rather than adopting the first user in
  the table.

The Dockerfile typechecks all six packages as a build gate, so a deploy that
does not compile fails at build time rather than in front of a user. Runtime
runs unprivileged as `node`, and Postgres is not published to the host.

Docs cover the ontology and why it is shaped this way, agent connection for
Claude Code / Codex / prime-agent / Buzz, and the provenance rules governing
seed data about real people — including how to have your record removed.

Verified: image builds, container reports healthy, serves the SPA, enforces
auth, and the production guard exits non-zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 19:19:53 -07:00

2.4 KiB

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       -> <your IP>
A   www.primeintellectgrowth.com   -> <your IP>

2. Configuration

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

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.

5. Verify

curl -s https://primeintellectgrowth.com/api/health
# {"ok":true,"service":"pig","version":"0.1.0"}

Upgrading

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:

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.