Repository navigation
feat(api): answer GET /api/health without a key - #10771
Merged
diegosouzapw merged 3 commits intoAug 20, 2026
Merged
diegosouzapw merged 3 commits into
diegosouzapw merged 3 commits into
Conversation
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.
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!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GET /api/healthhas no route today, so it falls through to the/api/*catch-all — and themanagement-auth boundary answers before routing. Measured on a live 3.8.50 instance:
GET /api/healthAUTH_001 Authentication requiredunknown_routeGET /api/health/pingGET /api/nonexistent-xyzunknown_routeAn 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/pingalready answers without a key, but it isn't the path anyorchestrator tries first.
The new route is deliberately minimal —
{ status, timestamp }— since everything it returns ispublic 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 authenticatedtoday. The exact-match check reuses the file's existing
pathMatchesExactRoute()helper (alreadyused for the Cloud API routes), so a trailing slash on the probe still resolves correctly.
Related Issues
wanted.
Validation
node --test tests/unit/health-root-public-liveness.test.ts(4/4),npm run test:vitest(368/368)npm run lintrelease/v3.8.50Tests Added Or Updated
tests/unit/health-root-public-liveness.test.ts(new)Coverage Notes
Touches
src/app/api/health/route.ts(new) andsrc/shared/constants/publicApiRoutes.ts. Bothcovered directly by the new test file.
Reviewer Notes
No auth, no version/uptime/memory in the body — intentional, see Summary.