# External-user setup — pulls pre-built images from GitHub Container Registry
# (published by ``.github/workflows/docker.yml`` on every push to ``main`` and
# every ``v*.*.*`` tag). No local toolchain required.
#
# Usage (from any folder):
#     curl -fsSL https://batazor.github.io/autopilot-page/docker-compose.prod.yml -o docker-compose.prod.yml
#     docker compose -f docker-compose.prod.yml up -d
#     open http://127.0.0.1:3000/overview
#
# First start:
#   * area.json, references/, db/buildings, db/gear, db/items, db/assets are
#     baked into the image — nothing to mount, nothing to seed.
#   * The dashboard prompts for a license file on first load. Grab the current
#     ``licence.jwt`` from the ``#install`` channel on Discord, upload it
#     in the UI, then ``docker compose ... restart bot`` once.
#
# Images are currently published as ``:latest`` only. ``WOS_IMAGE_TAG`` remains
# configurable for private forks that publish their own tags.
#
# Using a fork's images:
#     WOS_REGISTRY=ghcr.io/your-fork docker compose -f docker-compose.prod.yml up -d
#
# === ADB / network model ===
# ``bot`` runs in ``network_mode: host`` so the container shares the host's
# loopback. That way the host's ``adb start-server`` (bound to 127.0.0.1:5037
# by default — safe) is reachable as ``127.0.0.1:5037`` from inside the
# container, **without** exposing ADB on the host's external interfaces via
# ``adb -a``.
#
# Side effects of host-mode that this file already accounts for:
#   * ``bot`` cannot use the Compose-internal DNS name (``redis``) for the
#     other services — host-mode bot has no Compose network attached.
#     ``bot`` talks to Redis over the shared ``redis_socket`` named volume.
#   * ``bot`` has no ``ports:`` block — workers bind only to Redis/ADB on the host.
#   * ``api`` and ``web`` use the default bridge network and reach Redis via
#     the ``redis`` service name; ``web`` proxies ``/api`` to ``api:8765``.
#
# Platform support:
#   * **Linux** — ``network_mode: host`` is fully supported out of the box.
#   * **Docker Desktop (macOS/Windows)** — works only with the **Host
#     networking** beta enabled: Settings → Resources → Network → "Enable
#     host networking". Without it, the bot can reach ``redis`` on loopback
#     but **not** the host's adb server.
#
# === Persistent state ===
# Only the two mutable bits live in named volumes (shared between bot and api):
#   * ``wos_state``   → /app/db/state    (SQLite: devices, accounts, runtime)
#   * ``wos_license`` → /app/license-data (license file uploaded via the UI)
# Both survive ``docker compose down``; wiped only by ``docker compose down -v``.

services:
  redis:
    image: redis:alpine
    # ``redis:alpine`` runs as the ``redis`` user, but a fresh named volume
    # is mounted root-owned — so bind() on the socket fails with EACCES on
    # first boot. ``chown`` the socket dir in a thin entrypoint wrapper,
    # then ``exec docker-entrypoint.sh`` keeps the upstream UID drop. The
    # ``--unixsocketperm 777`` line lets bot/api containers (different UIDs
    # from ``redis``) connect without us aligning UIDs across images.
    entrypoint:
      - sh
      - -c
      - >
        chown redis:redis /var/run/redis &&
        exec docker-entrypoint.sh
        redis-server
        --appendonly yes
        --unixsocket /var/run/redis/redis.sock
        --unixsocketperm 777
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - redis_data:/data
      - redis_socket:/var/run/redis
    restart: always
    healthcheck:
      test: ["CMD", "redis-cli", "-s", "/var/run/redis/redis.sock", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

  bot:
    image: ${WOS_REGISTRY:-ghcr.io/batazor/autopilot}/bot:${WOS_IMAGE_TAG:-latest}
    # host-mode: shared loopback with the host so ``adb`` and the
    # ``127.0.0.1:5037`` server are reachable without ``adb -a`` (which would
    # expose ADB on every interface, including LAN).
    network_mode: host
    depends_on:
      redis:
        condition: service_healthy
    environment:
      # ``redis-py`` accepts ``unix:///path?db=N`` directly. Volume-mounted
      # at the same path in this container; host-mode networking doesn't
      # affect bind mounts.
      WOS_REDIS_URL: unix:///var/run/redis/redis.sock?db=0
      # Host's adb server, untouched (default loopback bind is fine because
      # the container shares the host's lo).
      ADB_SERVER_SOCKET: tcp:127.0.0.1:5037
      # ``WOS_LICENSE`` env override; the primary path is the license file
      # uploaded through the dashboard (lands in the ``wos_license`` volume).
      WOS_LICENSE: ${WOS_LICENSE:-}
    volumes:
      - wos_state:/app/db/state
      - wos_license:/app/license-data
      - redis_socket:/var/run/redis
      # Stable per-host id for license binding. Mounted :ro so the container
      # observes the same value across rebuilds. Falls back to MAC+hostname
      # if the file is absent (e.g. on platforms without systemd).
      - /etc/machine-id:/etc/machine-id:ro
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "ps aux | grep -q '[w]orker.supervisor' || exit 1"]
      interval: 30s
      timeout: 5s
      start_period: 45s
      retries: 3

  api:
    image: ${WOS_REGISTRY:-ghcr.io/batazor/autopilot}/bot:${WOS_IMAGE_TAG:-latest}
    command: ["api"]
    depends_on:
      redis:
        condition: service_healthy
    ports:
      - "127.0.0.1:8765:8765"
    environment:
      WOS_REDIS_URL: unix:///var/run/redis/redis.sock?db=0
      WOS_API_HOST: 0.0.0.0
      WOS_API_PORT: "8765"
      # Same license-file path as ``bot``. ``WOS_LICENSE`` is still honored as
      # an env override. ``WOS_ADMIN_TOKEN`` is only set in the maintainer's
      # environment — without it, the issuer endpoint 403s.
      WOS_LICENSE: ${WOS_LICENSE:-}
      WOS_ADMIN_TOKEN: ${WOS_ADMIN_TOKEN:-}
    volumes:
      - wos_state:/app/db/state
      - wos_license:/app/license-data
      - redis_socket:/var/run/redis
      - /etc/machine-id:/etc/machine-id:ro
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8765/health"]
      interval: 30s
      timeout: 5s
      start_period: 30s
      retries: 3

  web:
    image: ${WOS_REGISTRY:-ghcr.io/batazor/autopilot}/web:${WOS_IMAGE_TAG:-latest}
    depends_on:
      api:
        condition: service_healthy
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      WOS_API_URL: http://api:8765
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/overview').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
      interval: 30s
      timeout: 5s
      start_period: 45s
      retries: 3

volumes:
  redis_data:
  redis_socket:
  wos_state:
  wos_license:
