Skip to main content

rust_supervisor/config/
configurable.rs

1//! Public configuration input model for supervisor users.
2//!
3//! The structs in this module are the single raw configuration surface used for
4//! YAML loading, template rendering, and JSON Schema generation.
5
6use confique::Config;
7use rust_config_tree::ConfigSchema;
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10use std::path::PathBuf;
11
12use crate::{
13    config::{
14        audit::AuditConfig,
15        ipc_security::IpcSecurityConfig,
16        policy::{
17            ChildStrategyOverrideConfig, DynamicSupervisorConfig, FailureWindowConfig,
18            GroupDependencyConfig, GroupStrategyConfig, GroupsConfigSection, MeltdownConfig,
19            RestartBudgetConfig, SeverityDefaultConfig, SupervisionPipelineConfig,
20        },
21    },
22    spec::{
23        child_declaration::ChildrenConfigSection,
24        supervisor::{
25            BackpressureConfig, EscalationPolicy, RECOMMENDED_CHANNEL_CAPACITY, SupervisionStrategy,
26        },
27    },
28};
29
30/// Configuration file shape loaded from YAML.
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Config, JsonSchema, ConfigSchema)]
32pub struct SupervisorConfig {
33    /// Additional configuration files included by `rust-config-tree`.
34    #[config(default = [])]
35    #[serde(default)]
36    pub include: Vec<PathBuf>,
37
38    /// Root supervisor declaration values.
39    #[config(nested)]
40    pub supervisor: SupervisorRootConfig,
41
42    /// Runtime policy values.
43    #[config(nested)]
44    #[serde(default)]
45    pub policy: PolicyConfig,
46
47    /// Shutdown budget values.
48    #[config(nested)]
49    #[serde(default)]
50    pub shutdown: ShutdownConfig,
51
52    /// Observability switches and capacities.
53    #[config(nested)]
54    #[serde(default)]
55    pub observability: ObservabilityConfig,
56
57    /// Command audit persistence configuration.
58    #[config(nested)]
59    #[serde(default)]
60    pub audit: AuditConfig,
61
62    /// Backpressure policy for observability event subscribers.
63    #[config(nested)]
64    #[serde(default)]
65    pub backpressure: BackpressureConfig,
66
67    /// Group-level restart budgets and group policy declarations.
68    #[config(nested)]
69    #[serde(default)]
70    #[schemars(extend("x-tree-split" = true, "x-tree-transparent-array" = true))]
71    pub groups: GroupsConfigSection,
72
73    /// Group-level strategy overrides.
74    #[config(default = [])]
75    #[serde(default)]
76    pub group_strategies: Vec<GroupStrategyConfig>,
77
78    /// Cross-group failure propagation dependencies.
79    #[config(default = [])]
80    #[serde(default)]
81    pub group_dependencies: Vec<GroupDependencyConfig>,
82
83    /// Child-level strategy overrides.
84    #[config(default = [])]
85    #[serde(default)]
86    pub child_strategy_overrides: Vec<ChildStrategyOverrideConfig>,
87
88    /// Default severity class per task role.
89    #[config(default = [])]
90    #[serde(default)]
91    pub severity_defaults: Vec<SeverityDefaultConfig>,
92
93    /// Optional target-side dashboard IPC configuration.
94    pub dashboard: Option<DashboardIpcConfig>,
95
96    /// Child declarations loaded from YAML.
97    #[config(nested)]
98    #[serde(default)]
99    #[schemars(extend("x-tree-split" = true, "x-tree-transparent-array" = true))]
100    pub children: ChildrenConfigSection,
101}
102
103// impl ConfigSchema for SupervisorConfig {
104//     /// Returns child configuration paths declared by one loaded layer.
105//     ///
106//     /// # Arguments
107//     ///
108//     /// - `layer`: Partially loaded supervisor configuration layer.
109//     ///
110//     /// # Returns
111//     ///
112//     /// Returns include paths declared by this configuration layer.
113//     fn include_paths(layer: &<Self as Config>::Layer) -> Vec<PathBuf> {
114//         layer.include.clone().unwrap_or_default()
115//     }
116// }
117
118/// Root supervisor configuration.
119#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Config, JsonSchema)]
120pub struct SupervisorRootConfig {
121    /// Restart scope strategy for child failures.
122    #[config(default = "OneForAll")]
123    #[serde(default = "default_supervision_strategy")]
124    pub strategy: SupervisionStrategy,
125    /// Optional fallback escalation policy used when child and group policies
126    /// do not define a more specific policy.
127    ///
128    /// Root supervisors do not have a parent supervisor at runtime, so
129    /// `escalate_to_parent` at root level is a planning and diagnostic label.
130    #[schemars(!default)]
131    #[serde(default)]
132    pub escalation_policy: Option<EscalationPolicy>,
133    /// Control command channel capacity.
134    ///
135    /// Runtime capacity for queued supervisor control commands.
136    /// Recommended default: 256.
137    #[config(default = 256)]
138    #[serde(default = "default_channel_capacity")]
139    pub control_channel_capacity: usize,
140    /// Event broadcast channel capacity.
141    ///
142    /// Runtime capacity for supervisor event broadcast delivery.
143    /// Recommended default: 256.
144    #[config(default = 256)]
145    #[serde(default = "default_channel_capacity")]
146    pub event_channel_capacity: usize,
147    /// Runtime dynamic child acceptance policy.
148    #[config(nested)]
149    #[serde(default)]
150    pub dynamic_supervisor: DynamicSupervisorConfig,
151}
152
153/// Restart, backoff, and fuse configuration.
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Config, JsonSchema)]
155pub struct PolicyConfig {
156    /// Maximum child restarts within the child restart window.
157    #[config(default = 10)]
158    #[serde(default = "default_child_restart_limit")]
159    pub child_restart_limit: u32,
160    /// Child restart window in milliseconds.
161    #[config(default = 60000)]
162    #[serde(default = "default_child_restart_window_ms")]
163    pub child_restart_window_ms: u64,
164    /// Maximum supervisor failures within the supervisor failure window.
165    #[config(default = 30)]
166    #[serde(default = "default_supervisor_failure_limit")]
167    pub supervisor_failure_limit: u32,
168    /// Supervisor failure window in milliseconds.
169    #[config(default = 60000)]
170    #[serde(default = "default_supervisor_failure_window_ms")]
171    pub supervisor_failure_window_ms: u64,
172    /// Initial backoff in milliseconds.
173    #[config(default = 100)]
174    #[serde(default = "default_initial_backoff_ms")]
175    pub initial_backoff_ms: u64,
176    /// Maximum backoff in milliseconds.
177    #[config(default = 5000)]
178    #[serde(default = "default_max_backoff_ms")]
179    pub max_backoff_ms: u64,
180    /// Jitter ratio expressed as a fraction between zero and one.
181    #[config(default = 0.10)]
182    #[serde(default = "default_jitter_ratio")]
183    pub jitter_ratio: f64,
184    /// Heartbeat interval in milliseconds.
185    #[config(default = 1000)]
186    #[serde(default = "default_heartbeat_interval_ms")]
187    pub heartbeat_interval_ms: u64,
188    /// Stale heartbeat threshold in milliseconds.
189    #[config(default = 3000)]
190    #[serde(default = "default_stale_after_ms")]
191    pub stale_after_ms: u64,
192    /// Restart budget used by the supervision pipeline.
193    #[config(nested)]
194    #[serde(default)]
195    pub restart_budget: RestartBudgetConfig,
196    /// Failure window used by the supervision pipeline.
197    #[config(nested)]
198    #[serde(default)]
199    pub failure_window: FailureWindowConfig,
200    /// Meltdown fuse limits for child, group, and supervisor scopes.
201    #[config(nested)]
202    #[serde(default)]
203    pub meltdown: MeltdownConfig,
204    /// Supervision pipeline capacities and concurrent restart limit.
205    #[config(nested)]
206    #[serde(default)]
207    pub supervision_pipeline: SupervisionPipelineConfig,
208}
209
210/// Shutdown coordination configuration.
211#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Config, JsonSchema)]
212pub struct ShutdownConfig {
213    /// Graceful drain timeout in milliseconds.
214    #[config(default = 5000)]
215    #[serde(default = "default_graceful_timeout_ms")]
216    pub graceful_timeout_ms: u64,
217    /// Abort wait timeout in milliseconds.
218    #[config(default = 1000)]
219    #[serde(default = "default_abort_wait_ms")]
220    pub abort_wait_ms: u64,
221}
222
223/// Observability configuration.
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Config, JsonSchema)]
225pub struct ObservabilityConfig {
226    /// Event journal capacity.
227    #[config(default = 256)]
228    #[serde(default = "default_event_journal_capacity")]
229    pub event_journal_capacity: usize,
230    /// Whether metrics recording is enabled.
231    #[config(default = true)]
232    #[serde(default = "default_true")]
233    pub metrics_enabled: bool,
234    /// Whether command audit recording is enabled.
235    #[config(default = true)]
236    #[serde(default = "default_true")]
237    pub audit_enabled: bool,
238}
239
240impl Default for PolicyConfig {
241    /// Returns the default runtime policy configuration.
242    fn default() -> Self {
243        Self {
244            child_restart_limit: default_child_restart_limit(),
245            child_restart_window_ms: default_child_restart_window_ms(),
246            supervisor_failure_limit: default_supervisor_failure_limit(),
247            supervisor_failure_window_ms: default_supervisor_failure_window_ms(),
248            initial_backoff_ms: default_initial_backoff_ms(),
249            max_backoff_ms: default_max_backoff_ms(),
250            jitter_ratio: default_jitter_ratio(),
251            heartbeat_interval_ms: default_heartbeat_interval_ms(),
252            stale_after_ms: default_stale_after_ms(),
253            restart_budget: RestartBudgetConfig::default(),
254            failure_window: FailureWindowConfig::default(),
255            meltdown: MeltdownConfig::default(),
256            supervision_pipeline: SupervisionPipelineConfig::default(),
257        }
258    }
259}
260
261impl Default for ShutdownConfig {
262    /// Returns the default shutdown coordination configuration.
263    fn default() -> Self {
264        Self {
265            graceful_timeout_ms: default_graceful_timeout_ms(),
266            abort_wait_ms: default_abort_wait_ms(),
267        }
268    }
269}
270
271impl Default for ObservabilityConfig {
272    /// Returns the default observability configuration.
273    fn default() -> Self {
274        Self {
275            event_journal_capacity: default_event_journal_capacity(),
276            metrics_enabled: default_true(),
277            audit_enabled: default_true(),
278        }
279    }
280}
281
282/// Returns the default supervision strategy.
283fn default_supervision_strategy() -> SupervisionStrategy {
284    SupervisionStrategy::OneForAll
285}
286
287/// Returns the default child restart limit.
288fn default_child_restart_limit() -> u32 {
289    10
290}
291
292/// Returns the default child restart window in milliseconds.
293fn default_child_restart_window_ms() -> u64 {
294    60000
295}
296
297/// Returns the default supervisor failure limit.
298fn default_supervisor_failure_limit() -> u32 {
299    30
300}
301
302/// Returns the default supervisor failure window in milliseconds.
303fn default_supervisor_failure_window_ms() -> u64 {
304    60000
305}
306
307/// Returns the default initial backoff in milliseconds.
308fn default_initial_backoff_ms() -> u64 {
309    100
310}
311
312/// Returns the default maximum backoff in milliseconds.
313fn default_max_backoff_ms() -> u64 {
314    5000
315}
316
317/// Returns the default jitter ratio.
318fn default_jitter_ratio() -> f64 {
319    0.10
320}
321
322/// Returns the default heartbeat interval in milliseconds.
323fn default_heartbeat_interval_ms() -> u64 {
324    1000
325}
326
327/// Returns the default stale heartbeat threshold in milliseconds.
328fn default_stale_after_ms() -> u64 {
329    3000
330}
331
332/// Returns the default graceful shutdown timeout in milliseconds.
333fn default_graceful_timeout_ms() -> u64 {
334    5000
335}
336
337/// Returns the default abort wait timeout in milliseconds.
338fn default_abort_wait_ms() -> u64 {
339    1000
340}
341
342/// Returns the recommended channel capacity.
343fn default_channel_capacity() -> usize {
344    RECOMMENDED_CHANNEL_CAPACITY
345}
346
347/// Returns the default event journal capacity.
348fn default_event_journal_capacity() -> usize {
349    256
350}
351
352/// Serde default helper: returns true.
353fn default_true() -> bool {
354    true
355}
356
357/// Optional target-side dashboard IPC configuration.
358#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Config, JsonSchema)]
359pub struct DashboardIpcConfig {
360    /// Whether the target process opens the local IPC endpoint.
361    pub enabled: bool,
362    /// Stable target process identifier sent to relay and UI.
363    pub target_id: Option<String>,
364    /// Local Unix domain socket path used by the target process.
365    pub path: Option<PathBuf>,
366    /// Socket file permission string such as `0600`.
367    pub permissions: Option<String>,
368    /// Socket bind behavior when the path already exists.
369    pub bind_mode: Option<DashboardIpcBindMode>,
370    /// Dynamic registration settings used after IPC is ready.
371    pub registration: Option<DashboardRegistrationConfig>,
372    /// Optional IPC security pipeline configuration (C1-C9).
373    #[schemars(!default)]
374    #[serde(default)]
375    pub security_config: Option<IpcSecurityConfig>,
376}
377
378/// Socket bind behavior for target-side dashboard IPC.
379#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
380#[serde(rename_all = "snake_case")]
381pub enum DashboardIpcBindMode {
382    /// Fail when the socket path already exists.
383    CreateNew,
384    /// Remove a stale socket path before binding.
385    ReplaceStale,
386}
387
388/// Dynamic registration settings for a target process.
389#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Config, JsonSchema)]
390pub struct DashboardRegistrationConfig {
391    /// Whether the target process registers with relay after IPC is ready.
392    pub enabled: bool,
393    /// Local relay registration socket path.
394    pub relay_registration_path: Option<PathBuf>,
395    /// Human-readable name shown in the dashboard.
396    pub display_name: Option<String>,
397    /// Registration lease duration in seconds.
398    pub lease_seconds: Option<u64>,
399    /// Registration heartbeat interval in seconds.
400    pub registration_heartbeat_interval_seconds: Option<u64>,
401    /// Timeout in seconds for connecting to the relay registration socket.
402    /// Default: 5.
403    pub registration_connect_timeout_secs: Option<u64>,
404    /// Timeout in seconds for write and ack-read on the registration socket.
405    /// Default: 5.
406    pub registration_io_timeout_secs: Option<u64>,
407}