# ⚠ BEST-EFFORT / NOT CERTIFIED (ADR-012). Multi-replica/HA Postgres variant of the prod
# stack. Unlike docker-compose.prod.yml (supported SQLite), this BUILDS a local image with
# the [postgres] extra (Dockerfile.postgres) because the published image is base-only.
#
#   docker compose -f docker-compose.prod-postgres.yml --env-file .env up -d --build
#
# Same OIDC + TLS proxy as the SQLite recipe; only the store differs. Set POSTGRES_PASSWORD.
services:
  # ADR-012 §7 deployment gate (external re-review HIGH) — refuse to serve a
  # restored-but-unreconciled store. Same one-shot pattern as the SQLite recipe; waits for
  # Postgres to be healthy first since the marker lives in the shared store. See the restore
  # runbook (docs/user/backup_and_recovery.md).
  recover-gate:
    build:
      context: .
      dockerfile: Dockerfile.postgres
      args:
        BASE_IMAGE: ${KNEO_DASH_IMAGE:-ghcr.io/kneo-agent/kneo-dash:0.8.0}
    entrypoint: ["kneo-dash"]
    command: ["recover", "--status"]
    depends_on:
      db:
        condition: service_healthy
    environment:
      KNEO_DASH_DB_URL: postgresql://kneo:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}@db:5432/kneo_dash
    restart: "no"

  kneo-dash:
    build:
      context: .
      dockerfile: Dockerfile.postgres
      args:
        BASE_IMAGE: ${KNEO_DASH_IMAGE:-ghcr.io/kneo-agent/kneo-dash:0.8.0}
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      recover-gate:
        condition: service_completed_successfully # won't start while in recovery
    environment:
      KNEO_URL: ${KNEO_URL:?set KNEO_URL}
      KNEO_API_KEY: ${KNEO_API_KEY:?set KNEO_API_KEY}
      KNEO_DASH_AUTH_MODE: oidc
      KNEO_DASH_SESSION_SECRET: ${KNEO_DASH_SESSION_SECRET:?32+ char random secret}
      KNEO_DASH_OIDC_ISSUER: ${KNEO_DASH_OIDC_ISSUER}
      KNEO_DASH_OIDC_CLIENT_ID: ${KNEO_DASH_OIDC_CLIENT_ID}
      KNEO_DASH_OIDC_CLIENT_SECRET: ${KNEO_DASH_OIDC_CLIENT_SECRET}
      KNEO_DASH_OIDC_REDIRECT_URL: ${KNEO_DASH_OIDC_REDIRECT_URL}
      KNEO_DASH_OIDC_ROLE_MAP: ${KNEO_DASH_OIDC_ROLE_MAP:-}
      KNEO_DASH_DB_URL: postgresql://kneo:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}@db:5432/kneo_dash
      # --- full production config surface (0.8.0 B4; same block as docker-compose.prod.yml,
      # parity-guarded) — `--env-file` only interpolates; unlisted vars never reach the container.
      KNEO_DASH_OIDC_SCOPES: ${KNEO_DASH_OIDC_SCOPES:-openid email profile}
      KNEO_DASH_OIDC_ROLE_CLAIM: ${KNEO_DASH_OIDC_ROLE_CLAIM:-roles}
      KNEO_DASH_OIDC_BOOTSTRAP_ADMIN: ${KNEO_DASH_OIDC_BOOTSTRAP_ADMIN:-}
      KNEO_DASH_POST_LOGIN_REDIRECT: ${KNEO_DASH_POST_LOGIN_REDIRECT:-/}
      KNEO_DASH_SESSION_COOKIE_NAME: ${KNEO_DASH_SESSION_COOKIE_NAME:-kneo_dash_session}
      KNEO_DASH_SESSION_TTL_SECONDS: ${KNEO_DASH_SESSION_TTL_SECONDS:-43200}
      KNEO_DASH_SESSION_IDLE_SECONDS: ${KNEO_DASH_SESSION_IDLE_SECONDS:-1800}
      KNEO_DASH_SESSION_PURGE_INTERVAL_SECONDS: ${KNEO_DASH_SESSION_PURGE_INTERVAL_SECONDS:-3600}
      KNEO_DASH_DEFAULT_PROFILE: ${KNEO_DASH_DEFAULT_PROFILE:-}
      KNEO_DASH_STUCK_RUNNING_SECONDS: ${KNEO_DASH_STUCK_RUNNING_SECONDS:-120}
      KNEO_DASH_STUCK_BLOCKED_SECONDS: ${KNEO_DASH_STUCK_BLOCKED_SECONDS:-300}
      KNEO_DASH_HITL_NEAR_DEADLINE_SECONDS: ${KNEO_DASH_HITL_NEAR_DEADLINE_SECONDS:-300}
      KNEO_DASH_SSE_SEND_TIMEOUT_SECONDS: ${KNEO_DASH_SSE_SEND_TIMEOUT_SECONDS:-30.0}
      KNEO_DASH_SSE_MAX_STREAMS_PER_OPERATOR: ${KNEO_DASH_SSE_MAX_STREAMS_PER_OPERATOR:-5}
      KNEO_DASH_METRICS_TOKEN: ${KNEO_DASH_METRICS_TOKEN:-}
      KNEO_DASH_GRAFANA_URL: ${KNEO_DASH_GRAFANA_URL:-}
    expose: ["8090"]

  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: kneo
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
      POSTGRES_DB: kneo_dash
    volumes: [dash-db:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "kneo"]
      interval: 5s
      timeout: 3s
      retries: 12

  proxy:
    image: nginx:1.27@sha256:6784fb0834aa7dbbe12e3d7471e69c290df3e6ba810dc38b34ae33d3c1c05f7d # digest-pinned (tp2-R3): the soaked fixture must be identifiable — a mutable tag can move under the certification
    restart: unless-stopped
    depends_on: [kneo-dash]
    ports: ["443:443", "80:80"]
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/kneo-dash.conf:ro
      - ./tls:/etc/nginx/tls:ro

volumes:
  dash-db:
