Public: anyone can read this; the crew can change it.

An operator can see the app's state: /api/health and a boot line that names what is missing #22

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`GET /api/health` is now an action (`health` in `src/actions.ts`) that every door calls: anyone gets `{ok, env, commit, schemaVersion, uptimeSeconds}` and nothing else, and an admin gets `{identity, dataDir, databaseBytes, writable, terminal}` on top — the gate is in the action, so the JSON route, the new `health` MCP tool and anything added later cannot disagree about what a guest sees. `ok` is f…

shipped production is serving it deploy 2adbe7d

opened this

GET /api/health is now an action (health in src/actions.ts) that every door calls: anyone gets {ok, env, commit, schemaVersion, uptimeSeconds} and nothing else, and an admin gets {identity, dataDir, databaseBytes, writable, terminal} on top — the gate is in the action, so the JSON route, the new health MCP tool and anything added later cannot disagree about what a guest sees. ok is false, and the status 503, when the schema in the file is not the one this code knows, when the data directory has stopped taking writes, or when SQLite reports the file corrupt; the corruption check is a quick_check remembered for a minute per database, so a probe reads a version, two stats and nothing else — a hundred probes in a row measured 14.8 ms in total, and no probe writes.

At boot src/server.ts prints one line per real problem, from a pure warnings() in src/env.ts: an empty FORGE_APP_IDENTITY_KEY says every request is anonymous and nobody can write, a FORGE_APP_URL that is unset or http://localhost says cookies will not be Secure and same-origin checks compare against http://localhost. It names the variable and the cost, never the value, and a healthy boot adds nothing to the line it already printed. FORGE_APP_SSH is echoed exactly as given in terminal, or the word off when it is unset. The identity key itself never leaves server.ts: what travels on the request context is the fact "verified"/"disabled", not the secret.

Worth a look: the shape tests in src/server.test.ts assert the public keys exactly, for a guest and a signed-in user, and that the key string appears nowhere in an admin's answer; a second short-lived server whose schema_version row is removed answers 503. What I chose not to do: no admin page (that is #10), no row counts or usernames anywhere in the answer, no timing assertion in the test suite (a wall clock in CI is a flaky test, so the speed claim is the design — one SELECT, two stats, a cached quick_check), and I left src/tty.ts alone since the terminal has no status surface yet. No new dependencies. bun test is green (45 tests).

🤖 Generated with Claude Code

Closes #14

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs