# cxai-stack — full CxWebApp + sidecar services compose.
# Lives at /srv/cxai/compose/cxai-stack/compose.yaml on the VPS.
#
# All sidecars listen only on the internal `cxai` network. Only the cxwebapp
# is bound to 127.0.0.1:8085 for Caddy to TLS-terminate in front of.

name: cxai-stack

networks:
  cxai:
    driver: bridge

volumes:
  demand-reports:
  slack-state:
  diffusion-out:
  # Forge models + outputs + huggingface cache. Large (~5GB for SD 1.5
  # base alone). Lives on /var/lib/docker/volumes on the VPS, which has
  # ~360GB free.
  forge-data:

services:
  cxwebapp:
    image: registry.76-13-126-127.nip.io/cxai/cxwebapp:latest
    pull_policy: always
    container_name: cxai-cxwebapp
    restart: unless-stopped
    networks: [cxai]
    ports: ["127.0.0.1:8085:8080"]
    volumes:
      # Inject the freshly-built CxAI ADE build metadata over the baked-in
      # default so `/api/mac/info` reflects what's actually shipping.
      - ./cxai-mac-build-info.json:/opt/cxwebapp/share/cxai-mac/build-info.json:ro
    environment:
      CXWEBAPP_PORT: "8080"
      CXAI_DIFFUSION_UPSTREAM: "http://cxai-diffusion:8101"
      CXAI_DEMAND_UPSTREAM:    "http://cxai-demand:8102"
      CXAI_LANG_UPSTREAM:      "http://cxai-langsmith:8103"
      CXAI_SLACK_UPSTREAM:     "http://cxai-slack:8104"
      CXAI_MAC_BUILD_INFO:     "/opt/cxwebapp/share/cxai-mac/build-info.json"
      CXAI_MAC_DOWNLOAD_URL:   "${CXAI_MAC_DOWNLOAD_URL:-}"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8080/api/health"]
      interval: 30s
      timeout: 3s
      retries: 3

  # Real Stable-Diffusion-WebUI-Forge backend, CPU-only build (no GPU on
  # this VPS). Image is built from diffusion/forge/Dockerfile and serves
  # the standard /sdapi/v1 surface that cxai-diffusion's ForgeClient
  # talks to. First boot pulls SD 1.5 (~4GB) into forge-data.
  cxai-forge:
    image: registry.76-13-126-127.nip.io/cxai/cxai-forge:latest
    pull_policy: always
    container_name: cxai-forge
    restart: unless-stopped
    networks: [cxai]
    volumes:
      - forge-data:/data
    environment:
      # Reserve threads for the rest of the stack — EPYC has 8 cores.
      OMP_NUM_THREADS: "${CXAI_FORGE_THREADS:-6}"
      MKL_NUM_THREADS: "${CXAI_FORGE_THREADS:-6}"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:7860/sdapi/v1/options"]
      # Generous start period: first boot has to download SD 1.5.
      start_period: 600s
      interval: 30s
      timeout: 10s
      retries: 5

  cxai-diffusion:
    image: registry.76-13-126-127.nip.io/cxai/cxai-diffusion-svc:latest
    pull_policy: always
    container_name: cxai-diffusion
    restart: unless-stopped
    networks: [cxai]
    depends_on:
      cxai-forge:
        condition: service_started
    volumes:
      - diffusion-out:/var/lib/cxai-diffusion/out
    environment:
      # Point at the in-network Forge container by default; override with
      # CXAI_DIFFUSION_BASE_URL to swap in a remote/hosted backend. (The
      # gateway reads CXAI_DIFFUSION_BASE_URL — see cxai_diffusion.config
      # ._resolve_base_url. Keep this var name aligned.)
      CXAI_DIFFUSION_BASE_URL: "${CXAI_DIFFUSION_BASE_URL:-http://cxai-forge:7860}"
      CXAI_DIFFUSION_GATEWAY_TOKEN: "${CXAI_DIFFUSION_GATEWAY_TOKEN:-}"
      CXAI_DIFFUSION_OUT_DIR: "/var/lib/cxai-diffusion/out"
      CXAI_DIFFUSION_QUANTUM_SEED: "${CXAI_DIFFUSION_QUANTUM_SEED:-1}"
      # qiskit/matplotlib both want a writable home; /app is owned by root
      # (image is built with USER cxai uid 10001 but no /home).
      MPLCONFIGDIR: "/tmp/matplotlib"
      HOME: "/tmp"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8101/healthz"]
      interval: 30s
      timeout: 5s
      retries: 3

  cxai-demand:
    image: registry.76-13-126-127.nip.io/cxai/cxai-demand-svc:latest
    pull_policy: always
    container_name: cxai-demand
    restart: unless-stopped
    networks: [cxai]
    volumes:
      - demand-reports:/var/lib/cxai-demand/reports
    environment:
      OPENAI_API_KEY:       "${OPENAI_API_KEY:-}"
      CXAI_BASE_URL:        "${CXAI_BASE_URL:-https://cxai-studio.com/studio/v1}"
      CXAI_DRY_RUN:         "${CXAI_DRY_RUN:-true}"
      CXAI_DEMAND_PORT:     "8102"
      CXAI_DEMAND_REPORTS_DIR: "/var/lib/cxai-demand/reports"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8102/healthz"]
      interval: 30s
      timeout: 5s
      retries: 3

  cxai-langsmith:
    image: registry.76-13-126-127.nip.io/cxai/cxai-langsmith-svc:latest
    pull_policy: always
    container_name: cxai-langsmith
    restart: unless-stopped
    networks: [cxai]
    environment:
      CXBASE_TRANSPORT:    "${CXBASE_TRANSPORT:-bearer}"
      CXBASE_BASE_URL:     "${CXBASE_BASE_URL:-https://cxai-studio.com/studio/v1}"
      CXBASE_BEARER_TOKEN: "${CXBASE_BEARER_TOKEN:-freecc}"
      CXAI_MCP_URL:        "${CXAI_MCP_URL:-http://cxai-mac.tail91d296.ts.net/mcp}"
      LANGSMITH_API_KEY:   "${LANGSMITH_API_KEY:-}"
      LANGSMITH_TRACING:   "${LANGSMITH_TRACING:-false}"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8103/healthz"]
      interval: 30s
      timeout: 5s
      retries: 3

  cxai-slack:
    image: registry.76-13-126-127.nip.io/cxai/cxai-slack-svc:latest
    pull_policy: always
    container_name: cxai-slack
    restart: unless-stopped
    networks: [cxai]
    volumes:
      - slack-state:/var/lib/cxai-slack
    environment:
      SLACK_BOT_TOKEN:           "${SLACK_BOT_TOKEN:-}"
      SLACK_APP_TOKEN:           "${SLACK_APP_TOKEN:-}"
      SLACK_APP_SIGNING_SECRET:  "${SLACK_APP_SIGNING_SECRET:-}"
      SLACK_APP_ID:              "${SLACK_APP_ID:-}"
      SLACK_APP_CLIENT_ID:       "${SLACK_APP_CLIENT_ID:-}"
      SLACK_BOT_WEBHOOK_URL:     "${SLACK_BOT_WEBHOOK_URL:-}"
      USER_OAUTH_TOKEN:          "${USER_OAUTH_TOKEN:-}"
      OPENAI_API_KEY:            "${OPENAI_API_KEY:-}"
      OPENAI_BASE_URL:           "${OPENAI_BASE_URL:-}"
      OPENAI_MODEL:              "${OPENAI_MODEL:-}"
      CXAI_GATEWAY_TOKEN:        "${CXAI_GATEWAY_TOKEN:-${CXBASE_BEARER_TOKEN:-freecc}}"
      CXAI_GATEWAY_URL:          "${CXAI_GATEWAY_URL:-${CXBASE_BASE_URL:-https://cxai-studio.com/studio/v1}}"
      CXAI_GATEWAY_MODEL:        "${CXAI_GATEWAY_MODEL:-${CXBASE_MCP_MODEL:-claude-sonnet-4}}"
      XDG_STATE_HOME:            "/var/lib/cxai-slack"
      HOME:                      "/home/cxai"
      CXAI_SANDBOX_ROOT:         "/tmp/cxai-sandbox"
      CXAI_SLACK_SVC_PORT:       "8104"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8104/healthz"]
      interval: 30s
      timeout: 5s
      retries: 3
