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>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# PIG — self-hosted deployment.
|
||||
#
|
||||
# docker compose -p pig up -d --build
|
||||
#
|
||||
# The project name matters. Use something PIG-specific (`-p pig`) so this stack
|
||||
# never adopts another application's volumes — a compose project silently
|
||||
# inheriting a neighbouring database is a genuinely nasty way to lose data.
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-pig}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-pig}
|
||||
volumes:
|
||||
- pig-pgdata:/var/lib/postgresql/data
|
||||
# Not published to the host. The application reaches it over the compose
|
||||
# network; exposing Postgres publicly is never what you want.
|
||||
expose:
|
||||
- '5432'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-pig} -d ${POSTGRES_DB:-pig}']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
app:
|
||||
build: .
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgres://${POSTGRES_USER:-pig}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-pig}
|
||||
NODE_ENV: production
|
||||
PIG_PORT: 8920
|
||||
PIG_PUBLIC_URL: ${PIG_PUBLIC_URL:?PIG_PUBLIC_URL must be set}
|
||||
SUPABASE_URL: ${SUPABASE_URL:?SUPABASE_URL must be set in production}
|
||||
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
|
||||
SUPABASE_SERVICE_KEY: ${SUPABASE_SERVICE_KEY:-}
|
||||
PIG_ADMIN_EMAILS: ${PIG_ADMIN_EMAILS:-}
|
||||
PIG_INVITE_CODE: ${PIG_INVITE_CODE:-}
|
||||
PRIME_API_KEY: ${PRIME_API_KEY:-}
|
||||
PRIME_SYNC_ENABLED: ${PRIME_SYNC_ENABLED:-false}
|
||||
PIGGY_ENABLED: ${PIGGY_ENABLED:-false}
|
||||
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
|
||||
SLACK_BOT_TOKEN: ${SLACK_BOT_TOKEN:-}
|
||||
SLACK_SIGNING_SECRET: ${SLACK_SIGNING_SECRET:-}
|
||||
BUZZ_RELAY_URL: ${BUZZ_RELAY_URL:-}
|
||||
# Bound to loopback: TLS termination belongs to the reverse proxy in front,
|
||||
# not to this container.
|
||||
ports:
|
||||
- '127.0.0.1:${PIG_HOST_PORT:-8920}:8920'
|
||||
|
||||
volumes:
|
||||
pig-pgdata:
|
||||
# Named explicitly so it is obvious which volume holds the data, and so a
|
||||
# `docker compose down -v` mistake is at least a legible one.
|
||||
name: pig-pgdata
|
||||
Reference in New Issue
Block a user