From 3acfd972d6c75c003de5b0287cd02a796c89fa6d Mon Sep 17 00:00:00 2001 From: ciregenz Date: Sun, 16 Aug 2026 20:07:37 -0700 Subject: [PATCH] [eric] backend: real /api/health answers the reachability prober, ending the 401 console spam on every probe tick --- backend/main.py | 7 +++++++ backend/tests/test_health_route.py | 21 +++++++++++++++++++++ frontend/src/shared/config.ts | 7 ++++--- 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 backend/tests/test_health_route.py diff --git a/backend/main.py b/backend/main.py index 6826eec4..cd20a869 100644 --- a/backend/main.py +++ b/backend/main.py @@ -393,6 +393,13 @@ async def websocket_electron_main(websocket: WebSocket): ws_manager.disconnect_main(websocket) +@app.get("/api/health") +async def health() -> dict: + """Auth-exempt liveness for the frontend's reachability prober; it used to probe `/` and the + 401 answer spammed the renderer console on every probe tick.""" + return {"ok": True} + + @app.get("/api/dev/token") async def dev_token(): """Hand the per-install token to the dev frontend, which has no Electron diff --git a/backend/tests/test_health_route.py b/backend/tests/test_health_route.py new file mode 100644 index 00000000..d8293aa1 --- /dev/null +++ b/backend/tests/test_health_route.py @@ -0,0 +1,21 @@ +"""The reachability prober needs one route that answers 200 WITHOUT auth: probing `/` returned 401 +and Chromium logged every probe tick as a console error (field report 2026-08-16). Both directions +pinned: /api/health is open, and the auth wall still stands one path over.""" + +from fastapi.testclient import TestClient + +from backend.main import app + +client = TestClient(app) + + +def test_health_answers_200_without_any_token(): + r = client.get("/api/health") + assert r.status_code == 200 + assert r.json() == {"ok": True} + + +def test_health_is_the_exception_not_the_rule(): + # Negative control: a real API path without a token must still 401, or the exemption leaked. + r = client.get("/api/agents/sessions") + assert r.status_code == 401 diff --git a/frontend/src/shared/config.ts b/frontend/src/shared/config.ts index c0aec765..10f00feb 100644 --- a/frontend/src/shared/config.ts +++ b/frontend/src/shared/config.ts @@ -85,9 +85,10 @@ function _installAuthFetchInterceptor() { (window as any).__OPENSWARM_FETCH_PATCHED__ = true; const originalFetch = window.fetch.bind(window); - // Reachability probe uses the RAW fetch: any HTTP response (401 included) proves the backend - // is back, and it must never recurse into the retry/dedupe logic below. - setBackendProber(() => originalFetch(`http://${host}:${port}/`, { signal: AbortSignal.timeout(2000), cache: 'no-store' })); + // Reachability probe uses the RAW fetch: any HTTP response proves the backend is back, and it + // must never recurse into the retry/dedupe logic below. /api/health is auth-exempt and 200s; + // probing `/` answered 401 and Chromium logged every probe tick as a console error. + setBackendProber(() => originalFetch(`http://${host}:${port}/api/health`, { signal: AbortSignal.timeout(2000), cache: 'no-store' })); // Loopback calls answer in ms; anything past this is a dead/wedged backend, and an unbounded // hang here is exactly the silent forever-spinner class (ENG-241). Generous enough for a big