Monorepo for Aesthetic.Computer
aesthetic.computer
1# lith production Caddyfile — full subdomain routing
2# Cloudflare handles TLS. Caddy serves HTTP on :80.
3
4# --- papers.aesthetic.computer ---
5# (touched 2026-04-29 to re-fire webhook after the install-Caddyfile-before-reload fix)
6:443 {
7 tls /etc/caddy/origin-cert.pem /etc/caddy/origin-key.pem
8 log {
9 output file /var/log/caddy/access.log {
10 roll_size 50MiB
11 roll_keep 3
12 roll_keep_for 72h
13 }
14 format json
15 level INFO
16 }
17 # --- Global performance headers ---
18 # Cache static assets (1h fresh, serve stale for 24h while revalidating)
19 @cacheable path *.mjs *.js *.css *.woff2 *.woff *.ttf *.png *.jpg *.jpeg *.svg *.gif *.webp *.ico *.mp3 *.wav *.mp4 *.json
20 header @cacheable Cache-Control "public, max-age=3600, stale-while-revalidate=86400"
21 header @cacheable Access-Control-Allow-Origin *
22
23 # Service workers must always revalidate — cached sw.js delays rollout of
24 # bumped CACHE_NAME, pinning clients to stale module caches.
25 @serviceworker path /sw.js /firebase-messaging-sw.js
26 header @serviceworker Cache-Control "no-cache, no-store, must-revalidate"
27
28 # HTML: no-cache (always revalidate, but use ETag for 304)
29 @html path / *.html
30 header @html Cache-Control "no-cache"
31
32 # Encode everything (Caddy does this by default, but be explicit)
33 encode zstd gzip
34
35 @papers host papers.aesthetic.computer papers.prompt.ac
36 handle @papers {
37 handle /en {
38 rewrite * /index.html?lang=en
39 root * /opt/ac/system/public/papers.aesthetic.computer
40 file_server
41 }
42 handle /da {
43 rewrite * /index.html?lang=da
44 root * /opt/ac/system/public/papers.aesthetic.computer
45 file_server
46 }
47 handle /es {
48 rewrite * /index.html?lang=es
49 root * /opt/ac/system/public/papers.aesthetic.computer
50 file_server
51 }
52 handle /cn {
53 rewrite * /index.html?lang=zh
54 root * /opt/ac/system/public/papers.aesthetic.computer
55 file_server
56 }
57 root * /opt/ac/system/public/papers.aesthetic.computer
58
59 # /platter is the existing white research-records page (platter.html).
60 # We briefly created /platter/ as a directory with our own index — that
61 # was the wrong call; restored below by deleting the dir's index and
62 # reversing the prior 301 with a 302 to invalidate cached redirects.
63 # /platter/jeffrey/ remains a real directory (the dashboard); its
64 # explicit canonical redirect stays so the page's relative
65 # `./manifest.json` fetch resolves correctly.
66 # (See: 2026-04-29 platter restoration.)
67 redir /platter/ /platter 302
68 redir /platter/jeffrey /platter/jeffrey/ permanent
69
70 # SPA fallback for unknown paths: if none of {path}, {path}.html,
71 # {path}/index.html, or {path}index.html exists on disk, serve
72 # /index.html — but mark it no-cache. Without this, a request for a
73 # not-yet-deployed PDF gets the SPA HTML, and Cloudflare pins that
74 # HTML to the PDF URL for 4h via its default static-asset cache.
75 # (See: 2026-04-26 latency-paper deploy gap.) The two index.html
76 # terms cover both /platter (no slash → {path}/index.html) and
77 # /platter/ (slash → {path}index.html). (See: 2026-04-29
78 # jeffrey-platter URL move.)
79 @missing not file {
80 try_files {path} {path}.html {path}/index.html {path}index.html
81 }
82 handle @missing {
83 header Cache-Control "no-cache, must-revalidate, max-age=0"
84 rewrite * /index.html
85 file_server
86 }
87
88 # NOTE: {path} is intentionally absent from the top-level try_files.
89 # Caddy's try_files matches directories as well as files, so including
90 # {path} would silently match a request for /platter (the directory),
91 # short-circuit before {path}/index.html, then file_server would serve
92 # the wrong content rather than canonicalizing to /platter/. Without
93 # {path} here, file_server handles existing regular files itself and
94 # issues a 301 redirect for directory-without-slash to the canonical
95 # /<dir>/ form. (See: 2026-04-29 platter URL move.)
96 try_files {path}.html {path}/index.html {path}index.html
97 file_server
98 }
99
100 # --- bills.aesthetic.computer ---
101 @bills host bills.aesthetic.computer
102 handle @bills {
103 root * /opt/ac/system/public/bills.aesthetic.computer
104 try_files {path} {path}.html /index.html
105 file_server
106 }
107
108 # --- give.aesthetic.computer ---
109 @give host give.aesthetic.computer
110 handle @give {
111 handle /api/* {
112 reverse_proxy localhost:8888
113 }
114 handle /da {
115 rewrite * /index.html?lang=da¤cy=dkk
116 root * /opt/ac/system/public/give.aesthetic.computer
117 file_server
118 }
119 handle /es {
120 rewrite * /index.html?lang=es¤cy=usd
121 root * /opt/ac/system/public/give.aesthetic.computer
122 file_server
123 }
124 handle /de {
125 rewrite * /index.html?lang=de¤cy=eur
126 root * /opt/ac/system/public/give.aesthetic.computer
127 file_server
128 }
129 handle /cn {
130 rewrite * /index.html?lang=zh¤cy=usd
131 root * /opt/ac/system/public/give.aesthetic.computer
132 file_server
133 }
134 root * /opt/ac/system/public/give.aesthetic.computer
135 try_files {path} {path}.html /index.html
136 file_server
137 }
138
139 # --- news.aesthetic.computer ---
140 @news host news.aesthetic.computer
141 handle @news {
142 handle /api/* {
143 reverse_proxy localhost:8888
144 }
145 handle {
146 rewrite * /api/news{uri}
147 reverse_proxy localhost:8888
148 }
149 }
150
151 # --- api.aesthetic.computer ---
152 @apidomain host api.aesthetic.computer api.prompt.ac
153 handle @apidomain {
154 reverse_proxy localhost:8888
155 }
156
157 # --- feed.aesthetic.computer (DP-1 Feed V2 — Go + Postgres) ---
158 @feed host feed.aesthetic.computer
159 handle @feed {
160 header Access-Control-Allow-Origin *
161 handle /api/* {
162 reverse_proxy localhost:8787
163 }
164 handle /health {
165 reverse_proxy localhost:8787
166 }
167 handle {
168 root * /opt/dp1-feed
169 rewrite * /landing-page.html
170 file_server
171 }
172 }
173
174 # --- ipfs.aesthetic.computer (self-hosted IPFS gateway) ---
175 # Kubo emits Access-Control-Allow-Origin: * on its own. An earlier Caddy
176 # `header` directive was stacking a second copy, producing "*, *" that
177 # browsers reject. Let Kubo own the header, Caddy just proxies.
178 @ipfs host ipfs.aesthetic.computer
179 handle @ipfs {
180 reverse_proxy localhost:8090
181 }
182
183 # --- justanothersystem.org ---
184 @wwwjas2 host www.justanothersystem.org
185 handle @wwwjas2 {
186 redir https://justanothersystem.org{uri} 301
187 }
188
189 @jas host justanothersystem.org
190 handle @jas {
191 handle /api/* {
192 reverse_proxy localhost:8888
193 }
194 redir /bio /cv 301
195 redir /bio/ /cv 301
196 root * /opt/ac/system/public/justanothersystem.org
197 try_files {path} {path}.html /index.html
198 file_server
199 }
200
201 # --- builds.false.work ---
202 @builds_api {
203 host builds.false.work
204 path /api/* /.netlify/functions/*
205 }
206 handle @builds_api {
207 reverse_proxy localhost:8888
208 }
209 @builds host builds.false.work
210 handle @builds {
211 root * /opt/ac/system/public/builds.false.work
212 try_files {path} {path}.html /index.html
213 file_server
214 }
215
216 # --- sotce.net ---
217 @sotce host sotce.net www.sotce.net
218 handle @sotce {
219 handle /api/* {
220 reverse_proxy localhost:8888
221 }
222 handle /user {
223 reverse_proxy localhost:8888
224 }
225 handle /handle {
226 reverse_proxy localhost:8888
227 }
228 handle /authorized {
229 reverse_proxy localhost:8888
230 }
231 handle /aesthetic.computer/* {
232 uri strip_prefix /aesthetic.computer
233 root * /opt/ac/system/public/aesthetic.computer
234 file_server
235 }
236 # Everything else → sotce-net function
237 handle {
238 rewrite * /api/sotce-net{uri}
239 reverse_proxy localhost:8888
240 }
241 }
242
243 # --- kidlisp.com subdomains ---
244 @keep host keep.kidlisp.com
245 handle @keep {
246 handle /api/* {
247 reverse_proxy localhost:8888
248 }
249 handle /technology {
250 root * /opt/ac/system/public/kidlisp.com
251 rewrite * /keeps-tech.html
252 file_server
253 }
254 handle /wallet {
255 root * /opt/ac/system/public/kidlisp.com
256 rewrite * /wallet/index.html
257 file_server
258 }
259 handle {
260 root * /opt/ac/system/public/kidlisp.com
261 rewrite * /keeps.html
262 file_server
263 }
264 }
265
266 @buy host buy.kidlisp.com
267 handle @buy {
268 root * /opt/ac/system/public/kidlisp.com
269 rewrite * /buy.html
270 file_server
271 }
272
273 @pj host pj.kidlisp.com
274 handle @pj {
275 root * /opt/ac/system/public/kidlisp.com
276 rewrite * /pj.html
277 file_server
278 }
279
280 @device host device.kidlisp.com
281 handle @device {
282 handle /api/* {
283 reverse_proxy localhost:8888
284 }
285 handle /js/* {
286 root * /opt/ac/system/public/kidlisp.com
287 file_server
288 }
289 handle /qr/* {
290 root * /opt/ac/system/public/kidlisp.com
291 rewrite * /qr.html
292 file_server
293 }
294 handle {
295 root * /opt/ac/system/public/kidlisp.com
296 rewrite * /device.html
297 file_server
298 }
299 }
300
301 @topcalm host top.kidlisp.com calm.kidlisp.com
302 handle @topcalm {
303 handle /js/* {
304 root * /opt/ac/system/public/kidlisp.com
305 file_server
306 }
307 reverse_proxy localhost:8888
308 }
309
310 @learn host learn.kidlisp.com
311 handle @learn {
312 handle /api/* {
313 reverse_proxy localhost:8888
314 }
315 handle /decree* {
316 root * /opt/ac/system/public/kidlisp.com
317 rewrite * /decree.html
318 file_server
319 }
320 handle {
321 root * /opt/ac/system/public/kidlisp.com
322 rewrite * /learn.html
323 file_server
324 }
325 }
326
327 @keepsredirect host keeps.kidlisp.com
328 handle @keepsredirect {
329 redir https://keep.kidlisp.com{uri} 301
330 }
331
332 @keepsac host keeps.aesthetic.computer
333 handle @keepsac {
334 reverse_proxy localhost:8888
335 }
336
337 # --- jas.life ---
338 @jaslife host jas.life
339 handle @jaslife {
340 root * /opt/ac/system/public/jas.life
341 try_files {path} {path}.html /index.html
342 file_server
343 }
344
345 # --- www.jas.life redirect ---
346 @wwwjas host www.jas.life
347 handle @wwwjas {
348 redir https://jas.life{uri} 301
349 }
350
351 # --- pals.aesthetic.computer ---
352 # pals.aesthetic.computer → /api/logo (dynamic logo generation)
353 @pals host pals.aesthetic.computer
354 handle @pals {
355 rewrite * /api/logo{uri}
356 reverse_proxy localhost:8888
357 }
358
359 @l5prompt host l5.prompt.ac
360 handle @l5prompt {
361 redir https://l5.aesthetic.computer{uri} 301
362 }
363 @p5prompt host p5.prompt.ac
364 handle @p5prompt {
365 redir https://p5.aesthetic.computer{uri} 301
366 }
367 @procprompt host processing.prompt.ac
368 handle @procprompt {
369 redir https://processing.aesthetic.computer{uri} 301
370 }
371 @siteprompt host sitemap.prompt.ac
372 handle @siteprompt {
373 redir https://sitemap.aesthetic.computer{uri} 301
374 }
375 @apiprompt host api.prompt.ac
376 handle @apiprompt {
377 redir https://api.aesthetic.computer{uri} 301
378 }
379
380 # --- legacy duckweedtri hosts ---
381 @duckweedaesthetic host duckweedtri.aesthetic.computer
382 handle @duckweedaesthetic {
383 redir https://aesthetic.computer{uri} 301
384 }
385
386 @duckweedprompt host duckweedtri.prompt.ac
387 handle @duckweedprompt {
388 redir https://aesthetic.computer{uri} 301
389 }
390
391 # --- prompt.ac/menuband: serve from origin (no redirect) ---
392 # The host+path matcher is more specific than the host-only @promptac
393 # below, so Caddy evaluates this first. Earlier nested-handle attempt
394 # silently fell through to the redirect; named top-level matchers are
395 # the reliable shape.
396 @promptac_menuband {
397 host prompt.ac
398 path /menuband /menuband/*
399 }
400 handle @promptac_menuband {
401 root * /opt/ac/system/public
402 try_files {path} {path}/index.html
403 file_server
404 }
405
406 # --- prompt.ac → aesthetic.computer (everything else, 301) ---
407 @promptac host prompt.ac
408 handle @promptac {
409 redir https://aesthetic.computer{uri} 301
410 }
411
412 @tryshared {
413 host l5.aesthetic.computer processing.aesthetic.computer
414 path /aesthetic.computer/*
415 }
416 handle @tryshared {
417 root * /opt/ac/system/public
418 file_server
419 }
420
421 # --- l5.aesthetic.computer ---
422 @l5 host l5.aesthetic.computer
423 handle @l5 {
424 handle /api/* {
425 reverse_proxy localhost:8888
426 }
427 handle /docs* {
428 reverse_proxy localhost:8888
429 }
430 root * /opt/ac/system/public/l5.aesthetic.computer
431 try_files {path} {path}.html /index.html
432 file_server
433 }
434
435 # --- processing.aesthetic.computer ---
436 @processing host processing.aesthetic.computer
437 handle @processing {
438 handle /api/* {
439 reverse_proxy localhost:8888
440 }
441 handle /docs* {
442 reverse_proxy localhost:8888
443 }
444 root * /opt/ac/system/public/processing.aesthetic.computer
445 try_files {path} {path}.html /index.html
446 file_server
447 }
448
449 # --- rdp.jas.life ---
450 @rdp host rdp.jas.life
451 handle @rdp {
452 root * /opt/ac/system/public/rdp.jas.life
453 try_files {path} {path}.html /index.html
454 file_server
455 }
456
457 # --- kidlisp.com root domain ---
458 @kidlisproot host kidlisp.com www.kidlisp.com
459 handle @kidlisproot {
460 handle /api/* {
461 reverse_proxy localhost:8888
462 }
463 handle /.netlify/functions/* {
464 reverse_proxy localhost:8888
465 }
466 handle /keeps {
467 redir https://keep.kidlisp.com/ 301
468 }
469 # Serve AC runtime assets for iframe embedding
470 handle /aesthetic.computer/* {
471 root * /opt/ac/system/public
472 file_server
473 }
474 # kidlisp.com SPA
475 handle {
476 root * /opt/ac/system/public/kidlisp.com
477 try_files {path} {path}.html /index.html
478 file_server
479 }
480 }
481
482 # --- notepat.com ---
483 @notepatroot host notepat.com www.notepat.com
484 handle @notepatroot {
485 @notepatindex path /
486 handle @notepatindex {
487 rewrite * /notepat
488 }
489 # Permalink: notepat.com/amxd → current notepat.com.amxd as a
490 # direct download. Clients get the Content-Disposition so it
491 # lands in ~/Downloads instead of opening in-browser.
492 handle /amxd {
493 rewrite * /m4l/notepat.com.amxd
494 header Content-Disposition "attachment; filename=\"notepat.com.amxd\""
495 root * /opt/ac/system/public
496 file_server
497 }
498 reverse_proxy localhost:8888
499 }
500 @mainspa host aesthetic.computer www.aesthetic.computer lith.aesthetic.computer notepat.com www.notepat.com p5.aesthetic.computer sitemap.aesthetic.computer
501 handle @mainspa {
502 # Assets → DO Spaces CDN. The bucket root maps directly to
503 # assets.aesthetic.computer (no /assets/ prefix exists in the
504 # bucket), so we use handle_path to strip the matched /assets/
505 # prefix before substituting {path} into the redirect target.
506 # The previous form (`handle /assets/*` + `{uri}`) doubled the
507 # prefix and produced 403s on assets.aesthetic.computer/assets/...
508 # (See: 2026-04-29 platter readings 403 fix.)
509 handle_path /assets/* {
510 redir https://assets.aesthetic.computer{path} 302
511 }
512 # API → lith
513 handle /api/* {
514 reverse_proxy localhost:8888
515 }
516 handle /.netlify/functions/* {
517 reverse_proxy localhost:8888
518 }
519 # Media → lith
520 handle /media/* {
521 reverse_proxy localhost:8888
522 }
523 # Static rewrite shortcuts
524 handle /disks/* {
525 uri strip_prefix /disks
526 root * /opt/ac/system/public/aesthetic.computer/disks
527 file_server
528 }
529 handle /lib/* {
530 uri strip_prefix /lib
531 root * /opt/ac/system/public/aesthetic.computer/lib
532 file_server
533 }
534 # Static files, then SPA fallback
535 handle {
536 root * /opt/ac/system/public
537 @static file
538 handle @static {
539 file_server
540 }
541 handle {
542 reverse_proxy localhost:8888
543 }
544 }
545 }
546
547 # --- false.work ---
548 @falseroot host false.work
549 handle @falseroot {
550 root * /opt/ac/system/public/false.work
551 try_files {path} {path}.html /index.html
552 file_server
553 }
554
555 @wwwfalse host www.false.work
556 handle @wwwfalse {
557 redir https://false.work{uri} 301
558 }
559
560 # --- Fallback for any unmatched host ---
561 handle {
562 reverse_proxy localhost:8888
563 }
564}
565
566:80 {
567 redir https://{host}{uri} 301
568}