docs: what actually gates access, now that there is a deployment
The internal deployment exists (deployments repo, `mirror/`: quadlet, nginx edge, certbot, route53, VM SOP), so the docs that said "the real fix is HTTPS" were describing a future that has arrived from a different direction — TLS terminates in nginx on the service VM, not in this process, and there is no `--https` flag here. SECURITY.md's trust model said "trusted: anyone who can open a TCP connection". With an allow list in production that sentence was simply no longer true, so the table now names the address space, and a new section says what the list is and what it is not: one wall (network/nginx) and one belt (the app, for the path that bypasses nginx at 127.0.0.1:8080), plus the thing neither can do — once two browsers are in the room their media never asks the server again, and it can never tell you *who*, only *where from*. Saying "defence in depth" about that would have been the dishonest version. PLAN.md gains A19 (TLS + admission in production, LOCKED) and A18 becomes deprioritised rather than deleted — standalone `mirror serve --https` is still worth having for offline/ad-hoc use, it just stopped being the thing blocking cross-device sharing, and candidate phase 1 is now share-page resilience. AGENTS.md gains the two rules most likely to be broken by a well-meaning edit: trust XFF only from a trusted proxy, and change the nginx and app lists in the same commit. LLM-Generated: true
This commit is contained in:
@@ -48,7 +48,9 @@ came first, and the candidate phase list is §7 of `PLAN.md`.)*
|
||||
| **Media never passes through the server** | The server relays JSON only. Anything that puts frames in the Python process violates A3 |
|
||||
| **`Registry` stays pure** — no sockets, no FastAPI, no `await` | It returns `list[Envelope]`. Transport lives in `Hub`. This is why the policy has 100%-ish unit coverage |
|
||||
| **camelCase on the wire, snake_case in Python**, via `alias_generator=to_camel` | A silently-dropped `displaySurface` was a real bug we shipped. Any new multi-word field needs an alias test |
|
||||
| **No authentication** (A11) | Product brief. If you think it needs auth, that is a `PLAN.md` discussion first — and see `SECURITY.md` |
|
||||
| **No authentication** (A11) — but the **address is the access control** (A19) | Product brief: no accounts. What decides who gets in is `MIRROR_ALLOW_CIDRS` (`src/mirror/access.py`), enforced in the HTTP middleware *and* before the socket accepts. If you think it needs auth, that is a `PLAN.md` discussion first — and see `SECURITY.md` |
|
||||
| **Trust `X-Forwarded-For` only from `MIRROR_TRUSTED_PROXIES`, left-most value only** | Otherwise a browser writes its own pass. The deployment's nginx must *overwrite* the header (`$remote_addr`, never `$proxy_add_x_forwarded_for`) — and the nginx `allow`/`deny` list and `MIRROR_ALLOW_CIDRS` change in the same commit |
|
||||
| **`/healthz` stays exempt from the address gate** | The podman `HealthCmd` probes it from loopback, which the production list otherwise excludes — an exempt probe is why the container is not permanently unhealthy |
|
||||
| **No STUN/TURN by default** | A LAN tool must not announce internal addresses to a third party |
|
||||
| **Teardown is broadcast to every viewer**, not just subscribers | Tiles are drawn on `stream_added`; a non-subscribed viewer still has a stale tile to clear |
|
||||
| **Keep both `<video>` elements `muted playsinline`** | Autoplay policy on iOS/Safari otherwise blocks the wall entirely |
|
||||
|
||||
Reference in New Issue
Block a user