feat(api): add unauthenticated health endpoints
CI / verify (pull_request) Successful in 45s
CI / infrastructure-smoke (pull_request) Successful in 12s
CI / migration-smoke (pull_request) Successful in 1m42s

Add GET /api/v1/health/live and GET /api/v1/health/ready per ADR 0044.

- live returns a fixed 200 {"status":"live"} whenever the process serves.
- ready returns 200 {"status":"ready"} only when module startup has
  completed (and shutdown has not begun) and a database ping succeeds
  within 2s; otherwise a fixed 503 {"code":"NOT_READY"}.
- The ping runs on the shared pool as one simple-protocol message with
  a transaction-local statement_timeout, so PostgreSQL cancels a stalled
  statement; the pool discards the connection on failure.
- At most one ping is ever unsettled: probes share its outcome and, once
  its deadline passes, answer 503 immediately until it settles.
- Both routes set Cache-Control: no-store, accept GET only (405 with
  Allow: GET otherwise, HEAD included), never resolve a principal, and
  are mounted in app.ts beside, never inside, /api/v1/modules/.
- createApiApp gains a required `health` option; startApi supplies it
  and clears module readiness when shutdown begins.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ekklesia
2026-10-01 16:47:01 -05:00
co-authored by Claude Opus 5.5
parent d593c10f1d
commit aaec9789c4
18 changed files with 727 additions and 0 deletions
+23
View File
@@ -25,6 +25,7 @@ import {
} from "../src/app.js";
import { AUTH_REQUEST_MAX_BODY_BYTES } from "../src/body-limits.js";
import { createInMemoryDashboardStore } from "./support/in-memory-dashboard-store.js";
import { readyHealth } from "./support/ready-health.js";
import { emptyModuleConfiguration } from "./support/in-memory-module-configuration.js";
import { emptyModuleLifecycle } from "./support/in-memory-module-lifecycle.js";
import { emptyModuleStorage } from "./support/in-memory-module-storage.js";
@@ -104,6 +105,7 @@ function createAuthWiringApp(runtime: EkklesiaIdentityRuntime) {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -121,6 +123,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -355,6 +358,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -378,6 +382,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -403,6 +408,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -430,6 +436,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -457,6 +464,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -481,6 +489,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -507,6 +516,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -530,6 +540,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -555,6 +566,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -587,6 +599,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -620,6 +633,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime,
@@ -658,6 +672,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime: fakeRuntime(),
@@ -678,6 +693,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime: fakeRuntime(),
@@ -700,6 +716,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime: fakeRuntime(),
@@ -721,6 +738,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime: fakeRuntime(),
@@ -740,6 +758,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime: fakeRuntime(),
@@ -766,6 +785,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime: fakeRuntime(),
@@ -795,6 +815,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({ evaluator: { authorize: evaluatorAuthorize } }),
runtime: fakeRuntime(),
@@ -827,6 +848,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({
evaluator: { authorize: evaluatorAuthorize },
@@ -850,6 +872,7 @@ describe("Ekklesia API Hono application", () => {
moduleConfiguration: emptyModuleConfiguration(),
moduleStorage: emptyModuleStorage(),
dashboard: createInMemoryDashboardStore(),
health: readyHealth(),
moduleServices: fakeModuleServices(),
authorization: fakeAuthorization({}),
runtime: fakeRuntime(),