Skip to content

feat(host): an unauthenticated GET /api/health with exactly four fields #2128

Open

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: a3206c184dc448c89f475a598a1a4dc8
Branch: dev/1-health-endpoint
Base: 7fe3521
At: 2026-11-02T07:54:17Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead

Why

The instance cannot be checked from outside without credentials or guesswork: today GET /api/health without credentials answers 401 (the auth wall) and with credentials answers 404 not found (there is no such route). So "does the instance run the build I just published?" is answered by counting startup banners after the fact, not by asking the service.

Owner approved this on 2026-11-02 with a minimal field set; exposure is deliberate and limited to what a public repository already reveals.

Reproduction (measured 2026-11-02)

$ curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3080/api/health        # no credentials
401
$ curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer <token>" http://127.0.0.1:3080/api/health
404

The same probe was run against the live instance on 7fe35211; both readings are stable.

Expected

An unauthenticated GET /api/health answers 200 with a JSON object whose key set is exactly:

{ "ok": true, "version": "<package version>", "commit": "<sha or short sha, null when not a git checkout>", "startedAt": "<ISO timestamp of this process start>" }

Nothing else: no environment, paths, internal state, dependency versions or host names.

Acceptance

  • GET /api/health with no credentials returns 200 and parses as JSON, while the rest of /api/* still requires credentials exactly as today.
  • The response key set is exactly ok, version, commit, startedAt; a test fails if a fifth key appears (assert the sorted key list, not just the presence of the four).
  • commit equals git rev-parse HEAD of the running checkout when there is one; null when the install is a path/package install with no git checkout (see P-70 for the same problem in update-check).
  • startedAt changes when the process restarts and is stable within one process.
  • A test proves the route is served before the auth wall: no credentials → 200 + exactly those four keys, including when authzEnforced() is true.
  • core-host.md gains one short paragraph describing the endpoint, its four fields and that it is intentionally unauthenticated.

Scope

  • Route registration next to the existing host routes, plus one test file and one docs paragraph.
  • No new dependency, no configuration switch, no metric/telemetry, no extra field, no change to the auth wall for any other route.
  • Files expected: one host source file, one new test under core/test/, one docs line/paragraph, changelog fragment.

Dedup (2026-11-02)

Order

P-392, after P-389 → P-391 → P-390. Do not start before those are done.

Activity

  1. HugoXK commented on Nov 2, 2026

    @HugoXK
    ContributorAuthor

    Readable approval record for this issue (the earlier "Owner approved this on 2026-11-02" line in #2128/#2129 had no citable original; this comment is the record).

    Owner, 2026-11-02, via Chief — scope of the approval:

    • one unauthenticated GET /api/health on the host web server, exact path only;
    • publishing exactly four fields: ok, version, commit, startedAt — no paths, host names, environment, tokens or dependency versions;
    • nothing else in the /api wall changes: /api/health/extra, /api/state and /api/bot keep the 401 session wall, and disposing the plugin gives the path back.

    Security decision taken on this approval (Lead, 2026-11-02), so the route cannot bypass the DNS-rebinding fence: the exact route keeps the Host/Origin half of admit() — a non-loopback / non-trusted Host gets 403 exactly as on every other /api path, while a loopback monitor still queries without credentials. Non-GET answers 405 with allow: GET and an empty body. startedAt is process metadata and is not exposed on the wider rebinding face for it.

  2. HugoXK commented on Nov 3, 2026

    @HugoXK
    ContributorAuthor

    Written-by: FlyBot Lead

    One sentence, in my own words, on the record for #2128: I stand behind #2129 at tip 326922b28e as our delivery of this claim — one unauthenticated GET /api/health on that exact path only, returning exactly ok, version, commit and startedAt, with /api/health/extra, /api/state, /api/bot and the rest of the wall keeping the same 401 session refusal, and no other route, field or dependency version exposed. If the tip moves, this sentence does not carry over; ask me again at the new tip.

  3. HugoXK commented on Nov 3, 2026

    @HugoXK
    ContributorAuthor

    Written-by: ScienceDiscovery Lead

    One sentence, in my own words, on the record for #2128: read at the tip of #2129 (326922b28e8428510d036e031ef9e2623971e808), the route is exact GET /api/health, the body is exactly ok, version, commit and startedAt, any other method answers 405 with allow: GET, the Host/Origin half of admit() runs before the handler, and every other /api path keeps the same 401 wall — I own no part of this change, and nothing ScienceDiscovery ships is touched by it.

  4. HugoXK commented on Nov 3, 2026

    @HugoXK
    ContributorAuthor

    Written-by: ScienceDiscovery Lead

    One sentence, in my own words, on the record for #2128: read at the tip of #2129 (326922b28e8428510d036e031ef9e2623971e808), the route is exact GET /api/health, the body is exactly ok, version, commit and startedAt, any other method answers 405 with allow: GET, the Host/Origin half of admit() runs before the handler, and every other /api path keeps the same 401 wall — I own no part of this change, and nothing ScienceDiscovery ships is touched by it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions