name: nexus
services:
  postgres:
    image: postgres:17-alpine
    restart: always
    deploy:
      resources:
        limits:
          memory: 384M
        reservations:
          memory: 96M
    environment:
      POSTGRES_INITDB_ARGS: --data-checksums
      POSTGRES_DB: ${POSTGRES_DB:-nexus}
      POSTGRES_USER: ${POSTGRES_USER:-nexus}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
    volumes:
      - nexus-postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-nexus} -d ${POSTGRES_DB:-nexus}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks: [nexus]
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
  api:
    build:
      context: ./backend
      args:
        NEXUS_VERSION: ${NEXUS_VERSION:-dev}
        NEXUS_GIT_SHA: ${NEXUS_GIT_SHA:-unknown}
    restart: always
    deploy:
      resources:
        limits:
          memory: 512M
        reservations:
          memory: 128M
    environment:
      ASPNETCORE_ENVIRONMENT: Production
      ASPNETCORE_URLS: http://+:8080
      ConnectionStrings__Nexus: Host=postgres;Port=5432;Database=${POSTGRES_DB:-nexus};Username=${POSTGRES_USER:-nexus};Password=${POSTGRES_PASSWORD}
      Jwt__Key: ${JWT_KEY:?Set JWT_KEY in .env}
      Jwt__Issuer: ${JWT_ISSUER:-nexus}
      Jwt__Audience: ${JWT_AUDIENCE:-nexus-web}
      Bootstrap__OwnerEmail: ${BOOTSTRAP_OWNER_EMAIL:?Set BOOTSTRAP_OWNER_EMAIL in .env}
      # Existing initialized databases do not need the bootstrap password again.
      # A fresh database still fails closed in EnsureDatabaseAsync when this is empty.
      Bootstrap__OwnerPassword: ${BOOTSTRAP_OWNER_PASSWORD:-}
      ForwardedHeaders__ForwardLimit: ${FORWARDED_HEADERS_FORWARD_LIMIT:-1}
      ForwardedHeaders__KnownProxies__0: ${FORWARDED_HEADERS_KNOWN_PROXY:-}
      ForwardedHeaders__KnownNetworks__0: ${FORWARDED_HEADERS_KNOWN_NETWORK:-}
      Integrations__OpenClaw__BaseUrl: ${OPENCLAW_BASE_URL:-http://openclaw-gateway:18789}
      Integrations__OpenClaw__RequiredVersion: ${OPENCLAW_REQUIRED_VERSION:-2026.7.1}
      Integrations__OpenClaw__Token: ${OPENCLAW_GATEWAY_TOKEN:-}
      Integrations__OpenClaw__Password: ${OPENCLAW_GATEWAY_PASSWORD:-}
      GatewayConnector__DeviceStatePath: /var/lib/nexus/openclaw/device-state.json
      GatewayConnector__OperationAuditPath: /var/lib/nexus/openclaw/operation-audit.jsonl
      GatewayConnector__ClientId: nexus
      GatewayConnector__ClientMode: backend
      GatewayConnector__ExternalClientIdentitySupported: ${OPENCLAW_EXTERNAL_CLIENT_ID_SUPPORTED:-false}
      GatewayConnector__AllowReservedInternalClientIdentity: "false"
      GatewayConnector__TlsFingerprint: ${OPENCLAW_TLS_FINGERPRINT:-}
      OpenClawSetup__ExternalClientIdentitySupported: ${OPENCLAW_EXTERNAL_CLIENT_ID_SUPPORTED:-false}
      Integrations__OpenClaw__AllowCommandCron: ${OPENCLAW_ALLOW_COMMAND_CRON:-false}
      Admin__ResetToken: ${Admin__ResetToken:-}
      NexusApiKey: ${NEXUS_API_KEY:-}
    extra_hosts:
      - host.docker.internal:host-gateway
    depends_on:
      postgres:
        condition: service_healthy
        restart: true
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health/ready || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s
    volumes:
      - nexus-openclaw-device:/var/lib/nexus/openclaw
    networks:
      - nexus
      - openclaw_default
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
  web:
    build:
      context: ./frontend
      args:
        NEXUS_VERSION: ${NEXUS_VERSION:-dev}
        NEXUS_GIT_SHA: ${NEXUS_GIT_SHA:-unknown}
    restart: always
    deploy:
      resources:
        limits:
          memory: 128M
        reservations:
          memory: 32M
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nexus.rule=Host(`nexus.noveria.net`)"
      - "traefik.http.routers.nexus.tls=true"
      - "traefik.http.routers.nexus.tls.certresolver=letsencrypt"
      - "traefik.http.services.nexus.loadbalancer.server.port=80"
    ports:
      - "127.0.0.1:18880:80"
    depends_on:
      api:
        condition: service_healthy
        restart: true
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:80/ || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    networks:
      - nexus
      - proxy
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
networks:
  nexus:
  openclaw_default:
    external: true
  proxy:
    external: true
volumes:
  nexus-postgres:
  nexus-openclaw-device:
