# Production overlay: baked images, no published database port, real TLS, restart policies.
#
# Every secret comes from .env.prod via the Makefile's --env-file. Nothing here defaults to
# a working value, so a missing secret fails loudly rather than running insecurely.

services:
  postgres:
    restart: unless-stopped
    # No ports section: the database is reachable only over the compose network.

  # env_file is what actually places variables inside the container (--env-file does not).
  # Without it the API would start on its built-in defaults — including the placeholder
  # session secret — which is precisely the silent failure the startup guard now rejects.
  api:
    restart: unless-stopped
    env_file:
      - .env.prod
    environment:
      ENVIRONMENT: prod
      DRY_RUN: "false"
      LOG_LEVEL: INFO
    command:
      ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]

  worker:
    restart: unless-stopped
    env_file:
      - .env.prod
    environment:
      ENVIRONMENT: prod
      DRY_RUN: "false"
      LOG_LEVEL: INFO
    # Exactly one worker: the in-process send throttle assumes a single sender (design D10).
    deploy:
      replicas: 1

  web:
    restart: unless-stopped
    # No env_file: the frontend never holds secrets, only what it renders with.
    #
    # These names are deliberately un-prefixed. With NEXT_PUBLIC_, Next replaces the read
    # with a literal during `npm run build` — which happens inside `docker build`, where
    # none of these exist — so the values below would be ignored entirely and the widget
    # would render with no site key against a server that fails closed. `lib/public-config.ts`
    # reads them at request time; `eslint.config.mjs` bans the prefixed form.
    environment:
      NODE_ENV: production
      NEXT_TELEMETRY_DISABLED: "1"
      INTERNAL_API_URL: http://api:8000
      TURNSTILE_SITE_KEY: ${TURNSTILE_SITE_KEY:?TURNSTILE_SITE_KEY must be set}
      # The API refuses to boot on a localhost or plain-HTTP value, so failing the deploy
      # here keeps the two halves from disagreeing about where this deployment lives.
      APP_BASE_URL: ${APP_BASE_URL:?APP_BASE_URL must be set}
      # Public client id only. Fail the deploy rather than shipping an admin sign-in page
      # with no working button on it.
      GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:?GOOGLE_CLIENT_ID must be set}

  caddy:
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      DOMAIN: ${DOMAIN:?DOMAIN must be set in .env.prod}
    volumes:
      - ./caddy/Caddyfile.prod:/etc/caddy/Caddyfile:ro
