From c1f4a5f9ee6f34a53b1a70962c6dbce630db9059 Mon Sep 17 00:00:00 2001
From: Raudur Refur <261796072+refur-nfn@users.noreply.github.com>
Date: Wed, 1 Jul 2026 12:12:38 +0300
Subject: [PATCH] feat(toolkit): add REST healthcheck infrastructure
- Concurrent healthcheck execution with timeout protection
- HealthcheckRegistry for registering and running per-gear probes
- Message sanitization to prevent secret leaks in responses
- API Gateway integration for /health and /readyz endpoints
- Comprehensive test coverage with panic/timeout/degradation scenarios
Signed-off-by: Raudur Refur <261796072+refur-nfn@users.noreply.github.com>
---
gears/system/api-gateway/src/config.rs | 31 +-
gears/system/api-gateway/src/gear.rs | 182 ++++-
gears/system/api-gateway/src/web.rs | 50 +-
.../api-gateway/tests/access_log_tests.rs | 6 +-
.../api-gateway/tests/auth_middleware.rs | 54 +-
.../api-gateway/tests/body_limit_tests.rs | 18 +-
gears/system/api-gateway/tests/cors_tests.rs | 18 +-
.../api-gateway/tests/health_endpoints.rs | 264 +++++++
.../api-gateway/tests/http_metrics_tests.rs | 36 +-
.../api-gateway/tests/license_middleware.rs | 30 +-
.../api-gateway/tests/middleware_order.rs | 12 +-
.../api-gateway/tests/rate_limit_tests.rs | 18 +-
libs/toolkit/src/contracts.rs | 30 +-
libs/toolkit/src/healthcheck.rs | 642 ++++++++++++++++++
libs/toolkit/src/lib.rs | 8 +
libs/toolkit/src/registry.rs | 2 +
libs/toolkit/src/runtime/host_runtime.rs | 34 +-
libs/toolkit/tests/macro_tests.rs | 2 +
18 files changed, 1344 insertions(+), 93 deletions(-)
create mode 100644 gears/system/api-gateway/tests/health_endpoints.rs
create mode 100644 libs/toolkit/src/healthcheck.rs
diff --git a/gears/system/api-gateway/src/config.rs b/gears/system/api-gateway/src/config.rs
index 0ca959f4f..176a45bb3 100644
--- a/gears/system/api-gateway/src/config.rs
+++ b/gears/system/api-gateway/src/config.rs
@@ -8,8 +8,12 @@ fn default_body_limit_bytes() -> usize {
16 * 1024 * 1024
}
+fn default_healthcheck_timeout_ms() -> u64 {
+ 500
+}
+
/// API gateway configuration - reused from `api_gateway` gear
-#[derive(Debug, Clone, Deserialize, Serialize, Default)]
+#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
#[allow(clippy::struct_excessive_bools)]
pub struct ApiGatewayConfig {
@@ -57,6 +61,31 @@ pub struct ApiGatewayConfig {
/// HTTP metrics configuration.
#[serde(default)]
pub metrics: MetricsConfig,
+
+ /// Per-check `/health`/`/readyz` timeout (ms); raise for slow dependencies.
+ #[serde(default = "default_healthcheck_timeout_ms")]
+ pub healthcheck_timeout_ms: u64,
+}
+
+impl Default for ApiGatewayConfig {
+ // Manual (not derived) so defaults match the `#[serde(default = ...)]` fns above;
+ // a derived Default would zero each field (e.g. timeout 0 = instant probe failure).
+ fn default() -> Self {
+ Self {
+ bind_addr: String::default(),
+ enable_docs: false,
+ cors_enabled: false,
+ cors: None,
+ openapi: OpenApiConfig::default(),
+ defaults: Defaults::default(),
+ auth_disabled: false,
+ require_auth_by_default: default_require_auth_by_default(),
+ prefix_path: String::default(),
+ route_policies: RoutePoliciesConfig::default(),
+ metrics: MetricsConfig::default(),
+ healthcheck_timeout_ms: default_healthcheck_timeout_ms(),
+ }
+ }
}
#[derive(Debug, Clone, Deserialize, Serialize)]
diff --git a/gears/system/api-gateway/src/gear.rs b/gears/system/api-gateway/src/gear.rs
index 402bbe34b..4b760e847 100644
--- a/gears/system/api-gateway/src/gear.rs
+++ b/gears/system/api-gateway/src/gear.rs
@@ -3,7 +3,7 @@
//! Contains the `ApiGateway` gear struct and its trait implementations.
use async_trait::async_trait;
-use std::sync::Arc;
+use std::sync::{Arc, OnceLock};
use arc_swap::ArcSwap;
use dashmap::DashMap;
@@ -12,12 +12,12 @@ use anyhow::Result;
use axum::error_handling::HandleErrorLayer;
use axum::http::Method;
use axum::middleware::from_fn_with_state;
-use axum::{Router, extract::DefaultBodyLimit, middleware::from_fn, routing::get};
+use axum::{Extension, Router, extract::DefaultBodyLimit, middleware::from_fn, routing::get};
use parking_lot::Mutex;
use std::net::SocketAddr;
use std::time::Duration;
use tokio_util::sync::CancellationToken;
-use toolkit::api::{OpenApiRegistry, OpenApiRegistryImpl};
+use toolkit::api::{OpenApiRegistry, OpenApiRegistryImpl, OperationBuilder};
use toolkit::lifecycle::ReadySignal;
use tower::{BoxError, ServiceBuilder};
use tower_http::{
@@ -77,6 +77,13 @@ pub struct ApiGateway {
pub(crate) final_router: Mutex