# Self-hosted LiveSync — Docker Compose
# =============================================================================
#  PROFILES
#  --------
#  (default)              CouchDB only — LAN/localhost access, no TLS
#                         Suitable for desktop-only use or testing.
#
#  --profile caddy        CouchDB + Caddy reverse proxy
#                         Auto TLS via Let's Encrypt. Needs public domain + ports 80/443.
#
#  --profile tailscale    CouchDB + Tailscale sidecar
#                         No domain required. HTTPS via *.ts.net PKI.
#                         Needs a Tailscale account (free tier works).
#
#  --profile cloudflare   CouchDB + cloudflared tunnel daemon
#                         Free public HTTPS via Cloudflare. Needs a CF account + tunnel token.
#                         NOTE: Enable "Use Request API" in the Obsidian plugin to avoid 524 timeouts.
#
#  QUICK START (local test):
#    cp .env.example .env && edit .env
#    docker compose up -d
#    curl -u admin:yourpassword http://localhost:5984/_up
# =============================================================================

name: obsidian-livesync

services:

  # ---------------------------------------------------------------------------
  # CouchDB — the only required service
  # ---------------------------------------------------------------------------
  couchdb:
    image: couchdb:latest
    container_name: livesync-couchdb
    restart: unless-stopped
    # NOTE: Do NOT set user: here — the CouchDB entrypoint starts as root to
    # write docker.ini (from env vars), then drops to uid 5984 automatically.
    environment:
      COUCHDB_USER: ${COUCHDB_USER:?Set COUCHDB_USER in .env}
      COUCHDB_PASSWORD: ${COUCHDB_PASSWORD:?Set COUCHDB_PASSWORD in .env}
    volumes:
      - couchdb-data:/opt/couchdb/data
      # Mount to /opt/couchdb/etc/local.ini (NOT into local.d/).
      # Do NOT use :ro — the CouchDB entrypoint runs chmod on this file at startup
      # and will crash with EPERM if the file is read-only. The file is only read
      # at startup; runtime changes go via the REST API into local.d/docker.ini.
      - ./config/livesync.ini:/opt/couchdb/etc/local.ini
    ports:
      # Exposes CouchDB on the host for LAN/localhost access.
      # The tunnel profiles (caddy/tailscale/cloudflare) provide HTTPS on top.
      # You can remove this port mapping once a tunnel profile is in use.
      - "${COUCHDB_PORT:-5984}:5984"
    healthcheck:
      # Test with admin credentials — ensures both CouchDB is up AND auth is ready.
      # ${COUCHDB_USER} / ${COUCHDB_PASSWORD} are expanded by Docker Compose here.
      test:
        - "CMD-SHELL"
        - "curl -sf -u ${COUCHDB_USER}:${COUCHDB_PASSWORD} http://localhost:5984/_session | grep -q ok || exit 1"
      interval: 5s
      timeout: 5s
      retries: 24
      start_period: 20s
    networks:
      - livesync-net

  # ---------------------------------------------------------------------------
  # One-shot init container — runs couchdb-init.sh after CouchDB is healthy.
  # Sets single-node cluster, auth requirements, CORS, size limits, creates DB.
  # Restarts on failure (e.g. race at first boot) but won't re-run if already done.
  # ---------------------------------------------------------------------------
  couchdb-init:
    image: curlimages/curl:latest
    container_name: livesync-init
    restart: on-failure
    depends_on:
      couchdb:
        condition: service_healthy
    environment:
      COUCHDB_INTERNAL_URL: http://couchdb:5984
      COUCHDB_USER: ${COUCHDB_USER}
      COUCHDB_PASSWORD: ${COUCHDB_PASSWORD}
      COUCHDB_DATABASE: ${COUCHDB_DATABASE:-obsidiannotes}
    volumes:
      - ./scripts/couchdb-init.sh:/couchdb-init.sh:ro
    entrypoint: ["sh", "/couchdb-init.sh"]
    networks:
      - livesync-net

  # ---------------------------------------------------------------------------
  # PROFILE: caddy — Caddy reverse proxy with automatic Let's Encrypt TLS
  # Requirements: public domain, ports 80 + 443 open to internet
  # Usage: docker compose --profile caddy up -d
  # ---------------------------------------------------------------------------
  caddy:
    image: caddy:latest
    container_name: livesync-caddy
    profiles: [caddy]
    restart: unless-stopped
    depends_on:
      couchdb:
        condition: service_healthy
    environment:
      COUCHDB_DOMAIN: ${COUCHDB_DOMAIN:?Set COUCHDB_DOMAIN in .env for caddy profile}
      ACME_EMAIL: ${ACME_EMAIL:?Set ACME_EMAIL in .env for caddy profile}
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./config/Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
      - caddy-config:/config
    networks:
      - livesync-net

  # ---------------------------------------------------------------------------
  # PROFILE: tailscale — Tailscale sidecar for mesh VPN + optional Funnel
  # Requirements: Tailscale account (free), OAuth key, Funnel enabled in ACL
  # Usage: docker compose --profile tailscale up -d
  # The CouchDB port mapping above can be removed for tailscale-only deployments.
  # ---------------------------------------------------------------------------
  tailscale:
    image: tailscale/tailscale:latest
    container_name: livesync-tailscale
    profiles: [tailscale]
    restart: unless-stopped
    hostname: ${TS_HOSTNAME:-livesync}
    environment:
      TS_AUTHKEY: ${TS_AUTHKEY:?Set TS_AUTHKEY in .env for tailscale profile}
      TS_STATE_DIR: /var/lib/tailscale
      TS_SERVE_CONFIG: /config/serve.json
      TS_USERSPACE: "false"
      TS_ACCEPT_DNS: "false"
      TS_EXTRA_ARGS: ""
    volumes:
      - tailscale-state:/var/lib/tailscale
      - ./config/ts-serve.json:/config/serve.json:ro
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    # Share CouchDB's network namespace so Tailscale can reach it on localhost
    network_mode: service:couchdb
    depends_on:
      - couchdb

  # ---------------------------------------------------------------------------
  # PROFILE: cloudflare — cloudflared tunnel daemon
  # Requirements: Cloudflare account, tunnel token from CF Zero Trust dashboard
  # Usage: docker compose --profile cloudflare up -d
  # NOTE: Enable "Use Request API" toggle in the Obsidian LiveSync plugin settings
  #       to avoid Cloudflare's 100-second proxy timeout (524 errors).
  # ---------------------------------------------------------------------------
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: livesync-cloudflared
    profiles: [cloudflare]
    restart: unless-stopped
    command: tunnel --no-autoupdate run
    environment:
      TUNNEL_TOKEN: ${CF_TUNNEL_TOKEN:?Set CF_TUNNEL_TOKEN in .env for cloudflare profile}
    volumes:
      - ./config/cloudflared.yml:/etc/cloudflared/config.yml:ro
    depends_on:
      couchdb:
        condition: service_healthy
    networks:
      - livesync-net

# =============================================================================
# Volumes
# =============================================================================
volumes:
  couchdb-data:
    driver: local
  caddy-data:
    driver: local
  caddy-config:
    driver: local
  tailscale-state:
    driver: local

# =============================================================================
# Networks
# =============================================================================
networks:
  livesync-net:
    driver: bridge
