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