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