# docker-compose.ecosystem.yml — Full ByteLyst Ecosystem
#
# Brings up all infrastructure, platform services, dashboards, and product
# backends + web apps in a single Docker Compose stack.
#
# Prerequisites:
#   1. Copy .env.ecosystem.example → .env.ecosystem and fill in values
#   2. All product repos must be sibling directories of this repo
#
# Usage:
#   GITEA_NPM_TOKEN=<token> docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem up --build
#
# The GITEA_NPM_TOKEN env var is required for product Docker builds that install
# @bytelyst/* packages from the local Gitea npm registry via BuildKit secrets.
#
# Port Map:
#   Infrastructure: cosmos-emulator 8081, azurite 10000, mailpit 1025/8025,
#                   loki 3100, grafana 3000, traefik 80/8080
#   Phase 2 (opt-in): prometheus 9090, valkey 6379
#   Platform:       platform-service 4003, extraction-service 4005, mcp-server 4007
#   Dashboards:     admin-web 3001, tracker-web 3003
#   Products:       peakpulse 4010, chronomind 4011, jarvisjr 4012,
#                   nomgap 4013, mindlyst 4014, lysnrai 4015,
#                   notelett 4016, flowmonk 4017, actiontrail 4018,
#                   localmemgpt 4019, efforise 4020
#   Product webs:   chronomind 3030, jarvisjr 3035, flowmonk 3040,
#                   notelett 3045, mindlyst 3050, nomgap on Vercel,
#                   actiontrail 3060, localmemgpt 3070, lysnrai 3002,
#                   efforise 3080

# Product Dockerfiles require BuildKit secrets for Gitea npm registry access.
# GITEA_NPM_HOST defaults to host.docker.internal (Mac local dev).
x-product-build: &product-build
  args:
    GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
  secrets:
    - gitea_npm_token

secrets:
  gitea_npm_token:
    environment: GITEA_NPM_TOKEN

services:
  # ═════════════════════════════════════════════════════════════════
  # INFRASTRUCTURE
  # ═════════════════════════════════════════════════════════════════

  cosmos-emulator:
    image: mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview
    ports:
      - '127.0.0.1:8081:8081'
      - '127.0.0.1:1234:1234'
    environment:
      - PROTOCOL=http
      - ENABLE_EXPLORER=true
      - GATEWAY_PUBLIC_ENDPOINT=cosmos-emulator
    healthcheck:
      test: ['CMD-SHELL', 'curl -sf http://127.0.0.1:8080/ready || exit 1']
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 20s
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 1g

  azurite:
    image: mcr.microsoft.com/azure-storage/azurite:3.35.0
    command: azurite-blob --blobHost 0.0.0.0 --blobPort 10000 --location /data --skipApiVersionCheck
    ports:
      - '127.0.0.1:10000:10000'
    volumes:
      - azurite-data:/data
    healthcheck:
      test: ['CMD', 'nc', '-z', '127.0.0.1', '10000']
      interval: 10s
      timeout: 5s
      retries: 6
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 256m

  mailpit:
    image: axllent/mailpit:v1.27.5
    ports:
      - '127.0.0.1:1025:1025'
      - '127.0.0.1:8025:8025'
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:8025']
      interval: 10s
      timeout: 5s
      retries: 6
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 128m

  loki:
    image: grafana/loki:3.3.2
    ports:
      - '127.0.0.1:3100:3100'
    volumes:
      - ./services/monitoring/loki/loki-config.yml:/etc/loki/local-config.yaml
      - loki-data:/loki
    command: -config.file=/etc/loki/local-config.yaml
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3100/ready']
      interval: 15s
      timeout: 5s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 384m

  grafana:
    image: grafana/grafana:11.4.0
    ports:
      - '127.0.0.1:3000:3000'
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=bytelyst
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - ./services/monitoring/grafana/provisioning:/etc/grafana/provisioning
      - ./services/monitoring/grafana/dashboards:/var/lib/grafana/dashboards
      - grafana-data:/var/lib/grafana
    depends_on:
      loki:
        condition: service_started
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3000/api/health']
      interval: 15s
      timeout: 5s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 384m

  prometheus:
    image: prom/prometheus:v3.5.0
    profiles:
      - phase2-observability
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.enable-lifecycle'
    volumes:
      - ./services/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    depends_on:
      node-exporter:
        condition: service_started
      cadvisor:
        condition: service_started
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:9090/-/healthy']
      interval: 15s
      timeout: 5s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 384m

  node-exporter:
    image: prom/node-exporter:v1.9.1
    profiles:
      - phase2-observability
    command:
      - '--path.rootfs=/host'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($|/)'
    volumes:
      - /:/host:ro,rslave
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:9100/metrics']
      interval: 15s
      timeout: 5s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 128m

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.49.1
    profiles:
      - phase2-observability
    privileged: true
    command:
      - '--housekeeping_interval=30s'
      - '--docker_only=true'
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker:/var/lib/docker:ro
      - /dev/disk:/dev/disk:ro
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:8080/healthz']
      interval: 15s
      timeout: 5s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 256m

  valkey:
    image: valkey/valkey:8-alpine
    profiles:
      - phase2-shared
    command:
      - valkey-server
      - --save
      - '60'
      - '1'
      - --loglevel
      - warning
    volumes:
      - valkey-data:/data
    healthcheck:
      test: ['CMD', 'valkey-cli', 'ping']
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 128m

  gateway:
    image: traefik:v3.3
    profiles:
      - legacy-gateway
    command:
      - '--api.insecure=true'
      - '--providers.docker=true'
      - '--providers.docker.exposedbydefault=false'
      - '--entrypoints.web.address=:80'
      - '--accesslog=true'
      - '--accesslog.format=json'
    ports:
      - '80:80'
      - '127.0.0.1:8080:8080'
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      loki:
        condition: service_started
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 128m

  caddy:
    image: caddy:2-alpine
    container_name: caddy
    ports:
      # Bind to public eth0 IP only (not 0.0.0.0) so tailscaled can claim
      # 100.87.53.10:443 for `tailscale serve` on the tailnet.
      - '187.124.159.82:80:80'
      - '187.124.159.82:443:443'
    volumes:
      - ../Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
      - caddy-config:/config
    depends_on:
      platform-service:
        condition: service_healthy
      extraction-service:
        condition: service_healthy
      mcp-server:
        condition: service_healthy
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 256m

  # ═════════════════════════════════════════════════════════════════
  # PLATFORM SERVICES (from this repo)
  # ═════════════════════════════════════════════════════════════════

  platform-service:
    build:
      context: .
      dockerfile: services/platform-service/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4003
      - COSMOS_AUTO_INIT=true
      - PLATFORM_SERVICE_URL=http://platform-service:4003
      - EXTRACTION_SERVICE_URL=http://extraction-service:4005
      - MCP_SERVER_URL=http://mcp-server:4007
      - MAILPIT_UI_URL=http://mailpit:8025
    depends_on:
      mailpit:
        condition: service_healthy
      azurite:
        condition: service_healthy
      cosmos-emulator:
        condition: service_healthy
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.platform.rule=PathPrefix(`/platform`)'
      - 'traefik.http.services.platform.loadbalancer.server.port=4003'
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4003/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  extraction-service:
    build:
      context: .
      dockerfile: services/extraction-service/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4005
      - PYTHON_SIDECAR_URL=http://localhost:4006
      - PRODUCT_RATE_LIMIT_STORE=${PRODUCT_RATE_LIMIT_STORE:-valkey}
      - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379}
    depends_on:
      cosmos-emulator:
        condition: service_healthy
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.extraction.rule=PathPrefix(`/extraction`)'
      - 'traefik.http.services.extraction.loadbalancer.server.port=4005'
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4005/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  mcp-server:
    build:
      context: .
      dockerfile: services/mcp-server/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4007
      - PLATFORM_SERVICE_URL=http://platform-service:4003
      - EXTRACTION_SERVICE_URL=http://extraction-service:4005
    depends_on:
      platform-service:
        condition: service_healthy
      extraction-service:
        condition: service_healthy
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.mcp.rule=PathPrefix(`/mcp`)'
      - 'traefik.http.services.mcp.loadbalancer.server.port=4007'
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4007/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 384m

  # ═════════════════════════════════════════════════════════════════
  # PLATFORM DASHBOARDS (from this repo)
  # ═════════════════════════════════════════════════════════════════

  admin-web:
    build:
      context: .
      dockerfile: dashboards/admin-web/Dockerfile
    ports:
      - '127.0.0.1:3001:3001'
    env_file:
      - .env.ecosystem
    environment:
      - PORT=3001
      - PLATFORM_SERVICE_URL=http://platform-service:4003
      - EXTRACTION_SERVICE_URL=http://extraction-service:4005
      - SEED_SECRET=${SEED_SECRET:-dev-seed-secret}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3001']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  tracker-web:
    build:
      context: .
      dockerfile: dashboards/tracker-web/Dockerfile
    ports:
      - '127.0.0.1:3003:3003'
    env_file:
      - .env.ecosystem
    environment:
      - PORT=3003
      - PLATFORM_SERVICE_URL=http://platform-service:4003
      - PLATFORM_API_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3003']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  # ═════════════════════════════════════════════════════════════════
  # PRODUCT BACKENDS (from sibling repos)
  # ═════════════════════════════════════════════════════════════════

  peakpulse-backend:
    build:
      <<: *product-build
      context: ../learning_ai_peakpulse
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4010
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4010/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  chronomind-backend:
    build:
      <<: *product-build
      context: ../learning_ai_clock
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4011
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4011/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  jarvisjr-backend:
    build:
      <<: *product-build
      context: ../learning_ai_jarvis_jr
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4012
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4012/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  nomgap-backend:
    build:
      <<: *product-build
      context: ../learning_ai_fastgap
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4013
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4013/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  mindlyst-backend:
    build:
      <<: *product-build
      context: ../learning_multimodal_memory_agents
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4014
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4014/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  lysnrai-backend:
    build:
      <<: *product-build
      context: ../learning_voice_ai_agent
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4015
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4015/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  notelett-backend:
    build:
      <<: *product-build
      context: ../learning_ai_notes
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4016
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
      - EXTRACTION_SERVICE_URL=http://extraction-service:4005
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4016/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  flowmonk-backend:
    build:
      <<: *product-build
      context: ../learning_ai_flowmonk
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4017
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4017/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  actiontrail-backend:
    build:
      <<: *product-build
      context: ../learning_ai_trails
      dockerfile: backend/Dockerfile
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4018
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4018/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  localmemgpt-backend:
    build:
      <<: *product-build
      context: ../learning_ai_local_memory_gpt
      dockerfile: backend/Dockerfile
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4019
      - HOST=0.0.0.0
      - OLLAMA_URL=http://host.docker.internal:11434
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    volumes:
      - localmemgpt-data:/app/db
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4019/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  efforise-backend:
    build:
      <<: *product-build
      context: ../learning_ai_efforise
      dockerfile: backend/Dockerfile
    ports:
      - '127.0.0.1:4020:4020'
    env_file:
      - .env.ecosystem
    environment:
      - PORT=4020
      - HOST=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      platform-service:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:4020/health']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  # ═════════════════════════════════════════════════════════════════
  # PRODUCT WEB APPS (from sibling repos)
  # ═════════════════════════════════════════════════════════════════

  lysnrai-dashboard:
    build:
      <<: *product-build
      context: ../learning_voice_ai_agent
      dockerfile: user-dashboard-web/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        # NEXT_PUBLIC_* args are baked at build time for browser-side code
        NEXT_PUBLIC_PLATFORM_SERVICE_URL: http://localhost:4003
        NEXT_PUBLIC_PRODUCT_ID: lysnrai
    ports:
      - '127.0.0.1:3002:3002'
    environment:
      - NODE_ENV=production
      - PORT=3002
      # Non-prefixed vars are used by server-side code (API routes, SSR)
      - PLATFORM_SERVICE_URL=http://platform-service:4003
      - ACTIONTRAIL_SERVICE_URL=http://actiontrail-backend:4018
    depends_on:
      lysnrai-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3002']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  chronomind-web:
    build:
      <<: *product-build
      context: ../learning_ai_clock
      dockerfile: web/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        NEXT_PUBLIC_BACKEND_URL: http://localhost:4011
        NEXT_PUBLIC_PLATFORM_SERVICE_URL: http://localhost:4003
    ports:
      - '127.0.0.1:3030:3030'
    environment:
      - NODE_ENV=production
      - PORT=3030
      - BACKEND_URL=http://chronomind-backend:4011
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      chronomind-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3030']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  jarvisjr-web:
    build:
      <<: *product-build
      context: ../learning_ai_jarvis_jr
      dockerfile: web/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        NEXT_PUBLIC_PLATFORM_SERVICE_URL: http://localhost:4003
    ports:
      - '127.0.0.1:3035:3035'
    environment:
      - NODE_ENV=production
      - PORT=3035
      - HOSTNAME=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      jarvisjr-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3035']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  flowmonk-web:
    build:
      <<: *product-build
      context: ../learning_ai_flowmonk
      dockerfile: web/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        NEXT_PUBLIC_API_URL: http://localhost:4017
        NEXT_PUBLIC_PLATFORM_URL: http://localhost:4003/api
    ports:
      - '127.0.0.1:3040:3040'
    environment:
      - NODE_ENV=production
      - PORT=3040
      - HOSTNAME=0.0.0.0
      - API_URL=http://flowmonk-backend:4017
      - PLATFORM_URL=http://platform-service:4003/api
    depends_on:
      flowmonk-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3040']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  notelett-web:
    build:
      <<: *product-build
      context: ../learning_ai_notes
      dockerfile: web/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        NEXT_PUBLIC_NOTES_API_URL: http://localhost:4016/api
        NEXT_PUBLIC_PLATFORM_SERVICE_URL: http://localhost:4003/api
    ports:
      - '127.0.0.1:3045:3045'
    environment:
      - NODE_ENV=production
      - PORT=3045
      - NOTES_API_URL=http://notelett-backend:4016/api
      - PLATFORM_SERVICE_URL=http://platform-service:4003/api
    depends_on:
      notelett-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3045']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  mindlyst-web:
    build:
      <<: *product-build
      context: ../learning_multimodal_memory_agents
      dockerfile: mindlyst-native/web/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        NEXT_PUBLIC_PLATFORM_SERVICE_URL: http://localhost:4003
    ports:
      - '127.0.0.1:3050:3050'
    environment:
      - NODE_ENV=production
      - PORT=3050
      - HOSTNAME=0.0.0.0
      - PLATFORM_SERVICE_URL=http://platform-service:4003
    depends_on:
      mindlyst-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3050']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  # TODO(nomgap): Decide whether local Docker smoke tests still need a web
  # service once the Vercel deployment path is fully documented.
  # nomgap-web: deployed to Vercel; not part of this Docker stack.

  actiontrail-web:
    build:
      <<: *product-build
      context: ../learning_ai_trails
      dockerfile: web/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        NEXT_PUBLIC_API_URL: http://localhost:4018
        NEXT_PUBLIC_PLATFORM_URL: http://localhost:4003
    ports:
      - '127.0.0.1:3060:3060'
    environment:
      - NODE_ENV=production
      - PORT=3060
      - HOSTNAME=0.0.0.0
      - API_URL=http://actiontrail-backend:4018
      - PLATFORM_URL=http://platform-service:4003
    depends_on:
      actiontrail-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3060']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  localmemgpt-web:
    build:
      <<: *product-build
      context: ../learning_ai_local_memory_gpt
      dockerfile: web/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        NEXT_PUBLIC_BACKEND_URL: http://localhost:4019
        NEXT_PUBLIC_PLATFORM_URL: http://localhost:4003
    ports:
      - '127.0.0.1:3070:3070'
    environment:
      - NODE_ENV=production
      - PORT=3070
      - HOSTNAME=0.0.0.0
      - BACKEND_URL=http://localmemgpt-backend:4019
      - PLATFORM_URL=http://platform-service:4003
    depends_on:
      localmemgpt-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3070']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  efforise-web:
    build:
      <<: *product-build
      context: ../learning_ai_efforise
      dockerfile: client/Dockerfile
    ports:
      - '127.0.0.1:3080:3080'
    depends_on:
      efforise-backend:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3080']
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

  # ── Local LLM Lab (no backend — dashboard talks directly to Ollama) ──

  llmlab-dashboard:
    build:
      <<: *product-build
      context: ../learning_ai_local_llms
      dockerfile: dashboard/Dockerfile
      args:
        GITEA_NPM_HOST: ${GITEA_NPM_HOST:-host.docker.internal}
        OLLAMA_URL: http://host.docker.internal:11434
    ports:
      - '127.0.0.1:3075:3075'
    environment:
      - NODE_ENV=production
      - PORT=3075
      - HOSTNAME=0.0.0.0
      - OLLAMA_URL=http://host.docker.internal:11434
      - OLLAMA_HOST=http://host.docker.internal:11434
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    healthcheck:
      test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3075']
      interval: 15s
      timeout: 5s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512m

# ═════════════════════════════════════════════════════════════════
# VOLUMES
# ═════════════════════════════════════════════════════════════════

volumes:
  azurite-data:
  loki-data:
  grafana-data:
  prometheus-data:
  valkey-data:
  localmemgpt-data:
  caddy-data:
  caddy-config:
