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 root * /srv
52 file_server
53 }
54
55 # Serve OAuth client metadata
56 handle /client-metadata.json {
57 root * /srv
58 file_server
59 }
60
61 # Serve OAuth callback page
62 handle /oauth/callback {
63 root * /srv
64 rewrite * /oauth/callback.html
65 file_server
66 }
67
68 # Proxy all other requests to AppView
69 handle {
70 reverse_proxy appview:8080 {
71 # Health check
72 health_uri /xrpc/_health
73 health_interval 30s
74 health_timeout 5s
75
76 # Headers
77 header_up X-Real-IP {remote_host}
78 header_up X-Forwarded-For {remote_host}
79 header_up X-Forwarded-Proto {scheme}
80 }
81 }
82
83 # Logging (Docker captures stdout/stderr)
84 log {
85 output stdout
86 format json
87 }
88
89 # Security headers
90 header {
91 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
92 X-Content-Type-Options "nosniff"
93 X-Frame-Options "DENY"
94 Referrer-Policy "strict-origin-when-cross-origin"
95 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"
96 # Remove Server header
97 -Server
98 }
99
100 # Enable compression
101 encode gzip zstd
102}
103
104# PDS Domain
105coves.me {
106 reverse_proxy pds:3000 {
107 # Health check
108 health_uri /xrpc/_health
109 health_interval 30s
110 health_timeout 5s
111
112 # Headers for proper client IP handling
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 # WebSocket support for firehose
118 header_up Connection {>Connection}
119 header_up Upgrade {>Upgrade}
120 }
121
122 # Logging (Docker captures stdout/stderr)
123 log {
124 output stdout
125 format json
126 }
127
128 # Security headers
129 header {
130 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
131 X-Content-Type-Options "nosniff"
132 X-Frame-Options "DENY"
133 Referrer-Policy "strict-origin-when-cross-origin"
134 -Server
135 }
136
137 # Enable compression
138 encode gzip zstd
139}