0008. Public health check, authenticated status
- Status: Accepted
- Date: 2026-09-30
Context
In Phase 0,GET /api/health was public and returned the backend version, whether
trading was enabled, and the geoblock result with country and region. The Phase 0
security review (finding F20) noted that anyone who can reach the server could learn:
- the software version, which helps target known flaws;
- whether live trading is on, which marks the host as worth attacking;
- where the server is, from the geoblock country and region.
Decision
GET /api/healthstays public and returns only{"status": "ok" | "degraded"}with HTTP 200. When the backend reportsdegradedis defined by implementation.GET /api/statusrequires a session and returnsversion,trading_enabled,geoblockandfeeds. It is aGET, so it needs no CSRF token.- The same
Statusobject is sent on the authenticatedstatusWebSocket topic. - No other public endpoint returns version, trading state, location, account or feed details.
/api/health.
Consequences
- An unauthenticated visitor learns only that the service is up and whether it is degraded.
- Liveness checks keep working without credentials. A check must read the body to tell
okfromdegraded, because both return 200. - Checking the geoblock result or
trading_enabledneeds a login: through the UI’s status indicator, or by opening/api/statusin the logged-in browser.curlagainst/api/healthno longer shows them. - The UI reads status from the WebSocket topic and the REST endpoint, which serve the same state.
Alternatives considered
- Keep all details on the public endpoint. Rejected: finding F20.
- Require a session for
/healthas well. Rejected. Liveness checks would need a credential, and a stored session for a monitor is a new secret to protect. - Show details only to requests from localhost. Rejected. Behind the reverse proxy every request arrives from localhost, so the details would be public again; the backend does not trust proxy headers to find the real client.