chore(contrib): security headers in example nginx/caddy configs (P6-4)

Add HSTS + Permissions-Policy + the standard X-Frame/X-Content/Referrer set to
the contrib nginx (443 block) and caddy examples; fix the caddy SPA try_files
fallback (stray space). Generic (no homeserver-specific CSP). The real prod
config lives in the matrix repo. P6-4 trimmed to headers only — patch-package /
types-drift / build-config skipped (see LOTUS_TODO).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 14:41:08 -04:00
parent ebcd8ec926
commit c82ab5c7f5
4 changed files with 30 additions and 2 deletions
+1
View File
@@ -45,6 +45,7 @@ Implemented and gate-green; confirm each per `LOTUS_TESTING.md`, then delete the
| P6-1 | Desktop Linux parity (no-sleep in calls, launcher badge), autostart toggle, tray Do-Not-Disturb | `native/power.rs`, `lib.rs`, `useTauriDnd`, `General.tsx` | Linux desktop: no display sleep during a call; tray DND silences notifications; launch-on-login persists; Unity badge (Ubuntu); DND toggle polarity | | P6-1 | Desktop Linux parity (no-sleep in calls, launcher badge), autostart toggle, tray Do-Not-Disturb | `native/power.rs`, `lib.rs`, `useTauriDnd`, `General.tsx` | Linux desktop: no display sleep during a call; tray DND silences notifications; launch-on-login persists; Unity badge (Ubuntu); DND toggle polarity |
| P6-2 | EC deafen/screenshare-audio-mute via `io.lotus.set_deafen` (retires the `<audio>.muted` iframe hack) | fork `lotusDeafen.ts`, cinny `CallControl.ts` | AFTER publish+pin-bump: deafen silences remote audio + survives a reconnect / new screenshare / late joiner (the cases the DOM hack failed); screenshare-audio-mute toggles independently | | P6-2 | EC deafen/screenshare-audio-mute via `io.lotus.set_deafen` (retires the `<audio>.muted` iframe hack) | fork `lotusDeafen.ts`, cinny `CallControl.ts` | AFTER publish+pin-bump: deafen silences remote audio + survives a reconnect / new screenshare / late joiner (the cases the DOM hack failed); screenshare-audio-mute toggles independently |
| P6-3 | Forward-to-multiple-rooms (multi-select + partial-failure summary) + live bookmark previews (edits/redactions, snapshot fallback) | `ForwardMessageDialog.tsx`+`forwardContent.ts`, `BookmarksPanel.tsx` | forward one msg to 3 rooms (incl. 1 you cannot post to = partial summary); bookmark then edit shows edited; redact shows deleted; leave room shows snapshot | | P6-3 | Forward-to-multiple-rooms (multi-select + partial-failure summary) + live bookmark previews (edits/redactions, snapshot fallback) | `ForwardMessageDialog.tsx`+`forwardContent.ts`, `BookmarksPanel.tsx` | forward one msg to 3 rooms (incl. 1 you cannot post to = partial summary); bookmark then edit shows edited; redact shows deleted; leave room shows snapshot |
| P6-4 | HSTS + Permissions-Policy on prod nginx (+ contrib examples) | `matrix/cinny/nginx.conf`, `contrib/nginx`, `contrib/caddy` | after `nginx -s reload`: `curl -sI https://chat.lotusguild.org` shows HSTS + Permissions-Policy; a call (cam/mic/screenshare) + location share still work |
**Verified working in live testing (2026-06):** A2, B1B4, C1, C3, D (mic/camera/deafen/screenshare/fullscreen/more-menu/PiP). Denoise quality in D is still poor — tracked under the denoise project, not a regression. **Verified working in live testing (2026-06):** A2, B1B4, C1, C3, D (mic/camera/deafen/screenshare/fullscreen/more-menu/PiP). Denoise quality in D is still poor — tracked under the denoise project, not a regression.
+8 -1
View File
@@ -553,7 +553,14 @@ _Original scope:_
- **Live bookmark previews** — `BookmarksPanel` shows a stale snapshot captured at save time; resolve live from the event when cached (edits/redactions), fall back to the snapshot. - **Live bookmark previews** — `BookmarksPanel` shows a stale snapshot captured at save time; resolve live from the event when cached (edits/redactions), fall back to the snapshot.
- Other small paper-cuts as scoped. - Other small paper-cuts as scoped.
### [ ] P6-4 · Hygiene sweep ### [~] P6-4 · Hygiene sweep - TRIMMED (2026-07): security headers only
**Shipped:** HSTS + Permissions-Policy on the real prod nginx (`matrix/cinny/nginx.conf`, already had X-Frame/CSP/Referrer) + synced the `contrib/nginx` + `contrib/caddy` examples (also fixed the caddy `try_files` SPA fallback). Permissions-Policy allows `self` for the features the app uses (camera/mic/display-capture/geolocation/autoplay/fullscreen), denies unused. **User must `nginx -s reload` on the LXC + verify calls/location still work.**
**WON'T-DO (rationale):** patch-package migration - the current `patch-folds.mjs` is already robust (fails hard on drift) and patch-package would be more brittle to folds restructuring; `types/matrix` drift - risky spot-fixes with no concrete bug; build-config streamlining - build is already ~5s. Known follow-up: nginx `add_header` isn't inherited by the cache `location` blocks (pre-existing; the SPA entry `/` still gets all headers, so HSTS is delivered).
_Original scope:_
### [ ] P6-4-orig · Hygiene sweep
- `patch-folds.mjs` (edits `node_modules` directly) → `patch-package`. - `patch-folds.mjs` (edits `node_modules` directly) → `patch-package`.
- `contrib/nginx` + `contrib/caddy`: security headers (HSTS/CSP), `try_files` over rewrites, fix the caddy placeholder path. - `contrib/nginx` + `contrib/caddy`: security headers (HSTS/CSP), `try_files` over rewrites, fix the caddy placeholder path.
+12 -1
View File
@@ -1,6 +1,17 @@
# more info: https://caddyserver.com/docs/caddyfile/patterns#single-page-apps-spas # more info: https://caddyserver.com/docs/caddyfile/patterns#single-page-apps-spas
cinny.domain.tld { cinny.domain.tld {
root * /path/to/cinny/dist root * /path/to/cinny/dist
try_files {path} / index.html try_files {path} /index.html
file_server file_server
# Security headers (generic; add a Content-Security-Policy suited to your
# homeserver + any embedded services). Caddy serves HTTPS automatically, so
# HSTS is delivered over TLS.
header {
X-Frame-Options SAMEORIGIN
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
Strict-Transport-Security "max-age=63072000; includeSubDomains"
Permissions-Policy "accelerometer=(), autoplay=(self), camera=(self), display-capture=(self), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(self), midi=(), payment=(), usb=()"
}
} }
+9
View File
@@ -17,6 +17,15 @@ server {
listen [::]:443 ssl; listen [::]:443 ssl;
server_name cinny.domain.tld; server_name cinny.domain.tld;
# Security headers (generic; add a Content-Security-Policy suited to your
# homeserver + any embedded services). NOTE: nginx does not inherit
# server-level add_header into a location that sets its own add_header.
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header Permissions-Policy "accelerometer=(), autoplay=(self), camera=(self), display-capture=(self), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(self), midi=(), payment=(), usb=()" always;
location / { location / {
root /opt/cinny/dist/; root /opt/cinny/dist/;