Skip to content

feat(api): answer GET /api/health without a key - #10771

Merged
diegosouzapw merged 3 commits into
diegosouzapw:release/v3.8.50from
maxmad64bis:feat/health-root-endpoint
Aug 20, 2026
Merged

diegosouzapw merged 3 commits into
diegosouzapw:release/v3.8.50from
maxmad64bis:feat/health-root-endpoint

Conversation

@maxmad64bis

Copy link
Copy Markdown
Contributor

⚠️ base-red inherited: #9985

Summary

GET /api/health has no route today, so it falls through to the /api/* catch-all — and the
management-auth boundary answers before routing. Measured on a live 3.8.50 instance:

Request No key With admin key
GET /api/health 401 AUTH_001 Authentication required 404 unknown_route
GET /api/health/ping 200 200
GET /api/nonexistent-xyz 401 404 unknown_route

An unauthenticated probe gets the exact same 401 as a route that doesn't exist — it can't tell
"service down" from "bad credentials" from "no such route". That's the ambiguity the #6424
catch-all removed for authenticated callers, still intact for the one caller that never
authenticates. /api/health/ping already answers without a key, but it isn't the path any
orchestrator tries first.

The new route is deliberately minimal — { status, timestamp } — since everything it returns is
public on an exposed instance. Version, uptime and memory stay behind the authenticated
/api/monitoring/health.

The public-route allowlist gets an exact-match set rather than a new prefix entry:
startsWith("/api/health") would also expose /api/health/degradation, which is authenticated
today. The exact-match check reuses the file's existing pathMatchesExactRoute() helper (already
used for the Cloud API routes), so a trailing slash on the probe still resolves correctly.

Related Issues

Validation

  • Change type: routing (API)
  • Focused tests: node --test tests/unit/health-root-public-liveness.test.ts (4/4),
    npm run test:vitest (368/368)
  • npm run lint
  • Reconciled with release/v3.8.50
  • Production-code changes include a new automated test in this PR

Tests Added Or Updated

  • tests/unit/health-root-public-liveness.test.ts (new)

Coverage Notes

Touches src/app/api/health/route.ts (new) and src/shared/constants/publicApiRoutes.ts. Both
covered directly by the new test file.

Reviewer Notes

No auth, no version/uptime/memory in the body — intentional, see Summary.

maxmad64bis and others added 2 commits August 19, 2026 19:41
The path had no route, so it fell through to the /api/* catch-all — and the
management-auth boundary answers before routing. An unauthenticated probe got a
401, which is exactly what a wrong or missing key returns, so a Docker HEALTHCHECK
or a Kubernetes probe could not tell "service down" from "bad credentials" from
"no such route". That is the ambiguity diegosouzapw#6424 removed for authenticated callers and
left intact for the one caller that never authenticates.

The response is deliberately minimal — { status, timestamp } — because everything
this returns is public on an exposed instance. Version, uptime and memory stay
behind the authenticated /api/monitoring/health.

The public-route allowlist gets an exact-match set rather than a new prefix entry:
startsWith("/api/health") would also have exposed /api/health/degradation, which
is authenticated today.
The exact-path check used a raw Set.has() lookup instead of the file's own
pathMatchesExactRoute() helper (already used by PUBLIC_CLOUD_API_ROUTES),
which tolerates a trailing slash.

getRequestPathname() (src/shared/utils/apiAuth.ts) does not strip a trailing
slash — unlike classify.ts's normalizePathname() — so a probe hitting
/api/health/ reached isPublicApiRoute with the slash intact, missed the exact
match, and fell through to auth: the same ambiguous 401 this route exists to
avoid.
@diegosouzapw
diegosouzapw merged commit 49a47cb into diegosouzapw:release/v3.8.50 Aug 20, 2026
5 checks passed
@maxmad64bis
maxmad64bis deleted the feat/health-root-endpoint branch September 24, 2026 21:11
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants