using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace DodoSSH.Api.Setup; /// /// Health check wiring. /// /// /// The split between liveness and readiness is deliberate and load-bearing for the /// relay: /healthz/live checks the process and nothing else, so a transient /// PostgreSQL outage cannot cause the orchestrator to restart the container and /// guillotine every live SSH session. Dependency checks belong in /// /healthz/ready, which only removes the instance from load balancing. /// internal static class HealthChecks { /// Tag for checks that gate readiness (dependencies). internal const string ReadyTag = "ready"; /// Tag for checks that gate startup completion. internal const string StartupTag = "startup"; internal static IServiceCollection AddDodoHealthChecks(this IServiceCollection services) { services.AddHealthChecks(); // Dependency checks are registered by the milestone that introduces the // dependency, each tagged ReadyTag: // M1 — PostgreSQL, OIDC discovery + JWKS reachability, pending migrations // M4 — Data Protection key ring readability return services; } internal static WebApplication MapDodoHealthChecks(this WebApplication app) { // Liveness: process is running and the pipeline responds. No dependencies. app.MapHealthChecks("/healthz/live", new HealthCheckOptions { Predicate = _ => false, }).AllowAnonymous(); // Readiness: safe to route traffic here. app.MapHealthChecks("/healthz/ready", new HealthCheckOptions { Predicate = registration => registration.Tags.Contains(ReadyTag), }).AllowAnonymous(); // Startup: one-time initialisation finished (K8s startupProbe). app.MapHealthChecks("/healthz/startup", new HealthCheckOptions { Predicate = registration => registration.Tags.Contains(StartupTag), }).AllowAnonymous(); return app; } }