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}