# VPS deployment — public server with a domain and automatic TLS via Caddy.
# Requires DOMAIN and ACME_EMAIL set in .env
#
# IMPORTANT: Caddy binds to ports 80 and 443. If your server already runs Traefik
# or another reverse proxy on those ports, this file will conflict. Use
# docker-compose.traefik.yml instead.
#
# Usage:
#   cp .env.example .env   # fill in all fields including DOMAIN and ACME_EMAIL
#   docker compose -f docker-compose.vps.yml up -d

networks:
  kx_net:
    driver: bridge

volumes:
  kx_data:      # persistent job data, skills, and workflows
  caddy_data:   # TLS certificates (Let's Encrypt)
  caddy_config: # Caddy runtime config

services:

  # ── kx serve ────────────────────────────────────────────────────────────────
  kx:
    image: ghcr.io/kernex-dev/kernex-agent:latest
    pull_policy: always
    networks:
      - kx_net
    # No host port — only Caddy can reach kx
    env_file: .env
    # ── Provider selection ───────────────────────────────────────────────────────
    # Default: "claude-code" uses subscription credentials (bind-mounted below).
    # For production VPS, API key providers are more reliable (no OAuth session to renew).
    # Set KERNEX_PROVIDER and KERNEX_API_KEY (or provider-specific key) in .env:
    #   groq        ->  GROQ_API_KEY        (fast, low-cost)
    #   deepseek    ->  DEEPSEEK_API_KEY    (very low-cost)
    #   openai      ->  OPENAI_API_KEY
    #   anthropic   ->  ANTHROPIC_API_KEY
    #   gemini      ->  GEMINI_API_KEY
    #   openrouter  ->  OPENROUTER_API_KEY  (routes to many models via one key)
    #   mistral     ->  MISTRAL_API_KEY
    #   fireworks   ->  FIREWORKS_API_KEY
    #   xai         ->  XAI_API_KEY
    # Or set KERNEX_BASE_URL for an OpenAI-compatible proxy (e.g. LiteLLM).
    environment:
      KERNEX_PROVIDER: ${KERNEX_PROVIDER:-claude-code}
      KERNEX_MODEL: ${KERNEX_MODEL:-}
      KERNEX_API_KEY: ${KERNEX_API_KEY:-}
      KERNEX_BASE_URL: ${KERNEX_BASE_URL:-}
    command:
      - serve
      - --host
      - "0.0.0.0"
      - --port
      - "8080"
      - --workers
      - "${KX_WORKERS:-8}"
    volumes:
      # Persistent data directory — skills and workflows are bootstrapped here on first run
      - kx_data:/home/kx/.kx
      # Claude subscription credentials (required when KERNEX_PROVIDER=claude-code)
      - type: bind
        source: ${CLAUDE_CREDENTIALS_PATH}
        target: /home/kx/.claude/.credentials.json
        read_only: true
      # Optional: override bundled skills with your own directory
      # - type: bind
      #   source: ./my-skills
      #   target: /home/kx/.kx/skills
      #   read_only: true
      # Optional: override bundled workflows with your own directory
      # - type: bind
      #   source: ./my-workflows
      #   target: /home/kx/.kx/workflows
      #   read_only: true
    restart: unless-stopped

    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=64m
    mem_limit: 1g
    memswap_limit: 1g
    cpus: "2.0"
    pids_limit: 512
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "10"

    healthcheck:
      test: ["CMD", "curl", "-sf", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 15s

  # ── Caddy (TLS termination + reverse proxy) ──────────────────────────────────
  caddy:
    build:
      context: .
      dockerfile: Dockerfile.caddy
    image: kernex-caddy:latest
    networks:
      - kx_net
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    env_file: .env
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    restart: unless-stopped
    depends_on:
      kx:
        condition: service_healthy

    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=32m

    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
