# ==============================================
# Matrix (Synapse + Element) - Sample Swarm Deployment
# ==============================================
#
# Standalone Matrix homeserver deployment for use with Mosaic Stack.
# This is SEPARATE infrastructure — not part of the Mosaic Stack itself.
# Mosaic connects to it via MATRIX_HOMESERVER_URL environment variable.
#
# Also serves: personal communications, GoToSocial bridges, other projects.
#
# Usage (Docker Swarm via Portainer):
#   1. Create a new stack in Portainer
#   2. Paste this file or point to the repo
#   3. Set environment variables in Portainer's env var section
#   4. Deploy the stack
#
# Usage (Docker Swarm CLI):
#   1. cp docker-compose.sample.matrix.env .env
#   2. nano .env  # Configure
#   3. docker stack deploy -c docker-compose.sample.matrix.yml matrix
#
# Post-Deploy Setup:
#   1. Generate Synapse config (first run only):
#      docker exec <synapse_container> python -m synapse.app.homeserver \
#        --server-name ${MATRIX_DOMAIN} --report-stats no \
#        --generate-config --config-path /data/homeserver.yaml
#
#   2. Create admin account:
#      docker exec -it <synapse_container> register_new_matrix_user \
#        -u admin -a -c /data/homeserver.yaml http://localhost:8008
#
#   3. Create Mosaic bot account:
#      docker exec -it <synapse_container> register_new_matrix_user \
#        -u mosaic-bot -c /data/homeserver.yaml http://localhost:8008
#
#   4. Generate bot access token:
#      curl -X POST http://localhost:8008/_matrix/client/v3/login \
#        -d '{"type":"m.login.password","user":"mosaic-bot","password":"<password>"}'
#
#   5. Set MATRIX_ACCESS_TOKEN in Mosaic Stack .env
#
# Required Environment Variables:
#   MATRIX_DOMAIN=matrix.example.com        # Synapse server name (permanent!)
#   ELEMENT_DOMAIN=chat.example.com         # Element Web domain
#   POSTGRES_PASSWORD=<strong-password>     # Synapse database password
#
# Optional Environment Variables:
#   SYNAPSE_IMAGE_TAG=latest                # Synapse version
#   ELEMENT_IMAGE_TAG=latest                # Element Web version
#   POSTGRES_IMAGE_TAG=16-alpine            # PostgreSQL version
#   TRAEFIK_ENTRYPOINT=websecure            # Traefik entrypoint name
#   TRAEFIK_CERTRESOLVER=letsencrypt        # Traefik cert resolver
#   TRAEFIK_DOCKER_NETWORK=traefik-public   # Traefik network name
#   SYNAPSE_ENABLE_REGISTRATION=false       # Public registration
#   SYNAPSE_REPORT_STATS=no                 # Anonymous stats reporting
#   SYNAPSE_MAX_UPLOAD_SIZE=50M             # Max file upload size
#
# Connecting to Mosaic Stack:
#   Add to your Mosaic Stack .env:
#     MATRIX_HOMESERVER_URL=http://synapse:8008  (if same Docker network)
#     MATRIX_HOMESERVER_URL=https://matrix.example.com  (if external)
#     MATRIX_ACCESS_TOKEN=<bot access token from step 4>
#     MATRIX_BOT_USER_ID=@mosaic-bot:matrix.example.com
#
# ==============================================

services:
  # ======================
  # Synapse (Matrix Homeserver)
  # ======================
  synapse:
    image: matrixdotorg/synapse:${SYNAPSE_IMAGE_TAG:-latest}
    environment:
      SYNAPSE_SERVER_NAME: ${MATRIX_DOMAIN}
      SYNAPSE_REPORT_STATS: ${SYNAPSE_REPORT_STATS:-no}
      SYNAPSE_CONFIG_DIR: /data
      SYNAPSE_DATA_DIR: /data
      SYNAPSE_LOG_LEVEL: ${SYNAPSE_LOG_LEVEL:-WARNING}
      # Database connection (external PostgreSQL or bundled)
      POSTGRES_HOST: synapse-postgres
      POSTGRES_PORT: 5432
      POSTGRES_DB: ${SYNAPSE_POSTGRES_DB:-synapse}
      POSTGRES_USER: ${SYNAPSE_POSTGRES_USER:-synapse}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - synapse-data:/data
      - synapse-media:/data/media_store
    depends_on:
      synapse-postgres:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -fSs http://localhost:8008/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    networks:
      - internal
      - traefik-public
    deploy:
      restart_policy:
        condition: on-failure
        delay: 10s
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.matrix.rule=Host(`${MATRIX_DOMAIN}`)"
        - "traefik.http.routers.matrix.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
        - "traefik.http.routers.matrix.tls=${TRAEFIK_TLS_ENABLED:-true}"
        - "traefik.http.routers.matrix.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}"
        - "traefik.http.services.matrix.loadbalancer.server.port=8008"
        - "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-traefik-public}"
        # Well-known delegation (optional — for .well-known/matrix/server)
        # - "traefik.http.routers.matrix-wellknown.rule=Host(`${MATRIX_DOMAIN}`) && PathPrefix(`/.well-known/matrix`)"

  # ======================
  # Element Web (Matrix Client)
  # ======================
  element-web:
    image: vectorim/element-web:${ELEMENT_IMAGE_TAG:-latest}
    volumes:
      - element-config:/app/config
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:80 || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
    networks:
      - internal
      - traefik-public
    deploy:
      restart_policy:
        condition: on-failure
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.element.rule=Host(`${ELEMENT_DOMAIN}`)"
        - "traefik.http.routers.element.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
        - "traefik.http.routers.element.tls=${TRAEFIK_TLS_ENABLED:-true}"
        - "traefik.http.routers.element.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}"
        - "traefik.http.services.element.loadbalancer.server.port=80"
        - "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-traefik-public}"

  # ======================
  # PostgreSQL (Synapse Database)
  # ======================
  # Separate from Mosaic's PostgreSQL — Synapse manages its own schema.
  # If you prefer a shared PostgreSQL instance, remove this service and
  # point POSTGRES_HOST to your existing PostgreSQL with a separate database.
  synapse-postgres:
    image: postgres:${POSTGRES_IMAGE_TAG:-16-alpine}
    environment:
      POSTGRES_DB: ${SYNAPSE_POSTGRES_DB:-synapse}
      POSTGRES_USER: ${SYNAPSE_POSTGRES_USER:-synapse}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
    volumes:
      - synapse-postgres-data:/var/lib/postgresql/data
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "pg_isready -U ${SYNAPSE_POSTGRES_USER:-synapse} -d ${SYNAPSE_POSTGRES_DB:-synapse}",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - internal
    deploy:
      restart_policy:
        condition: on-failure

  # ======================
  # coturn (TURN/STUN for VoIP) - Optional
  # ======================
  # Uncomment if you need voice/video calls through NAT.
  # Requires additional DNS and port configuration.
  #
  # coturn:
  #   image: coturn/coturn:latest
  #   environment:
  #     TURN_REALM: ${MATRIX_DOMAIN}
  #     TURN_SECRET: ${COTURN_SECRET}
  #   ports:
  #     - "3478:3478/tcp"
  #     - "3478:3478/udp"
  #     - "5349:5349/tcp"
  #     - "5349:5349/udp"
  #     - "49152-49200:49152-49200/udp"
  #   networks:
  #     - internal
  #   deploy:
  #     restart_policy:
  #       condition: on-failure

volumes:
  synapse-data:
  synapse-media:
  synapse-postgres-data:
  element-config:

networks:
  internal:
    driver: overlay
  traefik-public:
    external: true
    name: ${TRAEFIK_DOCKER_NETWORK:-traefik-public}
