diff --git a/packages/server/src/server/auth.ts b/packages/server/src/server/auth.ts index 10cbb5911..2247597f9 100644 --- a/packages/server/src/server/auth.ts +++ b/packages/server/src/server/auth.ts @@ -122,6 +122,5 @@ export function shouldBypassBearerAuth(method: string, path: string): boolean { if (method === "OPTIONS") { return true; } - // Public liveness/version endpoints used by local supervisors and health probes. - return path === "/api/health" || path === "/api/status"; + return path === "/api/health"; } diff --git a/packages/server/src/server/bootstrap-auth.test.ts b/packages/server/src/server/bootstrap-auth.test.ts index 91e25f19a..a8d98ef4a 100644 --- a/packages/server/src/server/bootstrap-auth.test.ts +++ b/packages/server/src/server/bootstrap-auth.test.ts @@ -97,7 +97,7 @@ describe("daemon bearer auth", () => { expect(health.status).toBe(200); const status = await fetch(`http://127.0.0.1:${daemonHandle.port}/api/status`); - expect(status.status).toBe(200); + expect(status.status).toBe(401); } finally { await daemonHandle.close(); } diff --git a/public-docs/configuration.md b/public-docs/configuration.md index addae4457..e242de7c2 100644 --- a/public-docs/configuration.md +++ b/public-docs/configuration.md @@ -91,7 +91,7 @@ Legacy fields `log.level` and `log.format` are still supported and map to the ne ## Password authentication -You can require a password to connect to the daemon. When set, all HTTP and WebSocket clients must authenticate — except the health and status endpoints (`/api/health`, `/api/status`). +You can require a password to connect to the daemon. When set, all HTTP and WebSocket clients must authenticate. Only the `/api/health` liveness endpoint is exempt, so that process supervisors and load balancers can probe without credentials. The easiest way to set a password is with the CLI: diff --git a/public-docs/security.md b/public-docs/security.md index d1e2067d3..7f8a7dc0e 100644 --- a/public-docs/security.md +++ b/public-docs/security.md @@ -89,7 +89,7 @@ Configure via `daemon.hostnames` in `config.json`: By default, anyone who can reach the daemon's listening address can connect. On localhost this is fine — only local processes have access. But if you bind to a network interface (e.g. your LAN IP or `0.0.0.0`), or if you don't fully trust your local network, you can require a password. -When a password is configured, all HTTP requests must include an `Authorization: Bearer ` header and all WebSocket connections must authenticate via subprotocol. Unauthenticated requests receive a `401 Unauthorized` response. The `/api/health` and `/api/status` endpoints are exempt so that health probes and process supervisors continue to work without credentials. +When a password is configured, all HTTP requests must include an `Authorization: Bearer ` header and all WebSocket connections must authenticate via subprotocol. Unauthenticated requests receive a `401 Unauthorized` response. Only the `/api/health` liveness endpoint is exempt, so that process supervisors and load balancers can probe without credentials. The password is stored as a bcrypt hash in `config.json` — the daemon never stores it in plaintext. See [Configuration](/docs/configuration#password-authentication) for setup instructions.