# Security & Hardening Backlog Tracks the deliberately-deferred items from the 2026-06-27 audit and its review passes. The Critical→Medium findings and the cheap "bucket 🅰" LOWs are fixed on `fix/audit-2026-06-27-critical-medium`. What remains is recorded here so the decisions are explicit, not forgotten. ## 🅱 Worth a tracked ticket (real, not one-liners) - **Moderation UI gap** — the backend `DELETE /host/upload/{id}` and `DELETE /host/comment/{id}` endpoints have **no frontend caller**, so a host cannot remove a guest's content from the UI. This is a functional hole, not polish. Needs a host-facing "remove" action wired to those endpoints (a `ContextSheet` action gated on host role). - **Feed reactivity** — a single global SSE event (`like-update`/`new-comment`/`upload-processed`) triggers `loadFeed(true)`, which full-replaces the list with the first 20, collapsing a scrolled feed; and owner-deleted uploads aren't broadcast to other clients (only host deletes are). Patch the affected item from the SSE payload instead of full-reloading; emit `upload-deleted` from the owner `delete_upload` path too. - **Media HMAC domain separation** — the signed-media tokens reuse `jwt_secret` as the HMAC key. Correct today (different message structure), but a dedicated derived key (`HKDF(jwt_secret, "media-url")`) would isolate the domains so a future change to one can't weaken the other. - **Quota mount-detection + low-disk guard** — `compute_storage_quota` picks the disk via `starts_with` (root is a wildcard prefix → can match the wrong device) and there's no hard min-free-space precheck when the quota is disabled. Use longest-prefix match; add an unconditional 507/429 when free space is critically low. ## 🅲 Consciously won't-fix at ~100-guest single-box scale Diminishing returns vs. the deployment's actual threat model. Revisit only if the scale or tenancy model changes. - Rate-limiter `HashMap` key LRU/cap (attacker-chosen `recover:{ip}:{name}` keys accumulate up to the 24h prune ceiling) — bounded and pruned; not worth an LRU. - "Last host" / host↔host role-churn guard — operational, low blast radius. - Performance micro-indexes (`idx_like_user_upload`, comment pagination index) — current queries are sub-ms at this row count. - Optimistic-like in-flight guard, ownership-snapshot-at-mount, assorted copy tweaks — UX polish. ## By-design notes (documented, not bugs) - **Banned user retains ≤24h media access via already-held signed URLs.** The media gateway authorizes by *signature + uploader visibility*, not requester identity, and signed URLs are time-boxed (24h, bucketed). A banned/revoked-session user cannot mint new URLs (every API call 401/403s) but can replay URLs they already hold until expiry — only for content they already saw. Accepted: tightening this would require per-request identity on every `` load, which the ``-can't-send-a-Bearer constraint precludes. Shorten the TTL in `media_token.rs` if a stricter bound is ever needed. - **Two DB queries per *cold* media fetch** (`upload` row + uploader row). Mitigated by browser caching and the stable bucketed URL (so warm fetches don't hit the backend at all). Could be one JOIN if it ever shows up in profiling.