diff --git a/client/index.html b/client/index.html
index 70ba252..6ad308e 100644
--- a/client/index.html
+++ b/client/index.html
@@ -67,10 +67,6 @@
aria-label="Chat Grid, press question mark for help."
>
diff --git a/client/public/version.js b/client/public/version.js
index 501b97c..292448b 100644
--- a/client/public/version.js
+++ b/client/public/version.js
@@ -1,6 +1,6 @@
// Maintainer-controlled web client version metadata.
window.CHGRID_RELEASE_VERSION = "0.1.0";
-window.CHGRID_BUILD_REVISION = "R345";
+window.CHGRID_BUILD_REVISION = "R346";
window.CHGRID_WEB_VERSION = `${window.CHGRID_RELEASE_VERSION} ${window.CHGRID_BUILD_REVISION}`;
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
window.CHGRID_TIME_ZONE = "America/Detroit";
diff --git a/security_best_practices_report.md b/security_best_practices_report.md
index 5752141..05bf9d1 100644
--- a/security_best_practices_report.md
+++ b/security_best_practices_report.md
@@ -2,76 +2,28 @@
## Executive Summary
-This report originally identified three issues: cross-site WebSocket session hijacking, radio metadata SSRF, and an open cross-origin media proxy. All three have now been addressed in code with exact-origin enforcement via `CHGRID_HOST_ORIGIN`, SSRF-safe radio URL validation, and same-origin-only access for the PHP media proxy.
+I did a final security pass on the current alpha codebase after the recent auth, origin, proxy, and base-path changes. I do not see any remaining critical or high-severity blockers that should stop a public alpha release.
-## Critical Findings
+The previously identified major issues are now addressed:
+- WebSocket cookie resume is gated by exact host origin checks.
+- Radio metadata fetching validates public URLs and revalidates redirects.
+- The PHP media proxy now requires the configured app origin and a valid authenticated session.
+- Auth helper endpoints now support normal same-origin browser requests without weakening the origin boundary for cross-site callers.
-### SEC-001: Cross-site WebSocket hijacking via cookie-based session resume without Origin validation
+## Current Assessment
-Impact: A malicious website can open a WebSocket to the Chat Grid server from a victim's browser, inherit the victim's session cookie, and perform authenticated actions as that user.
+### No Critical or High Findings
-Evidence:
-- The server reads the session token directly from the WebSocket handshake cookie in [server.py](/home/jjm/code/chgrid/server/app/server.py#L329) and [server.py](/home/jjm/code/chgrid/server/app/server.py#L339).
-- The WebSocket client is auto-authenticated from that cookie before any application message is sent in [server.py](/home/jjm/code/chgrid/server/app/server.py#L1387) through [server.py](/home/jjm/code/chgrid/server/app/server.py#L1392).
-- There is no `Origin` validation in the handshake path around [server.py](/home/jjm/code/chgrid/server/app/server.py#L1380).
+I do not currently see a critical or high-severity issue in the shipped Python server, TypeScript client, or PHP media proxy based on this code audit.
-Why this matters:
-- Browser WebSockets are not protected by normal same-origin read restrictions once the server accepts the connection.
-- Because the app uses cookie-based session resume, a third-party origin can potentially establish an authenticated socket unless the server rejects unexpected origins.
+### Lower-Risk Notes
-Implemented fix:
-- The server now requires `CHGRID_HOST_ORIGIN` and passes that exact origin into the WebSocket handshake allowlist.
-- Browser sockets with missing or mismatched `Origin` are rejected before the application session resume path runs.
+1. DNS rebinding / TOCTOU remains a residual hardening concern for any design that validates DNS first and then opens network connections later. The current Python and PHP outbound URL checks are substantially better than before and are reasonable for this alpha.
+2. The frontend still uses `localStorage` for non-secret preferences and UI hints. I did not find any remaining secret/session token storage in browser-accessible storage.
+3. The current client/tooling dependency tree was improved earlier in this session. Remaining risk appears limited to lower-priority dev-tooling exposure rather than deployed runtime code.
-## High Findings
+## Recommendation
-### SEC-002: Radio metadata polling creates server-side request forgery from user-controlled `streamUrl`
+From a code-security perspective, this project looks acceptable for a public alpha release.
-Impact: An authenticated user who can create or edit a radio item can cause the server to fetch attacker-chosen URLs, including internal network targets.
-
-Evidence:
-- Radio item validation accepts `streamUrl` with only length normalization in [validator.py](/home/jjm/code/chgrid/server/app/items/types/radio_station/validator.py#L11) through [validator.py](/home/jjm/code/chgrid/server/app/items/types/radio_station/validator.py#L18).
-- The server later fetches that URL with `urllib.request.urlopen` in [server.py](/home/jjm/code/chgrid/server/app/server.py#L673) through [server.py](/home/jjm/code/chgrid/server/app/server.py#L700).
-- Any enabled radio with a listener in range is polled in [server.py](/home/jjm/code/chgrid/server/app/server.py#L703) through [server.py](/home/jjm/code/chgrid/server/app/server.py#L717).
-
-Why this matters:
-- This is a classic SSRF primitive.
-- A user does not need browser access to the target; the server makes the request.
-- Internal services, metadata endpoints, localhost services, or other private hosts are not blocked here.
-
-Implemented fix:
-- Radio `streamUrl` values are now validated server-side as public `http`/`https` URLs before they are saved.
-- Metadata polling revalidates and manually follows redirect hops so a safe initial URL cannot redirect into a blocked target.
-
-## Medium Findings
-
-### SEC-003: Optional PHP media proxy is an open cross-origin proxy when no allowlist is configured
-
-Impact: If deployed as-is without `CHGRID_MEDIA_PROXY_ALLOWLIST`, third parties can use the site as a public cross-origin proxy to arbitrary public hosts.
-
-Evidence:
-- The proxy explicitly enables wildcard CORS in [media_proxy.php](/home/jjm/code/chgrid/deploy/php/media_proxy.php#L380) through [media_proxy.php](/home/jjm/code/chgrid/deploy/php/media_proxy.php#L382).
-- It accepts a user-supplied `url` parameter in [media_proxy.php](/home/jjm/code/chgrid/deploy/php/media_proxy.php#L393) through [media_proxy.php](/home/jjm/code/chgrid/deploy/php/media_proxy.php#L401).
-- The hostname allowlist is optional, not required, in [media_proxy.php](/home/jjm/code/chgrid/deploy/php/media_proxy.php#L399) through [media_proxy.php](/home/jjm/code/chgrid/deploy/php/media_proxy.php#L401).
-- Redirect resolution and streaming then proceed for the target URL in [media_proxy.php](/home/jjm/code/chgrid/deploy/php/media_proxy.php#L320) through [media_proxy.php](/home/jjm/code/chgrid/deploy/php/media_proxy.php#L377).
-
-Why this matters:
-- Even with private/reserved IP blocking, this can still be abused for bandwidth consumption, origin laundering, and relaying requests to arbitrary public endpoints.
-- Because CORS is `*`, other websites can read proxy responses directly from browsers.
-
-Implemented fix:
-- The proxy now requires `CHGRID_HOST_ORIGIN`.
-- `Access-Control-Allow-Origin` is restricted to that exact origin, and mismatched request origins are rejected.
-- Upstream media hosts remain unrestricted, preserving the intended relay behavior for arbitrary public media sources.
-
-## Lower-Risk Notes
-
-- I did not find obvious client-side DOM XSS in the current UI paths I sampled. The app mostly builds DOM with `textContent` and node creation, and the `innerHTML` uses I saw were clearing containers rather than injecting untrusted markup.
-- The client no longer stores session tokens in `localStorage`; session persistence is now moved to an `HttpOnly` cookie path, which is the right direction.
-- TLS posture appears intentionally gated by config, and I did not treat local insecure WebSocket support as a finding.
-
-## Residual Risks / Gaps
-
-- I did not perform dependency CVE triage across `npm` and Python package locks in this pass.
-- I did not run server-side tests or dynamic attack simulations; this was a code audit.
-- The PHP proxy is deploy-time optional, but it is part of the repo and worth treating as production-facing if used.
+If you want one future hardening item after the alpha goes public, I would prioritize continued dependency maintenance and periodic review of the optional PHP proxy surface.