lean_ctx/core/intelligence_runtime/
configuration.rs1use std::path::Path;
5
6use crate::core::config::{Config, IntelligenceRuntimeConfig};
7
8use super::{InstallError, Result, health};
9
10pub const DISCLOSURE: &str = "Optional Intelligence Runtime: proprietary, runs locally. Its commercial features are licensed through LeanCTX | Hive or a commercial licence; the public reference runtime works without either. Staging packages are not production releases.";
11
12pub struct SetupRuntime {
14 config: IntelligenceRuntimeConfig,
15 observed_config: IntelligenceRuntimeConfig,
16 version: Option<String>,
17}
18
19impl SetupRuntime {
20 pub fn available(&self) -> bool {
21 self.version.is_some()
22 }
23
24 pub fn channel_available(&self) -> bool {
25 !self.config.channel_url.is_empty()
26 }
27
28 pub fn enabled(&self) -> bool {
29 self.available() && self.config.enabled && self.config.accept_proprietary
30 }
31
32 pub fn status(&self) -> &'static str {
33 if self.enabled() {
34 "configured"
35 } else if self.available() {
36 "available"
37 } else if self.channel_available() {
38 if self.config.manifest_sha256.is_empty() {
39 "download_available"
40 } else {
41 "repair_available"
42 }
43 } else {
44 "not_configured"
45 }
46 }
47
48 pub(crate) fn report(&self) -> serde_json::Value {
49 let mut report = serde_json::json!({"schema_version": 1, "status": self.status(),
58 "version": self.version, "disclosure": DISCLOSURE,
59 "semantic_detectors": "not_configured",
60 "semantic_descriptor_sha256_prefix": null,
61 "semantic_versions": [], "semantic_categories": [],
62 "runtime_started": false, "release_approved": false});
63 if self.enabled() {
64 let authenticated = super::trust_key(&self.config.trust_key_hex)
65 .and_then(|key| {
66 super::install::verified_active(
67 Path::new(&self.config.root),
68 &self.config.manifest_sha256,
69 &key,
70 )
71 })
72 .ok();
73 if let Some(package) = authenticated {
74 let state = super::install::semantic_state(&package);
75 let versions = super::install::semantic_set(&package)
76 .map_or_else(Vec::new, |set| set.versions);
77 for (key, value) in health::semantic_report(&package, &state, versions) {
78 report[key] = value;
79 }
80 } else {
81 report["semantic_detectors"] = "unverified".into();
82 }
83 }
84 for (key, value) in health::semantic_worker_report(&self.config) {
85 report[key] = value;
86 }
87 report
88 }
89
90 pub fn enable(&self) -> Result<serde_json::Value> {
92 if !self.available() {
93 return Err(InstallError::State);
94 }
95 let _guard = super::install::configuration_guard(Path::new(&self.config.root))?;
96 activate_checked(
97 Path::new(&self.config.root),
98 &self.config.manifest_sha256,
99 &self.config.trust_key_hex,
100 Some(self),
101 )
102 }
103
104 pub fn install_and_enable(&self) -> Result<serde_json::Value> {
106 if !self.channel_available() {
107 return Err(InstallError::Policy);
108 }
109 if global_runtime()? != self.observed_config {
110 return Err(InstallError::Configuration);
111 }
112 let root = Path::new(&self.config.root);
113 if self.config != self.observed_config {
114 super::install::prepare_parent_chain(root.parent().ok_or(InstallError::Directory)?)?;
115 }
116 let _guard = super::install::configuration_guard(root)?;
117 if global_runtime()? != self.observed_config {
118 return Err(InstallError::Configuration);
119 }
120 let expected = super::install::active_manifest(root)?;
121 let result = super::catalog::sync(
122 [&self.config.channel_url, &self.config.channel_signature_url],
123 &super::trust_key(&self.config.channel_root_key_hex)?,
124 root,
125 &expected,
126 )?;
127 let selected = result["receipt"]["manifest_sha256"]
128 .as_str()
129 .ok_or(InstallError::State)?;
130 let key = result["artifact_key_hex"]
131 .as_str()
132 .ok_or(InstallError::State)?;
133 let activation = activate_checked(root, selected, key, Some(self))?;
134 Ok(
135 serde_json::json!({"status": "installed_and_activated", "installation": result,
136 "activation": activation, "release_approved": false}),
137 )
138 }
139
140 pub(crate) fn rollback_and_enable(&self) -> Result<serde_json::Value> {
142 if self.config.manifest_sha256.is_empty() || self.config.staging {
143 return Err(InstallError::Policy);
144 }
145 let root = Path::new(&self.config.root);
146 let _guard = super::install::configuration_guard(root)?;
147 if global_runtime()? != self.observed_config {
148 return Err(InstallError::Configuration);
149 }
150 let (result, key) =
151 super::install::rollback_configured(root, &self.config.manifest_sha256)?;
152 let selected = result["receipt"]["manifest_sha256"]
153 .as_str()
154 .ok_or(InstallError::State)?;
155 let activation = activate_checked(root, selected, &key, Some(self))?;
159 Ok(
160 serde_json::json!({"status": "rolled_back_and_activated", "rollback": result,
161 "activation": activation, "release_approved": false}),
162 )
163 }
164}
165
166pub fn inspect_setup() -> Result<SetupRuntime> {
167 let observed_config = global_runtime()?;
169 let config = if observed_config == IntelligenceRuntimeConfig::default() {
170 super::bootstrap::config()?
171 } else {
172 observed_config.clone()
173 };
174 let version = if config == IntelligenceRuntimeConfig::default() {
175 None
176 } else {
177 if !super::bootstrap::permits_config(&config) {
178 return Err(InstallError::Policy);
179 }
180 let has_channel = !config.channel_url.is_empty()
181 || !config.channel_signature_url.is_empty()
182 || !config.channel_root_key_hex.is_empty();
183 if has_channel {
184 super::channel::validate_urls(&[&config.channel_url, &config.channel_signature_url])?;
185 super::trust_key(&config.channel_root_key_hex)?;
186 if !Path::new(&config.root).is_absolute() {
187 return Err(InstallError::Directory);
188 }
189 }
190 if config.manifest_sha256.is_empty() && config.trust_key_hex.is_empty() {
191 if !has_channel || config.enabled || config.accept_proprietary {
192 return Err(InstallError::Policy);
193 }
194 None
195 } else {
196 let key = super::trust_key(&config.trust_key_hex)?;
197 match super::install::verified_active(
198 Path::new(&config.root),
199 &config.manifest_sha256,
200 &key,
201 ) {
202 Ok(package) => Some(package.receipt.version),
203 Err(_) if has_channel => None,
206 Err(error) => return Err(error),
207 }
208 }
209 };
210 Ok(SetupRuntime {
211 config,
212 observed_config,
213 version,
214 })
215}
216
217fn global_runtime() -> Result<IntelligenceRuntimeConfig> {
218 Config::try_load_global()
219 .map(|config| config.intelligence_runtime)
220 .map_err(|_| InstallError::Configuration)
221}
222
223pub(super) fn activate(root: &Path, selected: &str, encoded: &str) -> Result<serde_json::Value> {
224 activate_checked(root, selected, encoded, None)
225}
226
227fn activate_checked(
228 root: &Path,
229 selected: &str,
230 encoded: &str,
231 expected: Option<&SetupRuntime>,
232) -> Result<serde_json::Value> {
233 let observed = global_runtime()?;
234 if expected.is_some_and(|expected| observed != expected.observed_config) {
235 return Err(InstallError::Configuration);
236 }
237 let key = super::trust_key(encoded)?;
238 let checked = health::check(root, selected, &key)?;
239 let staging = checked["receipt"]["staging_only"]
240 .as_bool()
241 .ok_or(InstallError::State)?;
242 let production = super::bootstrap::production_key(root)?.is_some();
243 if staging == production {
244 return Err(InstallError::Policy);
245 }
246 let previous = if production {
247 let mut compiled = super::bootstrap::config()?;
248 if !super::bootstrap::permits_config(&compiled) {
249 return Err(InstallError::Policy);
250 }
251 compiled.license_configuration = expected
252 .map(|expected| expected.config.license_configuration.clone())
253 .or_else(|| {
254 (!observed.staging && super::bootstrap::permits_config(&observed))
255 .then(|| observed.license_configuration.clone())
256 })
257 .unwrap_or_default();
258 if observed.root == compiled.root && super::bootstrap::permits_config(&observed) {
260 observed.clone()
261 } else {
262 compiled
263 }
264 } else if let Some(expected) = expected {
265 expected.config.clone()
266 } else if Path::new(&observed.root) == root {
267 observed.clone()
268 } else {
269 IntelligenceRuntimeConfig::default()
270 };
271 let root = root.to_str().ok_or(InstallError::Directory)?.to_owned();
272 Config::try_update_global(|config| {
273 if config.intelligence_runtime != observed {
274 return Err(crate::core::error::LeanCtxError::Config(
275 "runtime setup configuration changed".into(),
276 ));
277 }
278 config.intelligence_runtime = IntelligenceRuntimeConfig {
279 enabled: true,
280 accept_proprietary: true,
281 staging,
282 root,
283 manifest_sha256: selected.to_owned(),
284 trust_key_hex: encoded.to_owned(),
285 ..previous
286 };
287 Ok(())
288 })
289 .map_err(|_| InstallError::Configuration)?;
290 Ok(serde_json::json!({"status": "activated", "health": checked,
291 "staging_only": staging, "release_approved": false}))
292}
293
294pub(super) fn deactivate() -> Result<serde_json::Value> {
295 Config::update_global(|config| config.intelligence_runtime.enabled = false)
296 .map_err(|_| InstallError::Configuration)?;
297 Ok(serde_json::json!({"status": "disabled", "release_approved": false}))
298}