Skip to main content

lean_ctx/core/context_kernel/
kernel_config.rs

1//! Central runtime configuration for Context Kernel features.
2
3use std::sync::{Mutex, MutexGuard, OnceLock};
4
5/// Runtime feature toggles for the Context Kernel.
6#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
7pub struct KernelFeatures {
8    /// Master switch: enable kernel processing.
9    pub enabled: bool,
10    /// Record ETPAO metrics for proxy requests.
11    pub proxy_etpao: bool,
12    /// Record ETPAO metrics for MCP tool calls.
13    pub mcp_etpao: bool,
14    /// Apply content dedup to repeated reads.
15    pub content_dedup: bool,
16    /// Optimize tool schemas before sending to clients.
17    pub schema_optimization: bool,
18    /// Record receipt chain entries.
19    pub receipt_chain: bool,
20    /// Record to usage normalizer.
21    pub usage_tracking: bool,
22    /// Record identity attribution.
23    pub identity_tracking: bool,
24    /// Maximum kernel budget (tokens) per request.
25    pub max_kernel_budget: usize,
26    /// Content dedup cache capacity.
27    pub dedup_capacity: usize,
28}
29
30impl Default for KernelFeatures {
31    fn default() -> Self {
32        Self {
33            enabled: true,
34            proxy_etpao: true,
35            mcp_etpao: true,
36            content_dedup: true,
37            schema_optimization: true,
38            receipt_chain: true,
39            usage_tracking: true,
40            identity_tracking: true,
41            max_kernel_budget: 150,
42            dedup_capacity: 1024,
43        }
44    }
45}
46
47static FEATURES: OnceLock<Mutex<KernelFeatures>> = OnceLock::new();
48
49fn feature_guard() -> MutexGuard<'static, KernelFeatures> {
50    FEATURES
51        .get_or_init(|| Mutex::new(KernelFeatures::default()))
52        .lock()
53        .unwrap_or_else(std::sync::PoisonError::into_inner)
54}
55
56fn env_bool(name: &str, default: bool) -> bool {
57    match std::env::var(name) {
58        Ok(value) => match value.trim().to_ascii_lowercase().as_str() {
59            "1" | "true" | "yes" | "on" => true,
60            "0" | "false" | "no" | "off" => false,
61            _ => default,
62        },
63        Err(_) => default,
64    }
65}
66
67fn env_usize(name: &str, default: usize) -> usize {
68    std::env::var(name)
69        .ok()
70        .and_then(|value| value.trim().parse().ok())
71        .unwrap_or(default)
72}
73
74/// Returns a snapshot of the current kernel feature configuration.
75#[must_use]
76pub fn features() -> KernelFeatures {
77    feature_guard().clone()
78}
79
80/// Replaces the current kernel feature configuration.
81pub fn update_features(features: KernelFeatures) {
82    *feature_guard() = features;
83}
84
85/// Returns whether Context Kernel processing is enabled.
86#[must_use]
87pub fn is_enabled() -> bool {
88    feature_guard().enabled
89}
90
91/// Returns whether a named feature and the master kernel switch are enabled.
92#[must_use]
93pub fn is_feature_enabled(feature: &str) -> bool {
94    let features = feature_guard();
95    features.enabled
96        && match feature {
97            "enabled" => true,
98            "proxy_etpao" => features.proxy_etpao,
99            "mcp_etpao" => features.mcp_etpao,
100            "content_dedup" => features.content_dedup,
101            "schema_optimization" => features.schema_optimization,
102            "receipt_chain" => features.receipt_chain,
103            "usage_tracking" => features.usage_tracking,
104            "identity_tracking" => features.identity_tracking,
105            _ => false,
106        }
107}
108
109/// Restores the runtime feature configuration to its defaults.
110pub fn reset_features() {
111    update_features(KernelFeatures::default());
112}
113
114/// Builds kernel feature configuration from supported environment variables.
115#[must_use]
116pub fn from_env() -> KernelFeatures {
117    let defaults = KernelFeatures::default();
118    KernelFeatures {
119        enabled: env_bool("LEAN_CTX_KERNEL_ENABLED", defaults.enabled),
120        content_dedup: env_bool("LEAN_CTX_KERNEL_DEDUP", defaults.content_dedup),
121        schema_optimization: env_bool("LEAN_CTX_KERNEL_SCHEMA_OPT", defaults.schema_optimization),
122        max_kernel_budget: env_usize("LEAN_CTX_KERNEL_MAX_BUDGET", defaults.max_kernel_budget),
123        ..defaults
124    }
125}
126
127/// Global test lock for all kernel modules sharing global state.
128#[cfg(test)]
129pub static KERNEL_TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
130#[cfg(test)]
131mod tests {
132    use super::{
133        KernelFeatures, features, from_env, is_enabled, is_feature_enabled, reset_features,
134        update_features,
135    };
136    use std::sync::MutexGuard;
137
138    fn setup() -> MutexGuard<'static, ()> {
139        let guard = super::KERNEL_TEST_LOCK
140            .lock()
141            .unwrap_or_else(std::sync::PoisonError::into_inner);
142        reset_features();
143        guard
144    }
145
146    #[test]
147    fn default_all_enabled() {
148        let _guard = setup();
149        let features = KernelFeatures::default();
150        assert!(features.enabled);
151        assert!(features.proxy_etpao);
152        assert!(features.mcp_etpao);
153        assert!(features.content_dedup);
154        assert!(features.schema_optimization);
155        assert!(features.receipt_chain);
156        assert!(features.usage_tracking);
157        assert!(features.identity_tracking);
158    }
159
160    #[test]
161    fn update_persists() {
162        let _guard = setup();
163        let changed = KernelFeatures {
164            content_dedup: false,
165            max_kernel_budget: 77,
166            ..KernelFeatures::default()
167        };
168        update_features(changed);
169        let current = features();
170        assert!(!current.content_dedup);
171        assert_eq!(current.max_kernel_budget, 77);
172    }
173
174    #[test]
175    fn is_enabled_master_switch() {
176        let _guard = setup();
177        let changed = KernelFeatures {
178            enabled: false,
179            dedup_capacity: 1,
180            ..KernelFeatures::default()
181        };
182        update_features(changed);
183        assert!(!is_enabled());
184        assert!(!is_feature_enabled("content_dedup"));
185    }
186
187    #[test]
188    fn is_feature_by_name() {
189        let _guard = setup();
190        assert!(is_feature_enabled("content_dedup"));
191    }
192
193    #[test]
194    fn disabled_named_feature_is_false() {
195        let _guard = setup();
196        let changed = KernelFeatures {
197            schema_optimization: false,
198            ..KernelFeatures::default()
199        };
200        update_features(changed);
201        assert!(!is_feature_enabled("schema_optimization"));
202    }
203
204    #[test]
205    fn unknown_feature_false() {
206        let _guard = setup();
207        assert!(!is_feature_enabled("nonexistent"));
208    }
209
210    #[test]
211    fn from_env_respects_vars() {
212        let _guard = setup();
213        let _env_guard = crate::core::data_dir::test_env_lock();
214        let old_enabled = std::env::var_os("LEAN_CTX_KERNEL_ENABLED");
215        let old_budget = std::env::var_os("LEAN_CTX_KERNEL_MAX_BUDGET");
216        crate::test_env::set_var("LEAN_CTX_KERNEL_ENABLED", "false");
217        crate::test_env::set_var("LEAN_CTX_KERNEL_MAX_BUDGET", "321");
218
219        let configured = from_env();
220
221        match old_enabled {
222            Some(value) => crate::test_env::set_var("LEAN_CTX_KERNEL_ENABLED", value),
223            None => crate::test_env::remove_var("LEAN_CTX_KERNEL_ENABLED"),
224        }
225        match old_budget {
226            Some(value) => crate::test_env::set_var("LEAN_CTX_KERNEL_MAX_BUDGET", value),
227            None => crate::test_env::remove_var("LEAN_CTX_KERNEL_MAX_BUDGET"),
228        }
229        assert!(!configured.enabled);
230        assert_eq!(configured.max_kernel_budget, 321);
231    }
232
233    #[test]
234    fn reset_restores_defaults() {
235        let _guard = setup();
236        let changed = KernelFeatures {
237            enabled: false,
238            dedup_capacity: 1,
239            ..KernelFeatures::default()
240        };
241        update_features(changed);
242        reset_features();
243        let current = features();
244        assert!(current.enabled);
245        assert_eq!(current.dedup_capacity, 1024);
246    }
247}