Fixes#23. Production change: merging redeploys the web nginx config and the deploy script, so it waits for an owner's go-ahead.
Problem: SPA URLs (/, /home/…, /login/…) reach index.html through location / { rewrite ^(.+)$ /index.html break; }, which set no cache headers. Browsers then reused the shell heuristically (about 10 % of its age). Meanwhile lotus_deploy.sh ran rsync --delete, which removed the previous build's hashed assets. So after a deploy, a visitor without the service worker could get a stale shell requesting assets/index-<old>.js → 404 → blank app.
Changes
cinny/nginx.conf:map $uri $lotus_shell_cache_control adds no-cache, no-store, must-revalidate only when the request was rewritten to /index.html. Other files served by location / are unchanged (the map yields an empty value, so no header is added). location / now includes cinny-security-headers.conf, because an add_header in a location drops the server-level headers (CSP etc.).
cinny/lotus_deploy.sh:assets/ is no longer --deleted. New assets are synced, and files older than 14 days are pruned. Unchanged assets get a fresh mtime from the new build, so they're kept.
Verified
nginx -t passes in a harness using this repo's snippets (brotli lines stripped, since the module isn't local).
Serving a fake webroot through the new config: /, /home/, /login/… and /index.html → Cache-Control: no-cache, no-store, must-revalidate, CSP present. /assets/index-abc.js → public, immutable. /config.json → no-cache. /public/res/sound.ogg → unchanged.
bash -n lotus_deploy.sh OK.
After merge: run the usual nginx -t && systemctl reload nginx on LXC 106, then curl -sI https://chat.lotusguild.org/home/ | grep -i cache-control.
Fixes #23. **Production change:** merging redeploys the web nginx config and the deploy script, so it waits for an owner's go-ahead.
**Problem:** SPA URLs (`/`, `/home/…`, `/login/…`) reach `index.html` through `location / { rewrite ^(.+)$ /index.html break; }`, which set **no cache headers**. Browsers then reused the shell heuristically (about 10 % of its age). Meanwhile `lotus_deploy.sh` ran `rsync --delete`, which removed the previous build's hashed assets. So after a deploy, a visitor without the service worker could get a stale shell requesting `assets/index-<old>.js` → 404 → blank app.
**Changes**
- **`cinny/nginx.conf`:** `map $uri $lotus_shell_cache_control` adds `no-cache, no-store, must-revalidate` only when the request was rewritten to `/index.html`. Other files served by `location /` are unchanged (the map yields an empty value, so no header is added). `location /` now includes `cinny-security-headers.conf`, because an `add_header` in a location drops the server-level headers (CSP etc.).
- **`cinny/lotus_deploy.sh`:** `assets/` is no longer `--delete`d. New assets are synced, and files older than 14 days are pruned. Unchanged assets get a fresh mtime from the new build, so they're kept.
**Verified**
- `nginx -t` passes in a harness using this repo's snippets (brotli lines stripped, since the module isn't local).
- Serving a fake webroot through the new config: `/`, `/home/`, `/login/…` and `/index.html` → `Cache-Control: no-cache, no-store, must-revalidate`, CSP present. `/assets/index-abc.js` → `public, immutable`. `/config.json` → no-cache. `/public/res/sound.ogg` → unchanged.
- `bash -n lotus_deploy.sh` OK.
**After merge:** run the usual `nginx -t && systemctl reload nginx` on LXC 106, then `curl -sI https://chat.lotusguild.org/home/ | grep -i cache-control`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
SPA URLs (/, /home/…, /login/…) are rewritten to /index.html inside
location /, which set no cache headers, so browsers heuristically reused
a stale shell after a deploy, and rsync --delete had just removed the
assets it referenced: a blank app until the shell expired. Send
no-cache for the rewritten shell (map on $uri; location / now includes
the security headers, since add_header drops the inherited ones), and
stop deleting previous builds' hashed assets: prune after 14 days.
Fixes#23
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared
merged commit 9b0cbc9db1 into main2026-10-18 19:44:17 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #23. Production change: merging redeploys the web nginx config and the deploy script, so it waits for an owner's go-ahead.
Problem: SPA URLs (
/,/home/…,/login/…) reachindex.htmlthroughlocation / { rewrite ^(.+)$ /index.html break; }, which set no cache headers. Browsers then reused the shell heuristically (about 10 % of its age). Meanwhilelotus_deploy.shranrsync --delete, which removed the previous build's hashed assets. So after a deploy, a visitor without the service worker could get a stale shell requestingassets/index-<old>.js→ 404 → blank app.Changes
cinny/nginx.conf:map $uri $lotus_shell_cache_controladdsno-cache, no-store, must-revalidateonly when the request was rewritten to/index.html. Other files served bylocation /are unchanged (the map yields an empty value, so no header is added).location /now includescinny-security-headers.conf, because anadd_headerin a location drops the server-level headers (CSP etc.).cinny/lotus_deploy.sh:assets/is no longer--deleted. New assets are synced, and files older than 14 days are pruned. Unchanged assets get a fresh mtime from the new build, so they're kept.Verified
nginx -tpasses in a harness using this repo's snippets (brotli lines stripped, since the module isn't local)./,/home/,/login/…and/index.html→Cache-Control: no-cache, no-store, must-revalidate, CSP present./assets/index-abc.js→public, immutable./config.json→ no-cache./public/res/sound.ogg→ unchanged.bash -n lotus_deploy.shOK.After merge: run the usual
nginx -t && systemctl reload nginxon LXC 106, thencurl -sI https://chat.lotusguild.org/home/ | grep -i cache-control.🤖 Generated with Claude Code
https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5