Repository navigation
[FR] Enrich /api/health with process info (version, uptime, startedAt, node) #1513
Description
Problem
GET /api/health (the public liveness endpoint) returns a hardcoded { "status": "ok" } regardless of what version is running, when the process started, or how long it has been up. Self-hosted operators who wire Sourcebot into a Kubernetes livenessProbe — or who simply curl the endpoint from their fleet-management scripts — have no way to verify the build they deployed is the one actually running, or to detect a restart.
This is the counterpart to the recently-added GET /api/health/ready (which checks dependency health). The liveness endpoint should at minimum report:
- The Sourcebot version string (already exposed separately at
GET /api/version). - The process start timestamp and current uptime.
- The Node.js version, platform, and process PID.
Motivation
Three concrete operator scenarios this would unblock:
- Fleet verification. An Ansible/Puppet/Salt deploy rolls out
v5.1.5to N pods. The operator needs a one-shot check that each pod reports the new version. Today they have to call/api/versionand trust it; combining it with/api/healthremoves the second hop and lets them scrape liveness + version in one call. - Restart detection. A K8s node was rebooted last night. Was the Sourcebot pod restarted? The readiness probe (which is what K8s primarily uses for restart decisions) can't tell you — but a sudden drop in
uptimeon/api/healthis an unambiguous signal. - Log correlation. A user reports a bug with a particular Sourcebot instance. Support asks "what version are you on?" The operator can paste a single
curl /api/healthresponse instead of running two commands.
Current behavior
$ curl -fsS http://localhost:3000/api/health
{"status":"ok"}
That is the entire response.
Expected behavior
$ curl -fsS http://localhost:3000/api/health
{
"status": "ok",
"version": "v5.1.4",
"startedAt": "2026-07-26T10:42:18.000Z",
"uptime": 93847,
"pid": 17,
"node": {
"version": "v24.4.1",
"platform": "linux",
"arch": "arm64"
}
}
statuscontinues to be the literal"ok"so existing K8slivenessProbeconfigs (which only check the HTTP code) keep working.versionmirrorsGET /api/versionso a single call is enough for fleet verification.startedAtis an ISO 8601 timestamp computed once at module load (cheap, no per-request work).uptimeisprocess.uptime()rounded to the nearest integer (seconds).pidand thenodeblock are the standard Node.js process facts.
The endpoint stays unauthenticated, track: false for PostHog (operator probes should not pollute analytics), and continues to bypass withAuth (with the existing // eslint-disable-next-line authz/require-auth-wrapper comment).
Proposed solution
- Capture
STARTED_AT = new Date()at module load inpackages/web/src/app/api/(server)/health/route.ts. - Capture
STARTED_AT_MS = Date.now()similarly (kept as a number foruptimemath). - Extend the
GETresponse to includeversion,startedAt,uptime,pid, and thenodeblock. - Reuse the existing
SOURCEBOT_VERSIONconstant from@sourcebot/shared. - No new dependencies. No schema migration. No new files.
Alternatives considered
- Move the version into
/api/healthand deprecate/api/version. Rejected:/api/versionis a documented public endpoint with a small but non-zero set of consumers (some scripts explicitly call it). Removing it would be a backwards-incompatible change for no real win. - Add a separate
/api/infoendpoint. Rejected:/api/healthis the natural place for "is this process running and what is it?" — splitting the info into a different endpoint adds a round-trip without a clear semantic separation. - Include the git commit SHA. Rejected for this PR: the SHA would need to be injected at build time (no env var exists today, and the runtime can't reliably read
.git/HEADfrom inside a Docker container with a working tree stripped). The follow-up would be a newSOURCEBOT_COMMIT_SHAenv var plus anext.config.jsenvblock to expose it at build time. Worth doing, but a separate change.
Scope
- Modify
packages/web/src/app/api/(server)/health/route.tsto include the new fields. - Add a new test file
packages/web/src/app/api/(server)/health/route.test.ts(or extend an existing one if present) with three to four cases: shape, version matchesSOURCEBOT_VERSION, uptime is a non-negative number, startedAt is a parseable ISO timestamp. - Update
docs/docs/api-reference/health.mdxwith the new response shape and a one-line note that/api/versionstill exists for callers that only need the version. - Add a
[Unreleased] → Addedentry inCHANGELOG.md.
Acceptance criteria
-
curl /api/healthreturns a 200 response with the new shape and the existingstatus: "ok"field. -
versionmatches the value ofSOURCEBOT_VERSIONfrom@sourcebot/shared. -
startedAtis a valid ISO 8601 string in the past. -
uptimeis a non-negative integer and increases by ~1 per second between two requests. -
node.versionmatchesprocess.version,node.platformmatchesprocess.platform,node.archmatchesprocess.arch. - The endpoint is still unauthenticated, still
track: falsefor PostHog, and still returns the existingstatusfield. - Existing K8s
livenessProbeconfigs that only check the HTTP code keep working unchanged. - Tests pass; lint clean; tsc clean in the new files.
Backward compatibility
Fully backwards compatible. The existing status: "ok" field is preserved. Consumers that only check the HTTP code are unaffected. Consumers that parse the JSON body get a superset of the previous shape (new fields, no removed fields).
Risks
- The
startedAtis computed once at module load. In a long-running process that's fine. In a serverless / edge deployment, the module might be re-loaded per request andstartedAtwould reflect only the most recent init — but Sourcebot is not deployed that way today. - The
node.versionexposes the runtime version, which is already visible via any unauthenticated error response. No new information leak.
Activity