# Talos — self-hosted deployment
#
# This is the file we ship to customers. It runs PRE-BUILT images from a registry, so
# there is no source checkout, no build step, and no toolchain to install.
#
#   1. cp .env.example .env
#   2. follow the three instructions in .env
#   3. docker compose up -d
#   4. open http://localhost:3000
#
# Deliberately NOT the repository's root docker-compose.yml. That one builds from source
# and exposes every SaaS, billing and platform knob — handing it to a customer is handing
# them ninety lines of configuration to be confused by. This file has three variables.
#
# DEPLOYMENT_MODE is absent on purpose: it is baked into the image
# (TALOS_DISTRIBUTION=self-hosted), which also pins the mode and makes weak secrets fatal
# rather than a warning. See docs/deployment/MODES_DATABASES_AND_DISTRIBUTION.md.

services:
  postgres:
    # pgvector, not plain postgres:16 — the AI knowledge base needs the `vector`
    # extension. Drop-in replacement, same data directory.
    image: pgvector/pgvector:pg16
    restart: unless-stopped
    environment:
      POSTGRES_DB: talos
      POSTGRES_USER: talos
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
    volumes:
      - talos_postgres:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U talos -d talos']
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - talos_redis:/data
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 10s
      timeout: 5s
      retries: 5

  backend:
    image: ${TALOS_REGISTRY:-talosfleet}/talos-backend:${TALOS_VERSION:-latest}
    restart: unless-stopped
    ports:
      - '${BACKEND_PORT:-3001}:3001'
    environment:
      DATABASE_URL: postgresql://talos:${POSTGRES_PASSWORD}@postgres:5432/talos?schema=public
      # `redis`, not `localhost` — inside the compose network the hostname is the service
      # name. A stale `localhost` here makes the backend hang at boot and login fail with
      # an empty response, which is not an obvious symptom.
      REDIS_URL: redis://redis:6379

      # The only two secrets. The backend REFUSES TO BOOT if either is missing or is the
      # published default — see .env for why, and for how to generate them.
      JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET in .env}
      ENCRYPTION_KEY: ${ENCRYPTION_KEY:?set ENCRYPTION_KEY in .env}

      # Public URL of the frontend, used in the links inside emails.
      FRONTEND_URL: ${FRONTEND_URL:-http://localhost:3000}

      # ── Optional, all defaulted ──
      # Every one of these must be listed here, not just documented in .env: an unlisted
      # variable is silently dropped by Compose, so a customer would set it and nothing
      # would happen — a failure with no error message anywhere.
      ALLOW_SELF_REGISTRATION: ${ALLOW_SELF_REGISTRATION:-false}
      ALLOW_MULTI_ORG: ${ALLOW_MULTI_ORG:-false}

      # Sign-in with Google / GitHub. The backend refuses to boot if a provider is
      # enabled without credentials, rather than showing a button that cannot work.
      OAUTH_CALLBACK_BASE_URL: ${OAUTH_CALLBACK_BASE_URL:-}
      OAUTH_GOOGLE_ENABLED: ${OAUTH_GOOGLE_ENABLED:-false}
      OAUTH_GOOGLE_CLIENT_ID: ${OAUTH_GOOGLE_CLIENT_ID:-}
      OAUTH_GOOGLE_CLIENT_SECRET: ${OAUTH_GOOGLE_CLIENT_SECRET:-}
      OAUTH_GITHUB_ENABLED: ${OAUTH_GITHUB_ENABLED:-false}
      OAUTH_GITHUB_CLIENT_ID: ${OAUTH_GITHUB_CLIENT_ID:-}
      OAUTH_GITHUB_CLIENT_SECRET: ${OAUTH_GITHUB_CLIENT_SECRET:-}

      # Outbound email. Defaults to `log`, which writes invitation and reset links to
      # `docker compose logs backend` — workable for one admin, awkward for a team.
      MAIL_TRANSPORT: ${MAIL_TRANSPORT:-log}
      SMTP_HOST: ${SMTP_HOST:-}
      SMTP_PORT: ${SMTP_PORT:-587}
      SMTP_USER: ${SMTP_USER:-}
      SMTP_PASSWORD: ${SMTP_PASSWORD:-}
      MAIL_FROM: ${MAIL_FROM:-}
    volumes:
      # Terraform workspaces and generated state. Persisted because losing a state file
      # orphans real cloud resources that Terraform can then neither see nor destroy.
      - talos_terraform:/app/infra/terraform/workspaces
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    healthcheck:
      test: ['CMD', 'wget', '-qO-', 'http://localhost:3001/api/health']
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  frontend:
    image: ${TALOS_REGISTRY:-talosfleet}/talos-frontend:${TALOS_VERSION:-latest}
    restart: unless-stopped
    ports:
      - '${FRONTEND_PORT:-3000}:3000'
    environment:
      # There is deliberately no API URL to set here.
      #
      # Next.js compiles NEXT_PUBLIC_* into the client bundle at BUILD time, so a prebuilt
      # image cannot be told its API address at run time — this block used to pass
      # NEXT_PUBLIC_API_URL and it silently did nothing, leaving every install that was not
      # reached at localhost with a UI that could not talk to its own API.
      #
      # The browser now calls this container on a relative /api, and the Next server
      # forwards to the backend below. So Talos works at whatever hostname you put in front
      # of it, with nothing to configure and no CORS to get wrong.
      BACKEND_INTERNAL_URL: http://backend:3001
    depends_on:
      - backend

volumes:
  talos_postgres:
  talos_redis:
  talos_terraform:
