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