### Release history Closed-beta builds were tagged on GHCR under `2.0.0-dev` during development; each beta below carries only the bullets new to that build. The **Breaking Changes** above and the **Technical details** further down apply to the dev cycle as a whole, not to any individual beta. ### [2.0.0-beta18] - 2026-07-03 - Deeper security Audit after python-socketio CVE Pre-cut hardening pass. A functional + security audit of the 2.0-Rework branch produced 27 confirmed findings across auth, playback control, socket DoS surface, binge / library / resume flows, and admin/config bifurcation; every one landed as a fix in this beta. Nothing in here is a new feature -- this beta closes the gap between "everything works" and "we can put a version number on it and walk away". The bulk of the impact is in three places: closing paths that let a spectator drive party-wide playback, replacing the ephemeral per-process session secret with a real env-loaded one, and shrinking the socket DoS surface to something that survives contact with a curious user. ##### Breaking Changes (beta18-specific) - **`SESSION_SECRET` must be set in `.env`.** The signing key for the party-bound session cookie was previously `secrets.token_hex(32)` at module load, which meant every process restart invalidated every existing cookie AND under `--workers >1` each worker signed with its own key so session state was non-deterministic per request. Now loaded from env; when empty an ephemeral key is generated with a loud warning at boot. Generate once with `openssl rand -hex 32` and set as `SESSION_SECRET=...` in `.env`. Existing deployments that skip this step keep running (dev-mode fallback) but every restart kicks everyone out of their party with a 401. - **`SESSION_COOKIE_SECURE=true` for HTTPS deployments.** New env toggle (default `false`). When `true` the session cookie carries the `Secure` flag so browsers only send it over HTTPS -- closes the plaintext-leak vector on the party-bound cookie. Local dev leaves it `false` so cookies still work over `http://localhost`; production behind TLS should always set `true`. - **`CORS_ALLOWED_ORIGINS` env var replaces the hardcoded `*` for Socket.IO.** Comma-separated origin allowlist (`https://foo.example.com,https://bar.example.com`) or the historical `*` fallback for backwards compat. Pinning to real origins in production means cross-origin XHR polling from unrelated pages can't open sockets against your server. - **Session cookie name changed** from Starlette's default `session` to `ewp_session`. All existing party cookies stop verifying on upgrade; every current user gets re-prompted to join their party. Post-upgrade sessions persist across restarts as long as `SESSION_SECRET` is stable. No config action required, but expect a "why did everyone drop out?" moment during the rollout. - **HLS stream URL no longer carries `api_key=`.** External tools that scraped a full HLS URL from a party and expected to hit Emby directly using the embedded key no longer work; the URL is only meaningful as a request to the WatchParty proxy from a session-cookie'd browser. Prior behavior leaked the admin `EMBY_API_KEY` to every viewer through `