A community based topic aggregation platform built on atproto
1# Coves Production Stack 2# 3# Architecture: 4# - coves.social: AppView domain (API, frontend, .well-known/did.json) 5# - pds.coves.me: PDS domain (canonical hostname for relay registration) 6# - coves.me: PDS domain (legacy, kept for compatibility) 7# 8# Hardware: AMD Epyc 7351p (16c/32t), 256GB RAM, 2x500GB NVMe RAID 9# 10# Usage: 11# docker-compose -f docker-compose.prod.yml up -d 12# 13# Prerequisites: 14# 1. DNS configured for both domains 15# 2. SSL certificates (Caddy handles this automatically) 16# 3. .env.prod file with secrets 17# 4. .well-known/did.json deployed to coves.social 18 19services: 20 # PostgreSQL Database for AppView 21 postgres: 22 image: postgres:15 23 container_name: coves-prod-postgres 24 restart: unless-stopped 25 environment: 26 POSTGRES_DB: ${POSTGRES_DB} 27 POSTGRES_USER: ${POSTGRES_USER} 28 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 29 volumes: 30 - postgres-data:/var/lib/postgresql/data 31 # Mount backup directory for pg_dump 32 - ./backups:/backups 33 networks: 34 - coves-internal 35 healthcheck: 36 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] 37 interval: 10s 38 timeout: 5s 39 retries: 5 40 # Generous limits for 256GB server 41 deploy: 42 resources: 43 limits: 44 memory: 32G 45 reservations: 46 memory: 4G 47 48 # Coves AppView (Go Server) 49 appview: 50 build: 51 context: . 52 dockerfile: Dockerfile 53 image: coves/appview:${VERSION:-latest} 54 container_name: coves-prod-appview 55 restart: unless-stopped 56 ports: 57 - "127.0.0.1:8080:8080" # Only expose to localhost (Caddy proxies) 58 environment: 59 # Database 60 DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable 61 62 # Instance identity 63 INSTANCE_DID: did:web:coves.social 64 INSTANCE_DOMAIN: coves.social 65 66 # PDS connection (separate domain!) 67 PDS_URL: https://coves.me 68 69 # Jetstream (Bluesky production firehose) 70 JETSTREAM_URL: wss://jetstream2.us-east.bsky.network/subscribe 71 72 # Custom lexicon consumers (use production Jetstream with collection filters) 73 COMMUNITY_JETSTREAM_URL: wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=social.coves.community.profile&wantedCollections=social.coves.community.subscription 74 POST_JETSTREAM_URL: wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=social.coves.community.post 75 AGGREGATOR_JETSTREAM_URL: wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=social.coves.aggregator.service&wantedCollections=social.coves.aggregator.authorization 76 VOTE_JETSTREAM_URL: wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=social.coves.feed.vote 77 COMMENT_JETSTREAM_URL: wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=social.coves.community.comment 78 79 # Security - MUST be false in production 80 AUTH_SKIP_VERIFY: "false" 81 SKIP_DID_WEB_VERIFICATION: "false" 82 83 # OAuth (for community account provisioning) 84 OAUTH_CLIENT_ID: ${OAUTH_CLIENT_ID} 85 OAUTH_REDIRECT_URI: ${OAUTH_REDIRECT_URI} 86 OAUTH_PRIVATE_JWK: ${OAUTH_PRIVATE_JWK} 87 88 # Application settings 89 PORT: 8080 90 ENV: production 91 LOG_LEVEL: info 92 93 # Encryption key for community credentials 94 ENCRYPTION_KEY: ${ENCRYPTION_KEY} 95 96 # Cursor encryption for pagination 97 CURSOR_SECRET: ${CURSOR_SECRET} 98 networks: 99 - coves-internal 100 depends_on: 101 postgres: 102 condition: service_healthy 103 healthcheck: 104 test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/xrpc/_health"] 105 interval: 30s 106 timeout: 5s 107 retries: 3 108 start_period: 10s 109 # Go is memory-efficient, but give it room for connection pools 110 deploy: 111 resources: 112 limits: 113 memory: 8G 114 reservations: 115 memory: 512M 116 117 # Bluesky PDS (Personal Data Server) 118 # Handles community accounts and their repositories 119 pds: 120 image: ghcr.io/bluesky-social/pds:latest 121 container_name: coves-prod-pds 122 restart: unless-stopped 123 ports: 124 - "127.0.0.1:3000:3000" # Only expose to localhost (Caddy proxies) 125 environment: 126 # PDS identity (use pds.coves.me for fresh relay registration) 127 PDS_HOSTNAME: pds.coves.me 128 PDS_PORT: 3000 129 PDS_DATA_DIRECTORY: /pds 130 PDS_BLOBSTORE_DISK_LOCATION: /pds/blocks 131 PDS_BLOB_UPLOAD_LIMIT: 104857600 # 100 MB 132 133 # PLC Directory (production) 134 PDS_DID_PLC_URL: https://plc.directory 135 136 # Handle domains 137 # Community handles use @community.coves.social (AppView domain) 138 # Note: Root domain (coves.social) handle works via .well-known resolution 139 PDS_SERVICE_HANDLE_DOMAINS: .coves.social 140 141 # Security (set real values in .env.prod) 142 PDS_JWT_SECRET: ${PDS_JWT_SECRET} 143 PDS_ADMIN_PASSWORD: ${PDS_ADMIN_PASSWORD} 144 PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ${PDS_ROTATION_KEY} 145 146 # Email (optional, for account recovery) 147 # NOTE: Must set BOTH or NEITHER - PDS fails with partial config 148 # PDS_EMAIL_SMTP_URL: ${PDS_EMAIL_SMTP_URL} 149 # PDS_EMAIL_FROM_ADDRESS: ${PDS_EMAIL_FROM_ADDRESS} 150 151 # Production mode 152 PDS_DEV_MODE: "false" 153 PDS_INVITE_REQUIRED: "false" # Set to true if you want invite-only 154 155 # Logging 156 NODE_ENV: production 157 LOG_ENABLED: "true" 158 LOG_LEVEL: info 159 160 # AppView proxy (for app.bsky.* methods like getProfile, notifications, etc.) 161 PDS_BSKY_APP_VIEW_URL: https://api.bsky.app 162 PDS_BSKY_APP_VIEW_DID: did:web:api.bsky.app 163 164 # Report service (for reporting content) 165 PDS_REPORT_SERVICE_URL: https://mod.bsky.app 166 PDS_REPORT_SERVICE_DID: did:plc:ar7c4by46qjdydhdevvrndac 167 168 # Relay crawlers (for federation with Bluesky network) 169 PDS_CRAWLERS: https://bsky.network,https://relay1.us-east.bsky.network,https://relay1.us-west.bsky.network,https://relay.fire.hose.cam,https://relay.upcloud.world 170 volumes: 171 - pds-data:/pds 172 networks: 173 - coves-internal 174 healthcheck: 175 test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/xrpc/_health"] 176 interval: 30s 177 timeout: 5s 178 retries: 5 179 # PDS (Node.js) needs memory for blob handling 180 deploy: 181 resources: 182 limits: 183 memory: 16G 184 reservations: 185 memory: 1G 186 187 # Caddy Reverse Proxy 188 # Handles HTTPS automatically via Let's Encrypt 189 # Uses Cloudflare plugin for wildcard SSL certificates (*.coves.social) 190 caddy: 191 # Pre-built Caddy with Cloudflare DNS plugin 192 # Updates automatically with docker-compose pull 193 # Alternative: build your own with Dockerfile.caddy 194 image: ghcr.io/slothcroissant/caddy-cloudflaredns:latest 195 container_name: coves-prod-caddy 196 restart: unless-stopped 197 ports: 198 - "80:80" 199 - "443:443" 200 environment: 201 # Required for wildcard SSL via DNS challenge 202 # Create at: Cloudflare Dashboard → My Profile → API Tokens → Create Token 203 # Permissions: Zone:DNS:Edit for coves.social zone 204 CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN} 205 volumes: 206 - ./Caddyfile:/etc/caddy/Caddyfile:ro 207 - caddy-data:/data 208 - caddy-config:/config 209 # Static files (.well-known, client-metadata.json, oauth callback) 210 - ./static:/srv:ro 211 networks: 212 - coves-internal 213 depends_on: 214 - appview 215 - pds 216 217networks: 218 coves-internal: 219 driver: bridge 220 name: coves-prod-network 221 222volumes: 223 postgres-data: 224 name: coves-prod-postgres-data 225 pds-data: 226 name: coves-prod-pds-data 227 caddy-data: 228 name: coves-prod-caddy-data 229 caddy-config: 230 name: coves-prod-caddy-config