# Coves Local Development Stack # All-in-one setup: PDS + PostgreSQL + optional Relay # # Usage: # make dev-up # Start PDS + PostgreSQL # make dev-down # Stop everything # docker-compose up relay # Optional: start with relay # # Profiles: # - default: PDS + PostgreSQL (dev database on port 5433) # - test: PostgreSQL test database (port 5434) # - relay: BigSky relay (optional, will crawl entire network!) services: # PostgreSQL Database (Port 5435) # Used by Coves AppView for indexing data from firehose postgres: image: postgres:15 container_name: coves-dev-postgres ports: - "5435:5432" environment: POSTGRES_DB: ${POSTGRES_DB:-coves_dev} POSTGRES_USER: ${POSTGRES_USER:-dev_user} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-dev_password} volumes: - postgres-data:/var/lib/postgresql/data networks: - coves-dev healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-dev_user} -d ${POSTGRES_DB:-coves_dev}"] interval: 5s timeout: 5s retries: 5 # PostgreSQL Test Database (Port 5434) - Optional # Use with: docker-compose --profile test up postgres-test postgres-test: image: postgres:15 container_name: coves-test-postgres ports: - "${POSTGRES_TEST_PORT:-5434}:5432" environment: POSTGRES_DB: ${POSTGRES_TEST_DB:-coves_test} POSTGRES_USER: ${POSTGRES_TEST_USER:-test_user} POSTGRES_PASSWORD: ${POSTGRES_TEST_PASSWORD:-test_password} volumes: - postgres-test-data:/var/lib/postgresql/data networks: - coves-dev healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_TEST_USER:-test_user} -d ${POSTGRES_TEST_DB:-coves_test}"] interval: 5s timeout: 5s retries: 5 profiles: - test # Bluesky Personal Data Server (PDS) # Handles user repositories, DIDs, and CAR files # NOTE: When using --profile plc, PDS waits for PLC directory to be healthy pds: image: ghcr.io/bluesky-social/pds:latest container_name: coves-dev-pds ports: - "3001:3001" # PDS XRPC API (avoiding production PDS on :3000) environment: # PDS Configuration PDS_HOSTNAME: ${PDS_HOSTNAME:-localhost} PDS_PORT: 3001 # Match external port for correct DID registration PDS_DATA_DIRECTORY: /pds PDS_BLOBSTORE_DISK_LOCATION: /pds/blocks # IMPORTANT: For local E2E testing, this MUST point to local PLC directory # Default to local PLC (http://plc-directory:3000) for full local stack # The container hostname 'plc-directory' is used for Docker network communication PDS_DID_PLC_URL: ${PDS_DID_PLC_URL:-http://plc-directory:3000} # PDS_CRAWLERS not needed - we're not using a relay for local dev # Note: PDS uses its own internal SQLite database and CAR file storage # Our PostgreSQL database is only for the Coves AppView # JWT secrets (for local dev only) PDS_JWT_SECRET: ${PDS_JWT_SECRET:-local-dev-jwt-secret-change-in-production} PDS_ADMIN_PASSWORD: ${PDS_ADMIN_PASSWORD:-admin} PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ${PDS_PLC_ROTATION_KEY:-af514fb84c4356241deed29feb392d1ee359f99c05a7b8f7bff2e5f2614f64b2} # Service endpoints # Allow both user handles (.local.coves.dev) and community handles (.community.coves.social) PDS_SERVICE_HANDLE_DOMAINS: ${PDS_SERVICE_HANDLE_DOMAINS:-.local.coves.dev,.community.coves.social} # Dev mode settings (allows HTTP instead of HTTPS) PDS_DEV_MODE: "true" # Disable invite codes for testing PDS_INVITE_REQUIRED: "false" # Development settings NODE_ENV: development LOG_ENABLED: "true" LOG_LEVEL: ${LOG_LEVEL:-debug} volumes: - pds-data:/pds networks: - coves-dev healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3001/xrpc/_health"] interval: 10s timeout: 5s retries: 5 # Jetstream - Consumes PDS firehose and serves JSON WebSocket # This is the RECOMMENDED approach for local E2E testing # Jetstream converts raw atProto CBOR firehose to clean JSON events # # Flow: PDS firehose → Jetstream (CBOR→JSON) → Your AppView (JSON) # # Usage: # docker-compose --profile jetstream up pds jetstream # Your AppView connects to: ws://localhost:6008/subscribe # # Why use Jetstream instead of direct PDS firehose? # - PDS emits raw CBOR (binary) - hard to parse # - Jetstream converts to clean JSON - easy to consume # - Same format as production Bluesky Jetstream jetstream: image: ghcr.io/bluesky-social/jetstream:sha-306e463693365e21a5ffd3ec051a5a7920000214 container_name: coves-dev-jetstream ports: - "6008:6008" # Jetstream WebSocket endpoint - "6009:6009" # Metrics endpoint environment: # Point Jetstream at local PDS firehose JETSTREAM_WS_URL: ws://pds:3001/xrpc/com.atproto.sync.subscribeRepos # Server configuration JETSTREAM_LISTEN_ADDR: ":6008" JETSTREAM_METRICS_LISTEN_ADDR: ":6009" # Data storage JETSTREAM_DATA_DIR: /data JETSTREAM_EVENT_TTL: 24h # Set long liveness TTL for local dev (PDS may be quiet for long periods) JETSTREAM_LIVENESS_TTL: 24h # Performance tuning JETSTREAM_WORKER_COUNT: 10 JETSTREAM_MAX_QUEUE_SIZE: 1000 # Development settings LOG_LEVEL: ${LOG_LEVEL:-debug} volumes: - jetstream-data:/data networks: - coves-dev depends_on: pds: condition: service_healthy # Health check disabled for dev - container has no HTTP clients installed # Jetstream logs show it connects and runs successfully healthcheck: disable: true profiles: - jetstream # PostgreSQL Database for PLC Directory (Port 5436) # Separate database for local PLC directory to avoid conflicts postgres-plc: image: postgres:15 container_name: coves-dev-postgres-plc ports: - "5436:5432" environment: POSTGRES_DB: plc_dev POSTGRES_USER: plc_user POSTGRES_PASSWORD: plc_password volumes: - postgres-plc-data:/var/lib/postgresql/data networks: - coves-dev healthcheck: test: ["CMD-SHELL", "pg_isready -U plc_user -d plc_dev"] interval: 5s timeout: 5s retries: 5 profiles: - plc # Local PLC Directory - For E2E testing without polluting production plc.directory # This allows dev mode DID registration for testing community provisioning # # Usage: # docker-compose --profile plc up postgres-plc plc-directory # Or with all services: docker-compose --profile jetstream --profile plc up # # Configuration in your tests: # PLC_DIRECTORY_URL=http://localhost:3002 # IS_DEV_ENV=false # Use production mode but point to local PLC plc-directory: image: node:18-alpine container_name: coves-dev-plc ports: - "3002:3000" # PLC directory API working_dir: /app command: > sh -c " if [ ! -d '/app/.git' ]; then echo 'First run: Installing PLC directory...' && apk add --no-cache git python3 make g++ yarn && git clone https://github.com/did-method-plc/did-method-plc.git . && yarn install --frozen-lockfile && yarn build && echo 'PLC directory installed successfully!' fi && cd packages/server && yarn start " environment: # Point to dedicated PLC PostgreSQL database DATABASE_URL: postgresql://plc_user:plc_password@postgres-plc:5432/plc_dev?sslmode=disable # Development settings DEBUG_MODE: "1" LOG_ENABLED: "true" LOG_LEVEL: debug LOG_DESTINATION: "1" NODE_ENV: development # API configuration PORT: 3000 volumes: # Persist the PLC repo so we don't rebuild every time - plc-app-data:/app networks: - coves-dev depends_on: postgres-plc: condition: service_healthy healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/"] interval: 10s timeout: 5s retries: 10 start_period: 120s profiles: - plc # Indigo Relay (BigSky) - OPTIONAL for local dev # WARNING: BigSky is designed to crawl the entire atProto network! # For local dev, consider using direct PDS firehose instead (see AppView config below) # # To use relay: docker-compose -f docker-compose.dev.yml up pds relay # To skip relay: docker-compose -f docker-compose.dev.yml up pds # # If using relay, you MUST manually configure it to only watch local PDS: # 1. Start relay # 2. Use admin API to block all domains except localhost # curl -X POST http://localhost:2471/admin/pds/requestCrawl \ # -H "Authorization: Bearer dev-admin-key" \ # -d '{"hostname": "localhost:3001"}' relay: image: ghcr.io/bluesky-social/indigo:bigsky-0a2d4173e6e89e49b448f6bb0a6e1ab58d12b385 container_name: coves-dev-relay ports: - "2471:2470" # Relay firehose WebSocket (avoiding conflicts) environment: # Relay Configuration BGS_ADMIN_KEY: ${BGS_ADMIN_KEY:-dev-admin-key} BGS_PORT: 2470 # IMPORTANT: Allow insecure WebSocket for local PDS (ws:// instead of wss://) BGS_CRAWL_INSECURE_WS: "true" # Database connection (uses PostgreSQL for relay state) DATABASE_URL: postgresql://${POSTGRES_USER:-dev_user}:${POSTGRES_PASSWORD:-dev_password}@postgres:5432/${POSTGRES_DB:-coves_dev}?sslmode=disable # Relay will discover PDSs automatically - use admin API to restrict! # See comments above for how to configure allowlist # Development settings LOG_LEVEL: ${LOG_LEVEL:-debug} networks: - coves-dev depends_on: postgres: condition: service_healthy pds: condition: service_healthy healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:2470/xrpc/_health"] interval: 10s timeout: 5s retries: 5 # Mark as optional - start with: docker-compose up pds relay profiles: - relay # Coves AppView (Your Go Application) # Subscribes to PDS firehose and indexes Coves-specific data # Note: Uncomment when you have a Dockerfile for the AppView # appview: # build: # context: . # dockerfile: Dockerfile # container_name: coves-dev-appview # ports: # - "8081:8080" # AppView API (avoiding conflicts) # environment: # # Database connection # DATABASE_URL: postgresql://${POSTGRES_USER:-dev_user}:${POSTGRES_PASSWORD:-dev_password}@postgres:5432/${POSTGRES_DB:-coves_dev}?sslmode=disable # # # PDS Firehose subscription (direct, no relay) # FIREHOSE_URL: ws://pds:3000/xrpc/com.atproto.sync.subscribeRepos # # # PDS connection (for XRPC calls) # PDS_URL: http://pds:3000 # # # Application settings # PORT: 8080 # ENV: development # LOG_LEVEL: ${LOG_LEVEL:-debug} # networks: # - coves-dev # depends_on: # postgres: # condition: service_healthy # pds: # condition: service_healthy networks: coves-dev: driver: bridge name: coves-dev-network volumes: postgres-data: name: coves-dev-postgres-data postgres-test-data: name: coves-test-postgres-data postgres-plc-data: name: coves-dev-postgres-plc-data pds-data: name: coves-dev-pds-data jetstream-data: name: coves-dev-jetstream-data plc-app-data: name: coves-dev-plc-app-data