# Two-instance federation harness (social-federation task 11.4).
#
# Brings up two complete journal instances — journal-a.test and
# journal-b.test — that federate with each other over a private Docker
# network, with real HTTPS between them (Caddy internal CA; the journals
# trust it via NODE_EXTRA_CA_CERTS). Driven by
# apps/journal/app/lib/federation-two-instance.integration.test.ts via
# ./run.sh — see README.md.
#
# Ports published to the host (driver-facing, loopback only):
#   127.0.0.1:5499 → postgres        (schema push + seeding + asserts)
#   127.0.0.1:3401 → journal-a :3000 (plain HTTP; the TLS hop is only
#   127.0.0.1:3402 → journal-b :3000  needed for instance↔instance)

name: trails-federation-e2e
# Shared journal environment. Secrets here are throwaway test values —
# the whole stack is loopback-only and torn down by run.sh.
x-journal-env: &journal-env
  SESSION_SECRET: two-instance-test-secret
  FEDERATION_ENABLED: "true"
  FEDERATION_KEY_ENCRYPTION_KEY: two-instance-test-encryption-key
  FEDERATION_LOG_LEVEL: debug
  LOG_LEVEL: debug
  # Both instances live on RFC 1918 Docker addresses; Fedify's SSRF
  # guard must be told to stand down. Testing only — see the comment in
  # apps/journal/app/lib/federation.server.ts.
  FEDERATION_ALLOW_PRIVATE_ADDRESS: "true"
  # Trust Caddy's internal CA for the instance↔instance HTTPS hop.
  # Published world-readable by the ca-publisher one-shot — the journal
  # runs as a non-root user and can't read caddy's 0700 data volume.
  NODE_EXTRA_CA_CERTS: /ca/root.crt
  SENTRY_DISABLED: "true"


services:
  postgres:
    image: imresamu/postgis:16-3.4 # multi-arch, same as docker-compose.dev.yml
    environment:
      POSTGRES_USER: trails
      POSTGRES_PASSWORD: trails
      POSTGRES_DB: trails_a
    volumes:
      - ./init-dbs.sql:/docker-entrypoint-initdb.d/90-init-dbs.sql:ro
    ports:
      - "127.0.0.1:5499:5432"
    healthcheck:
      # -h 127.0.0.1 forces TCP: during first-boot initdb a temporary
      # postgres answers on the unix socket only, and a socket-based
      # pg_isready reports healthy before the init scripts have run.
      test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U trails -d trails_b"]
      interval: 2s
      timeout: 3s
      retries: 30

  caddy:
    image: caddy:2-alpine
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
    networks:
      default:
        # The instances reach each other through Caddy: these aliases
        # make journal-a.test / journal-b.test resolve to the proxy
        # inside the compose network.
        aliases:
          - journal-a.test
          - journal-b.test
    healthcheck:
      # The journals read the internal CA root at process start
      # (NODE_EXTRA_CA_CERTS) — gate their startup on it existing.
      test: ["CMD", "test", "-f", "/data/caddy/pki/authorities/local/root.crt"]
      interval: 2s
      timeout: 3s
      retries: 30

  # One-shot: copies Caddy's internal root CA into a volume the
  # non-root journal user can actually read (caddy's own data dir is
  # 0700 root).
  ca-publisher:
    image: caddy:2-alpine
    entrypoint:
      - sh
      - -c
      - |
        until [ -f /data/caddy/pki/authorities/local/root.crt ]; do sleep 0.5; done
        cp /data/caddy/pki/authorities/local/root.crt /ca/root.crt
        chmod 444 /ca/root.crt
    volumes:
      - caddy-data:/data:ro
      - ca-cert:/ca
    depends_on:
      caddy:
        condition: service_healthy

  journal-a:
    build:
      context: ../..
      dockerfile: apps/journal/Dockerfile
    image: trails-federation-e2e-journal
    pull_policy: never
    environment:
      <<: *journal-env
      DATABASE_URL: postgres://trails:trails@postgres:5432/trails_a
      ORIGIN: https://journal-a.test
    ports:
      - "127.0.0.1:3401:3000"
    volumes:
      - ca-cert:/ca:ro
    healthcheck: &journal-health
      test: ["CMD", "curl", "-fsS", "http://localhost:3000/api/health"]
      interval: 3s
      timeout: 5s
      retries: 40
      start_period: 10s
    depends_on:
      postgres:
        condition: service_healthy
      ca-publisher:
        condition: service_completed_successfully

  journal-b:
    # Image-only on purpose: run.sh builds it once via journal-a before
    # `up`. Building from both services races the export of the shared
    # tag ("already exists"), and a registry pull must never happen.
    image: trails-federation-e2e-journal
    pull_policy: never
    environment:
      <<: *journal-env
      DATABASE_URL: postgres://trails:trails@postgres:5432/trails_b
      ORIGIN: https://journal-b.test
    ports:
      - "127.0.0.1:3402:3000"
    volumes:
      - ca-cert:/ca:ro
    healthcheck: *journal-health
    depends_on:
      postgres:
        condition: service_healthy
      ca-publisher:
        condition: service_completed_successfully

volumes:
  caddy-data:
  ca-cert:

