# briven data-plane stack — Phase 6 of BACKEND_FORK_BRIEF.md.
#
# Per-project Postgres + REST + Auth + Realtime + Schema-introspection,
# fronted by Caddy. Studio (apps/studio) is deployed separately as a
# Next.js app at studio.briven.tech and talks to this stack via the
# Caddy proxy.
#
# Services dropped from upstream supabase docker-compose:
#   - studio          (lives in apps/studio, deployed as separate Next app)
#   - kong            (replaced with caddy — see plan §1)
#   - storage / imgproxy  (deferred to Phase 9 per brief §5)
#   - functions       (deferred — briven has separate Deno isolate plan)
#   - analytics       (logflare) — briven uses Loki+Grafana instead
#   - vector          (hard-rule violation of docs/DOCKER.md rule 6 —
#                      bind-mounts /var/run/docker.sock; never re-add)
#   - supavisor       (deferred to Phase 9 — pool sizing only matters at scale)
#
# Hard rules enforced (docs/DOCKER.md):
#   - every service uses logging: *briven-logging (cap 10MB x 3 files)
#   - no service mounts /var/run/docker.sock
#   - no auto-poll of the Docker API
#   - restart: unless-stopped, not always

x-logging: &briven-logging
  driver: json-file
  options:
    max-size: '10m'
    max-file: '3'

services:
  # ── Reverse proxy ────────────────────────────────────────────────────
  # Strips identifying upstream headers (Server, X-Powered-By) before
  # any response leaves the trust zone. Adds Server: briven. Auto-TLS
  # via Let's Encrypt if BRIVEN_PUBLIC_HOST is set to a routable domain.
  caddy:
    image: caddy:2.10-alpine
    container_name: briven-caddy
    restart: unless-stopped
    logging: *briven-logging
    networks:
      - briven-network
    ports:
      - '${BRIVEN_PROXY_HTTP_PORT:-8000}:80'
      - '${BRIVEN_PROXY_HTTPS_PORT:-8443}:443'
    environment:
      BRIVEN_PUBLIC_HOST: ${BRIVEN_PUBLIC_HOST:-api.briven.tech}
    volumes:
      - ./volumes/proxy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro,z
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      auth:
        condition: service_healthy
      rest:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost/']
      interval: 10s
      timeout: 5s
      retries: 5

  # ── Postgres ─────────────────────────────────────────────────────────
  # The data plane. Schemas per project are created on demand by the
  # control-plane provisioning flow (Phase 7).
  db:
    image: supabase/postgres:15.8.1.085
    container_name: briven-db
    restart: unless-stopped
    logging: *briven-logging
    networks:
      - briven-network
    ports:
      - '${BRIVEN_DB_HOST_PORT:-5432}:5432'
    environment:
      POSTGRES_HOST: /var/run/postgresql
      POSTGRES_PORT: 5432
      POSTGRES_DB: ${BRIVEN_POSTGRES_DB:-postgres}
      POSTGRES_PASSWORD: ${BRIVEN_POSTGRES_PASSWORD:?BRIVEN_POSTGRES_PASSWORD is required}
      PGPORT: 5432
      PGPASSWORD: ${BRIVEN_POSTGRES_PASSWORD:?BRIVEN_POSTGRES_PASSWORD is required}
      PGDATABASE: ${BRIVEN_POSTGRES_DB:-postgres}
      JWT_SECRET: ${BRIVEN_JWT_SECRET:?BRIVEN_JWT_SECRET is required}
      JWT_EXP: ${BRIVEN_JWT_EXP:-3600}
    volumes:
      - briven-db-data:/var/lib/postgresql/data
      - briven-db-config:/etc/postgresql-custom
      - ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z
      - ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z
      - ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z
      - ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
      - ./volumes/db/_briven.sql:/docker-entrypoint-initdb.d/migrations/97-_briven.sql:Z
      - ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z
    healthcheck:
      test: ['CMD', 'pg_isready', '-U', 'postgres', '-h', 'localhost']
      interval: 5s
      timeout: 5s
      retries: 10

  # ── Auth (gotrue) ────────────────────────────────────────────────────
  # GOTRUE_* env names are baked into the upstream Go binary; we wrap
  # them with BRIVEN_AUTH_* indirection at the compose layer. Caddy
  # strips response headers (Server, X-Powered-By, Via) — see
  # volumes/proxy/caddy/Caddyfile. Response BODY scrubbing of upstream
  # identifiers (gotrue error messages, postgrest hints) is not yet
  # implemented — vanilla caddy:2-alpine lacks the replace-response
  # module. Tracked in HANDOFF.md §"Carry-over gaps".
  auth:
    image: supabase/gotrue:v2.186.0
    container_name: briven-auth
    restart: unless-stopped
    logging: *briven-logging
    networks:
      - briven-network
    depends_on:
      db:
        condition: service_healthy
    environment:
      GOTRUE_API_HOST: 0.0.0.0
      GOTRUE_API_PORT: 9999
      API_EXTERNAL_URL: ${BRIVEN_API_EXTERNAL_URL:?BRIVEN_API_EXTERNAL_URL is required}
      GOTRUE_DB_DRIVER: postgres
      GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${BRIVEN_POSTGRES_PASSWORD}@db:5432/${BRIVEN_POSTGRES_DB:-postgres}
      GOTRUE_SITE_URL: ${BRIVEN_AUTH_SITE_URL:?BRIVEN_AUTH_SITE_URL is required}
      GOTRUE_URI_ALLOW_LIST: ${BRIVEN_AUTH_URI_ALLOW_LIST:-}
      GOTRUE_DISABLE_SIGNUP: ${BRIVEN_AUTH_DISABLE_SIGNUP:-false}
      GOTRUE_JWT_ADMIN_ROLES: service_role
      GOTRUE_JWT_AUD: authenticated
      GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
      GOTRUE_JWT_EXP: ${BRIVEN_JWT_EXP:-3600}
      GOTRUE_JWT_SECRET: ${BRIVEN_JWT_SECRET}
      GOTRUE_EXTERNAL_EMAIL_ENABLED: ${BRIVEN_AUTH_EMAIL_ENABLED:-true}
      GOTRUE_MAILER_AUTOCONFIRM: ${BRIVEN_AUTH_MAILER_AUTOCONFIRM:-false}
      GOTRUE_SMTP_HOST: ${BRIVEN_AUTH_SMTP_HOST:-}
      GOTRUE_SMTP_PORT: ${BRIVEN_AUTH_SMTP_PORT:-587}
      GOTRUE_SMTP_USER: ${BRIVEN_AUTH_SMTP_USER:-}
      GOTRUE_SMTP_PASS: ${BRIVEN_AUTH_SMTP_PASS:-}
      GOTRUE_SMTP_ADMIN_EMAIL: ${BRIVEN_AUTH_SMTP_ADMIN_EMAIL:-}
      GOTRUE_SMTP_SENDER_NAME: ${BRIVEN_AUTH_SMTP_SENDER_NAME:-briven}
    healthcheck:
      test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:9999/health']
      interval: 5s
      timeout: 5s
      retries: 10

  # ── PostgREST ────────────────────────────────────────────────────────
  # Auto-generates a REST API from the DB schema. PGRST_* names are
  # native; wrapped via BRIVEN_REST_* at the compose layer.
  rest:
    image: postgrest/postgrest:v14.8
    container_name: briven-rest
    restart: unless-stopped
    logging: *briven-logging
    networks:
      - briven-network
    depends_on:
      db:
        condition: service_healthy
    environment:
      PGRST_DB_URI: postgres://authenticator:${BRIVEN_POSTGRES_PASSWORD}@db:5432/${BRIVEN_POSTGRES_DB:-postgres}
      PGRST_DB_SCHEMAS: ${BRIVEN_REST_DB_SCHEMAS:-public,storage,graphql_public}
      PGRST_DB_ANON_ROLE: anon
      PGRST_JWT_SECRET: ${BRIVEN_JWT_SECRET}
      PGRST_DB_USE_LEGACY_GUCS: 'false'
      PGRST_APP_SETTINGS_JWT_SECRET: ${BRIVEN_JWT_SECRET}
      PGRST_APP_SETTINGS_JWT_EXP: ${BRIVEN_JWT_EXP:-3600}
    command:
      - 'postgrest'
    healthcheck:
      test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3000/']
      interval: 5s
      timeout: 5s
      retries: 10

  # ── Realtime (Phoenix) ───────────────────────────────────────────────
  # WebSocket subscriptions on Postgres logical replication. Native env
  # names (API_JWT_SECRET, DB_HOST, etc.) wrapped via BRIVEN_REALTIME_*.
  realtime:
    image: supabase/realtime:v2.76.5
    container_name: briven-realtime
    restart: unless-stopped
    logging: *briven-logging
    networks:
      - briven-network
    depends_on:
      db:
        condition: service_healthy
    environment:
      PORT: 4000
      DB_HOST: db
      DB_PORT: 5432
      DB_USER: supabase_admin
      DB_PASSWORD: ${BRIVEN_POSTGRES_PASSWORD}
      DB_NAME: ${BRIVEN_POSTGRES_DB:-postgres}
      DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
      DB_ENC_KEY: ${BRIVEN_REALTIME_ENC_KEY:?BRIVEN_REALTIME_ENC_KEY is required}
      API_JWT_SECRET: ${BRIVEN_JWT_SECRET}
      SECRET_KEY_BASE: ${BRIVEN_REALTIME_SECRET_KEY_BASE:?BRIVEN_REALTIME_SECRET_KEY_BASE is required}
      ERL_AFLAGS: -proto_dist inet_tcp
      DNS_NODES: "''"
      RLIMIT_NOFILE: '10000'
      APP_NAME: realtime
      SEED_SELF_HOST: 'true'
      RUN_JANITOR: 'true'
    healthcheck:
      test:
        - CMD-SHELL
        - 'curl -sSfL --head -o /dev/null -H "Authorization: Bearer ${BRIVEN_JWT_SECRET}" http://localhost:4000/api/tenants/realtime-dev/health'
      interval: 5s
      timeout: 5s
      retries: 3

  # ── postgres-meta ────────────────────────────────────────────────────
  # Schema introspection backend that Studio reads from. PG_META_* native;
  # wrapped via BRIVEN_META_*.
  meta:
    image: supabase/postgres-meta:v0.96.3
    container_name: briven-meta
    restart: unless-stopped
    logging: *briven-logging
    networks:
      - briven-network
    depends_on:
      db:
        condition: service_healthy
    environment:
      PG_META_PORT: 8080
      PG_META_DB_HOST: db
      PG_META_DB_PORT: 5432
      PG_META_DB_NAME: ${BRIVEN_POSTGRES_DB:-postgres}
      PG_META_DB_USER: supabase_admin
      PG_META_DB_PASSWORD: ${BRIVEN_POSTGRES_PASSWORD}
    healthcheck:
      test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:8080/health']
      interval: 10s
      timeout: 5s
      retries: 5

  # ── pgBackRest ───────────────────────────────────────────────────────
  # Phase 9 of BACKEND_FORK_BRIEF.md. Continuous WAL archive + incremental
  # backups to Cloudflare R2 every 15 minutes; full backup nightly. Reads
  # PGDATA via a shared bind-mount with the db service (NOT via the
  # daemon API). Restore procedure documented in infra/datapane/RESTORE.md.
  pgbackrest:
    image: pgbackrest/pgbackrest:2.55.1
    container_name: briven-pgbackrest
    restart: unless-stopped
    logging: *briven-logging
    networks:
      - briven-network
    depends_on:
      db:
        condition: service_healthy
    environment:
      PGBACKREST_REPO1_TYPE: s3
      PGBACKREST_REPO1_S3_BUCKET: ${BRIVEN_BACKUP_R2_BUCKET:?BRIVEN_BACKUP_R2_BUCKET is required}
      PGBACKREST_REPO1_S3_ENDPOINT: ${BRIVEN_BACKUP_R2_ENDPOINT:?BRIVEN_BACKUP_R2_ENDPOINT is required}
      PGBACKREST_REPO1_S3_REGION: auto
      PGBACKREST_REPO1_S3_KEY: ${BRIVEN_BACKUP_R2_ACCESS_KEY:?BRIVEN_BACKUP_R2_ACCESS_KEY is required}
      PGBACKREST_REPO1_S3_KEY_SECRET: ${BRIVEN_BACKUP_R2_SECRET_KEY:?BRIVEN_BACKUP_R2_SECRET_KEY is required}
      PGBACKREST_REPO1_RETENTION_FULL: '4'
      PGBACKREST_REPO1_RETENTION_DIFF: '7'
      PGBACKREST_REPO1_CIPHER_TYPE: aes-256-cbc
      PGBACKREST_REPO1_CIPHER_PASS: ${BRIVEN_BACKUP_ENCRYPTION_KEY:?BRIVEN_BACKUP_ENCRYPTION_KEY is required}
      PGBACKREST_PROCESS_MAX: '2'
      PGBACKREST_COMPRESS_TYPE: zst
      PGBACKREST_LOG_LEVEL_FILE: info
      PGBACKREST_LOG_LEVEL_CONSOLE: warn
    volumes:
      - briven-db-data:/var/lib/postgresql/data:ro
      - briven-backup-cache:/var/lib/pgbackrest
      - ./pgbackrest/pgbackrest.conf:/etc/pgbackrest/pgbackrest.conf:ro,z
      - ./pgbackrest/crontab:/etc/crontabs/root:ro,z
      - ./pgbackrest/entrypoint.sh:/entrypoint.sh:ro,z
    entrypoint: ['/bin/sh']
    command: ['/entrypoint.sh']
    healthcheck:
      test: ['CMD', 'pgbackrest', '--stanza=briven', 'check']
      interval: 1m
      timeout: 30s
      retries: 3

networks:
  briven-network:
    name: briven-network

volumes:
  briven-db-data:
    name: briven-db-data
  briven-db-config:
    name: briven-db-config
  briven-backup-cache:
    name: briven-backup-cache
  caddy_data:
    name: briven-caddy-data
  caddy_config:
    name: briven-caddy-config
