# deploy/compose.yaml — T2 modular reference deployment (spec §2.2 / ADR-0011).
#
# Four services: caddy (rutster-edge), engine (rutster-engine), brain
# (rutster-brain, network_mode: "service:engine" so the loopback tap posture
# survives unchanged — resolve_tap_url rejects non-loopback until step 6),
# valkey (upstream valkey/valkey).
#
# stop_grace_period 660s paired with RUTSTER_DRAIN_DEADLINE_SECS=600 —
# grace EXCEEDS drain or the orchestrator kills calls the engine was
# gracefully draining (spec §2.2 / §8). 60s slack accounts for a
# legitimately-long in-flight call (599s) + shutdown round-trip.
#
# Bring your own proxy: comment out the `caddy` service; the engine keeps
# its plaintext :8080 listener; tuned-timeout configs for nginx/HAProxy/
# Traefik ship in docs/deploy/reverse-proxies.md (slice G).
#
# Bring-up:
#   cp .env.example .env
#   $EDITOR .env     # set DOMAIN, ACME_EMAIL, MEDIA_ADVERTISED_IP, TWILIO_*
#   docker compose up -d
#   curl -fsS https://$RUTSTER_DOMAIN/healthz && echo OK

services:
  caddy:
    # The custom xcaddy build with curated DNS plugins (cloudflare/
    # route53/porkbun/hetzner/desec — duckdns excluded per spec §3.2).
    # In production, pulls from the git.adlee.work/alee/rutster-edge
    # registry (slice F). For local dev / CI without registry auth, build
    # from the Dockerfile:
    #   build:
    #     context: ..
    #     dockerfile: deploy/Dockerfile
    #     target: rutster-edge
    image: rutster-edge:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - caddy-data:/data
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
    env_file:
      - .env
    depends_on:
      - engine
    networks:
      - rutster-net

  engine:
    # The FOB: axum signaling + media thread + trunk WS + /metrics.
    # Plaintext :8080 behind Caddy (slice C's rustls will terminate
    # in-process TLS — same listener, operator-choice).
    image: rutster-engine:latest
    restart: unless-stopped
    # 660s grace > 600s drain (RUTSTER_DRAIN_DEADLINE_SECS) — invariant.
    stop_grace_period: 660s
    ports:
      # Signaling + trunk WS behind Caddy (Caddy proxies :443 -> engine:8080).
      # Comment out for production hardening (Caddy is the only entry).
      - "8080:8080"
      # WebRTC media UDP direct — never proxied (spec §2.1).
      - "49152-49407:49152-49407/udp"
    volumes:
      - ./Caddyfile:/etc/rutster/Caddyfile:ro   # copied in image already; mount for edit-without-rebuild
    env_file:
      - .env
    networks:
      - rutster-net

  brain:
    # Brain shares engine's network namespace — the FOB reaches the brain's
    # tap port via 127.0.0.1:8082 (loopback-only posture; resolve_tap_url
    # rejects non-loopback URLs until step 6 per spec §2.2). The brain
    # graduates to its own netns + wss:// tap auth with step 6.
    image: rutster-brain:latest
    restart: unless-stopped
    # CRITICAL: quoted — YAML would otherwise parse `service:engine` as a
    # `service: engine` mapping (key:value). The quotes make it a string.
    network_mode: "service:engine"
    env_file:
      - .env
    depends_on:
      - engine

  valkey:
    # Upstream valkey image (BSD-3; aggregation-clean vs GPL-3 per ADR-0004).
    # Dark on day one except EventSink (slice E lands ValkeyEventSink
    # against this service). The operator contract (volumes, ports, upgrade
    # shape) is stable from v1 per spec §2.1 — the spend ledger (ADR-0009)
    # + fleet directory land later without changing this service.
    image: valkey/valkey:8.0
    restart: unless-stopped
    command: ["valkey-server", "--dir", "/data", "--save", "60", "1", "--appendonly", "yes"]
    volumes:
      - valkey-data:/data
    networks:
      - rutster-net

volumes:
  # /data — Caddy's cert/ACME state. Loss risks the Let's Encrypt
  # duplicate-cert lockout (5/week — total inbound outage class).
  # See docs/deploy/certificates.md (slice G) + TLS brief §5 risk 5.
  caddy-data:
  # /var/lib/valkey — stream/state persistence (ADR-0005).
  valkey-data:

networks:
  rutster-net:
    driver: bridge
