1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 | # makefuture.ro — the site, plus the nordschleife multiplayer relay.
# ---------------------------------------------------------------------------
# Deployed to /opt/web/caddy/etc/conf.d/makefuture.caddy. This is the whole
# public configuration of the site: routing, TLS hostnames, headers, caching.
# It lives in this repository and ships with deploy.sh, so the config and the
# content it serves are always the same version.
#
# Snippet names are prefixed `mf-` because conf.d is shared with eratic.ro and
# opus.ro, and two projects both defining `(site)` would be a genuinely
# confusing afternoon.
#
# Certificates are obtained automatically for every hostname named below.
# Nothing to renew, nothing to remember.
(mf-hardening) {
header {
# One year. After the first visit the browser refuses plain HTTP here.
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
# The site's actual dependencies, written down.
#
# 'unsafe-inline' is present for script-src, and that is a real
# weakening rather than an oversight worth pretending about: these are
# hand-written pages where the game bootstraps, the importmap in
# nordschleife.html and the relay URL are all inline <script> blocks.
# Removing it means hashing every one of them and rehashing on every
# edit. What the policy still buys is the origin list below — a pixel,
# font or analytics snippet added by accident fails visibly instead of
# quietly phoning home.
#
# The named origins are each load-bearing:
# cdn.jsdelivr.net three.js (nordschleife importmap) and Phaser 3
# fonts.googleapis the stylesheet; fonts.gstatic the font files
# ipwho.is coarse IP geolocation for the VPN mechanic in
# accept-the-cookie, and only after consent
# wss://makefuture.ro the multiplayer relay proxied at /ws below
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob:; media-src 'self'; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://ipwho.is wss://makefuture.ro; worker-src 'self' blob:; frame-ancestors 'none'; base-uri 'none'; form-action 'none'; object-src 'none'; upgrade-insecure-requests"
Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=(), interest-cohort=()"
-Server
-X-Powered-By
}
}
(mf-caching) {
# Nothing here is fingerprinted — this is a hand-written site, so every URL
# is stable and its content can change under it. That rules out
# `immutable` anywhere, and makes the ceiling a judgement call rather than
# a certainty.
# Big static media and 3D assets. A day is long enough to matter for the
# 6MB of assets/ and short enough that a replaced texture is not haunting
# people next week.
@static path *.png *.jpg *.jpeg *.webp *.gif *.svg *.ico *.mp4 *.webm *.woff *.woff2 *.glb *.gltf *.bin
header @static Cache-Control "public, max-age=86400"
# Track geometry for nordschleife: generated data, refreshed only when the
# build script reruns.
@trackdata path /games/nordschleife/data/*
header @trackdata Cache-Control "public, max-age=86400"
# Feeds sit in between. Ten minutes feels live without making a reader who
# polls every minute free traffic.
@feeds path /feed.xml /sitemap.xml
header @feeds Cache-Control "public, max-age=600"
# HTML and everything else is always revalidated, or a deploy would not be
# visible until someone's cache happened to expire.
@revalidate {
not path /games/nordschleife/data/* /feed.xml /sitemap.xml
not path *.png *.jpg *.jpeg *.webp *.gif *.svg *.ico *.mp4 *.webm *.woff *.woff2 *.glb *.gltf *.bin
}
header @revalidate Cache-Control "public, max-age=0, must-revalidate"
}
# ---------------------------------------------------------------------------
# makefuture.ro
# ---------------------------------------------------------------------------
# Every game used to be a flat file under /experiments/. Those URLs were
# linked and bookmarked while they were the only address, so they keep
# working as permanent redirects into /games/.
(mf-legacy-experiments) {
redir /experiments/nordschleife.html /games/nordschleife/ 308
redir /experiments/racer.html /games/racer/ 308
redir /experiments/curve.html /games/curve/ 308
redir /experiments/snake.html /games/snake/ 308
redir /experiments/tetris.html /games/tetris/ 308
redir /experiments/breakout.html /games/breakout/ 308
redir /experiments/life.html /games/life/ 308
}
makefuture.ro {
root * /srv/makefuture/current
encode zstd gzip
import mf-hardening
import mf-caching
import mf-legacy-experiments
# The nordschleife multiplayer relay.
#
# Same origin as the game deliberately: the page connects to
# wss://makefuture.ro/ws, so there is no CORS to configure and no second
# certificate to keep alive. Caddy upgrades the connection and proxies it
# to the relay container over the web-edge network — the relay itself
# publishes no host port and is unreachable from the internet except
# through here.
handle /ws {
reverse_proxy mp-relay:8090
}
handle {
file_server
}
# Matches eratic.ro's privacy stance: no request logs, no IPs recorded.
log {
output discard
}
}
# ---------------------------------------------------------------------------
# play.makefuture.ro — the games library.
# ---------------------------------------------------------------------------
# Same document root as the main site, so /games/, /assets/ and every game URL
# resolve identically on both hostnames — nothing is copied, nothing can
# drift. The only difference is the front door: / lands on the library instead
# of the landing page. The rewrite is internal, so the address bar stays
# play.makefuture.ro.
#
# The nordschleife relay needs no handler here: the page connects to
# wss://makefuture.ro/ws by absolute URL, which mf-hardening's connect-src
# already allows regardless of which hostname served the page.
#
# NOTE: the DNS record for this hostname must exist (A 178.104.116.235 or
# CNAME makefuture.ro) before Caddy can obtain its certificate. Until then
# Caddy retries issuance in the background; the other sites are unaffected.
play.makefuture.ro {
root * /srv/makefuture/current
encode zstd gzip
import mf-hardening
import mf-caching
import mf-legacy-experiments
rewrite / /games/
handle {
file_server
}
log {
output discard
}
}
# ---------------------------------------------------------------------------
# One canonical hostname. 308 keeps the method and body, and browsers remember
# it.
# ---------------------------------------------------------------------------
www.makefuture.ro {
redir https://makefuture.ro{uri} 308
}
|