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 # Proxy all requests to AppView 64 handle { 65 reverse_proxy appview:8080 { 66 # Health check 67 health_uri /xrpc/_health 68 health_interval 30s 69 health_timeout 5s 70 71 # Headers for proper DPoP verification 72 # Host headers are critical for DPoP htu (HTTP URI) matching 73 header_up Host {host} 74 header_up X-Real-IP {remote_host} 75 header_up X-Forwarded-For {remote_host} 76 header_up X-Forwarded-Proto {scheme} 77 header_up X-Forwarded-Host {host} 78 } 79 } 80 81 # Logging (Docker captures stdout/stderr) 82 log { 83 output stdout 84 format json 85 } 86 87 # Security headers 88 header { 89 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" 90 X-Content-Type-Options "nosniff" 91 X-Frame-Options "DENY" 92 Referrer-Policy "strict-origin-when-cross-origin" 93 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" 94 # Remove Server header 95 -Server 96 } 97 98 # Enable compression 99 encode gzip zstd 100} 101 102# PDS Domain (both hostnames point to same PDS) 103# pds.coves.me is the canonical hostname for relay registration 104pds.coves.me, coves.me { 105 reverse_proxy pds:3000 { 106 # Health check 107 health_uri /xrpc/_health 108 health_interval 30s 109 health_timeout 5s 110 111 # Headers for proper client IP handling 112 header_up Host {host} 113 header_up X-Real-IP {remote_host} 114 header_up X-Forwarded-For {remote_host} 115 header_up X-Forwarded-Proto {scheme} 116 117 # Note: Caddy v2 handles WebSocket upgrades automatically 118 # No need for explicit Connection/Upgrade headers 119 } 120 121 # Logging (Docker captures stdout/stderr) 122 log { 123 output stdout 124 format json 125 } 126 127 # Security headers 128 header { 129 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" 130 X-Content-Type-Options "nosniff" 131 X-Frame-Options "DENY" 132 Referrer-Policy "strict-origin-when-cross-origin" 133 -Server 134 } 135 136 # Enable compression 137 encode gzip zstd 138}