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}