A community based topic aggregation platform built on atproto
1# Coves Production Caddyfile 2# Handles HTTPS for both coves.social (AppView) and coves.me (PDS) 3# 4# Domain architecture: 5# - coves.social: AppView (API, web app) 6# - *.coves.social: Community handles (route atproto-did to PDS) 7# - pds.coves.me: PDS canonical hostname (for relay registration) 8# - coves.me: PDS legacy hostname (kept for compatibility) 9 10# Community handle subdomains (e.g., gaming.coves.social) 11# These need to route /.well-known/atproto-did to PDS for handle resolution 12# 13# NOTE: Wildcard certs require DNS challenge. For Cloudflare: 14# 1. Create API token with Zone:DNS:Edit permissions 15# 2. Set CLOUDFLARE_API_TOKEN environment variable 16# 3. Use caddy-dns/cloudflare plugin (see docker-compose.prod.yml) 17*.coves.social { 18 tls { 19 dns cloudflare {env.CLOUDFLARE_API_TOKEN} 20 } 21 # Handle resolution - proxy to PDS 22 handle /.well-known/atproto-did { 23 reverse_proxy pds:3000 24 } 25 26 # OAuth well-known endpoints - proxy to PDS 27 handle /.well-known/oauth-protected-resource { 28 reverse_proxy pds:3000 29 } 30 31 handle /.well-known/oauth-authorization-server { 32 reverse_proxy pds:3000 33 } 34 35 # All other requests return 404 (subdomains only exist for handle resolution) 36 handle { 37 respond "Not Found" 404 38 } 39 40 # Security headers 41 header { 42 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" 43 X-Content-Type-Options "nosniff" 44 -Server 45 } 46} 47 48# AppView Domain (root) 49coves.social { 50 # Serve .well-known files for DID verification 51 handle /.well-known/* { 52 header Access-Control-Allow-Origin "*" 53 root * /srv 54 file_server 55 } 56 57 # Serve OAuth client metadata 58 handle /client-metadata.json { 59 root * /srv 60 file_server 61 } 62 63 # Serve OAuth callback page 64 handle /oauth/callback { 65 root * /srv 66 rewrite * /oauth/callback.html 67 file_server 68 } 69 70 # Proxy all other requests to AppView 71 handle { 72 reverse_proxy appview:8080 { 73 # Health check 74 health_uri /xrpc/_health 75 health_interval 30s 76 health_timeout 5s 77 78 # Headers 79 header_up X-Real-IP {remote_host} 80 header_up X-Forwarded-For {remote_host} 81 header_up X-Forwarded-Proto {scheme} 82 } 83 } 84 85 # Logging (Docker captures stdout/stderr) 86 log { 87 output stdout 88 format json 89 } 90 91 # Security headers 92 header { 93 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" 94 X-Content-Type-Options "nosniff" 95 X-Frame-Options "DENY" 96 Referrer-Policy "strict-origin-when-cross-origin" 97 Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://*.bsky.network wss://*.bsky.network" 98 # Remove Server header 99 -Server 100 } 101 102 # Enable compression 103 encode gzip zstd 104} 105 106# PDS Domain (both hostnames point to same PDS) 107# pds.coves.me is the canonical hostname for relay registration 108pds.coves.me, coves.me { 109 reverse_proxy pds:3000 { 110 # Health check 111 health_uri /xrpc/_health 112 health_interval 30s 113 health_timeout 5s 114 115 # Headers for proper client IP handling 116 header_up Host {host} 117 header_up X-Real-IP {remote_host} 118 header_up X-Forwarded-For {remote_host} 119 header_up X-Forwarded-Proto {scheme} 120 121 # Note: Caddy v2 handles WebSocket upgrades automatically 122 # No need for explicit Connection/Upgrade headers 123 } 124 125 # Logging (Docker captures stdout/stderr) 126 log { 127 output stdout 128 format json 129 } 130 131 # Security headers 132 header { 133 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" 134 X-Content-Type-Options "nosniff" 135 X-Frame-Options "DENY" 136 Referrer-Policy "strict-origin-when-cross-origin" 137 -Server 138 } 139 140 # Enable compression 141 encode gzip zstd 142}