Skip to content

Add Actuator-style health and build-information endpoints #77

Description

@Subhransu-De

Context

The application currently exposes only GET /health. It is a process-only liveness check that returns {"status":"up"} without querying PostgreSQL or another dependency. There is no distinct readiness endpoint, Actuator-style index/info contract, or build-provenance endpoint.

This issue defines the complete initial operational endpoint contract. It complements, but is broader than, #18 (health split) and #19 (observability strategy).

Endpoint contract

Endpoint name Description What it should do Success response Failure response
GET /actuator/health/liveness Process liveness probe. Return successfully only when the FastAPI process can serve an HTTP request. Do not call PostgreSQL, Keycloak/JWKS, Logfire, or any network dependency. It is intended for a container orchestrator restart decision. HTTP 200
{"status":"UP"}
HTTP 503
{"status":"DOWN"}

A hung process will normally fail by probe timeout or connection failure rather than returning JSON.
GET /actuator/health/readiness Traffic-admission probe. Check PostgreSQL with SELECT 1. Check the resolved OIDC JWKS URI with a short timeout and require a valid JSON document with a non-empty keys array. Do not check Redis, migrations, or Logfire: Redis is not used, migrations are an external pre-start task, and Logfire export is optional. HTTP 200
{"status":"UP","checks":{"database":{"status":"UP"},"identity_provider":{"status":"UP"}}}
HTTP 503
{"status":"DOWN","checks":{"database":{"status":"DOWN","reason":"unavailable"},"identity_provider":{"status":"UP"}}}

Use stable non-sensitive reason codes only: unavailable, timeout, or invalid_response. Never expose raw exceptions, URLs, credentials, or connection details.
GET /actuator Operational endpoint index. Return links only; do not duplicate health data. HTTP 200
{"_links":{"self":{"href":"/actuator"},"liveness":{"href":"/actuator/health/liveness"},"readiness":{"href":"/actuator/health/readiness"},"info":{"href":"/actuator/info"}}}
No endpoint-specific failure. An unexpected error uses the existing HTTP 500 application/problem+json response.
GET /actuator/info Immutable deployed-service and build identity. Return the application distribution name and version from installed Python package metadata. Return CI-injected build revision and creation time. Do not return runtime health, environment variables, credentials, or host data. HTTP 200
{"service":{"name":"fastapi-template","version":"0.1.0"},"build":{"revision":"<full-git-sha>","created":"<RFC-3339-UTC>"}}
No endpoint-specific failure. An unexpected error uses the existing HTTP 500 application/problem+json response.

Metadata sources of truth

Value Source of truth Delivery to the running application
Service name [project].name in pyproject.toml Installed distribution metadata via importlib.metadata
Application version [project].version in pyproject.toml Installed distribution metadata via importlib.metadata
Build revision Exact Git SHA checked out by CI OCI org.opencontainers.image.revision label and an image ENV mirror for the endpoint
Build creation time CI-generated UTC timestamp at image build OCI org.opencontainers.image.created label and an image ENV mirror for the endpoint

requires-python = ">=3.13" is an interpreter compatibility constraint, not the application version. The runtime currently cannot read package metadata because this project is not installed as a distribution with discoverable dist-info; add an explicit build backend and install the application into the image before using importlib.metadata.

Out of scope

  • Do not implement GET /actuator/health; readiness is the single aggregate availability decision for this template.
  • Do not implement GET /actuator/health/startup; FastAPI lifespan completes OIDC discovery before the app accepts requests, so Kubernetes can use liveness as its startup probe.
  • Do not implement /actuator/metrics or /actuator/metrics/{metric_name} until a concrete metrics registry/export contract exists. Current Logfire/OpenTelemetry setup provides instrumentation but no app-owned metric catalogue.

Acceptance criteria

  • All four endpoints above exist and have the documented status codes, response bodies, and no request body.
  • Liveness has no database, OIDC, telemetry, or other external dependency call.
  • Readiness checks PostgreSQL and the resolved OIDC JWKS document with bounded timeouts.
  • Readiness failure responses reveal only stable reason codes, never implementation-sensitive diagnostics.
  • The app is packaged and installed so importlib.metadata is the only runtime source for service name and application version; remove the duplicate ApplicationSettings.app_name source.
  • CI supplies full Git SHA and RFC 3339 UTC build time once per image build; the image records standard OCI version, revision, and created labels and exposes the latter two to the application without requiring Docker socket access.
  • Existing GET /health becomes an explicitly documented temporary compatibility alias for liveness, or is intentionally removed in a breaking release. Update scenario tests to wait on readiness, and update test expectations, telemetry exclusion, access-log filtering, Docker/Compose health configuration, and documentation accordingly.
  • Probe endpoints are unauthenticated but documented as network-restricted to the orchestrator; /actuator and /actuator/info have an explicit operator access policy.
  • Unit and integration tests cover success and each dependency-failure path; scenario tests wait on readiness.
  • README documents endpoint semantics, consumers, status codes, response schema, access policy, and Kubernetes/ECS probe configuration.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions