# Headscale - Self-Hosted Tailscale Control Server
# =============================================================================
# Open-source implementation of the Tailscale control server
# =============================================================================
# Deployed via: Portainer GitOps (or docker compose up -d on Calypso)
# Ports: 8080 (HTTP API), 443 (HTTPS via NPM), 9090 (Metrics)
#
# Why Calypso?
# - Already runs Authentik (SSO/OIDC provider) for seamless integration
# - Already runs Nginx Proxy Manager for external HTTPS access
# - Infrastructure services host (Gitea, NPM, Authentik)
# - Synology NAS = always-on, stable, reliable
#
# External Access:
# - NPM proxy host: headscale.vish.gg → 192.168.0.250:8085
#   WebSocket support MUST be enabled in NPM (already configured, host ID 44)
# - /admin path routed to Headplane at 192.168.0.250:3002 via NPM Advanced tab
# - OIDC auth via Authentik (provider pk=15, app slug=headscale)
#   Authentik reached via public HTTPS - no shared Docker network needed
#
# Config files:
# - Headscale: /volume1/docker/headscale/config/config.yaml on Calypso
# - Headplane: /volume1/docker/headscale/headplane/config.yaml on Calypso
# - NOT managed by inline configs block (Synology Docker Compose v2.20 doesn't support it)
#
# Architecture:
# ┌─────────────────────────────────────────────────────────────────────┐
# │                         HEADSCALE SETUP                              │
# ├─────────────────────────────────────────────────────────────────────┤
# │                                                                      │
# │  ┌─────────────┐                      ┌─────────────────────────┐  │
# │  │   Clients   │                      │        Calypso          │  │
# │  │             │                      │                         │  │
# │  │ ┌─────────┐ │     HTTPS/443        │  ┌───────────────────┐  │  │
# │  │ │Tailscale│ │─────────────────────▶│  │  Nginx Proxy Mgr  │  │  │
# │  │ │ Client  │ │   headscale.vish.gg  │  │    (SSL Term)     │  │  │
# │  │ └─────────┘ │                      │  └─────────┬─────────┘  │  │
# │  │             │                      │            │            │  │
# │  │ ┌─────────┐ │                      │            ▼            │  │
# │  │ │ Phone   │ │                      │  ┌───────────────────┐  │  │
# │  │ │  App    │ │                      │  │    Headscale      │  │  │
# │  │ └─────────┘ │                      │  │     :8080         │  │  │
# │  │             │                      │  └─────────┬─────────┘  │  │
# │  │ ┌─────────┐ │                      │            │            │  │
# │  │ │  Linux  │ │                      │            ▼            │  │
# │  │ │ Server  │ │                      │  ┌───────────────────┐  │  │
# │  │ └─────────┘ │                      │  │    Authentik      │  │  │
# │  └─────────────┘                      │  │  sso.vish.gg      │  │  │
# │                                       │  │  (OIDC via HTTPS) │  │  │
# │                                       │  └───────────────────┘  │  │
# │                                       └─────────────────────────┘  │
# └─────────────────────────────────────────────────────────────────────┘

services:
  headscale:
    image: headscale/headscale:latest
    container_name: headscale
    restart: unless-stopped
    labels:
      # Required so Headplane can locate this container via Docker socket
      me.tale.headplane.target: "headscale"
    volumes:
      # Config file at /volume1/docker/headscale/config/config.yaml
      - /volume1/docker/headscale/config:/etc/headscale
      # Persistent data: keys, SQLite database
      - headscale-data:/var/lib/headscale
      # Unix socket for headscale CLI
      - headscale-socket:/var/run/headscale
    ports:
      - "8085:8080"    # Main API - proxied via NPM to headscale.vish.gg
      - "50443:50443"  # gRPC
      - "9099:9090"    # Prometheus metrics
    command: serve
    networks:
      - headscale-net
    healthcheck:
      test: ["CMD", "headscale", "health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  headplane:
    image: ghcr.io/tale/headplane:latest
    container_name: headplane
    restart: unless-stopped
    ports:
      - "3002:3000"    # Host port 3002 (3000+3001 taken by DSM nginx)
    volumes:
      # Headplane config (secrets live on Calypso, reference copy in repo)
      - /volume1/docker/headscale/headplane/config.yaml:/etc/headplane/config.yaml
      # Persistent data: session DB, agent cache
      - headplane-data:/var/lib/headplane
      # Shared read/write access to headscale config (for Settings UI)
      - /volume1/docker/headscale/config/config.yaml:/etc/headscale/config.yaml
      # Docker socket - read-only, needed to restart headscale after config changes
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - headscale-net
    depends_on:
      - headscale
    healthcheck:
      test: ["CMD", "/bin/hp_healthcheck"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

volumes:
  headscale-data:
    name: headscale-data
  headscale-socket:
    name: headscale-socket
  headplane-data:
    name: headplane-data

networks:
  headscale-net:
    name: headscale-net
    driver: bridge
