service.rs (9972B)
1 use std::collections::BTreeMap; 2 3 use radroots_runtime_paths::ServiceId; 4 use serde::Deserialize; 5 6 /// Instance cardinality supported by a hardened service target. 7 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 8 #[serde(rename_all = "snake_case")] 9 pub enum ServiceInstanceSupport { 10 Multiple, 11 } 12 13 /// Configuration document format supported by a hardened service target. 14 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 15 #[serde(rename_all = "snake_case")] 16 pub enum ServiceConfigurationFormat { 17 Toml, 18 } 19 20 impl ServiceConfigurationFormat { 21 #[must_use] 22 pub const fn as_str(self) -> &'static str { 23 match self { 24 Self::Toml => "toml", 25 } 26 } 27 } 28 29 /// State initialization policy supported by a hardened service target. 30 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 31 #[serde(rename_all = "snake_case")] 32 pub enum ServiceStateInitialization { 33 Explicit, 34 } 35 36 /// Daemon state-open policy supported by a hardened service target. 37 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 38 #[serde(rename_all = "snake_case")] 39 pub enum ServiceRunStatePolicy { 40 ExistingOnly, 41 } 42 43 /// Detailed local-administration transport supported by a hardened service. 44 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 45 #[serde(rename_all = "snake_case")] 46 pub enum ServiceAdminTransport { 47 Http11OverUnixDomainSocket, 48 } 49 50 /// Versioned base path for detailed local administration. 51 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 52 pub enum ServiceAdminBasePath { 53 #[serde(rename = "/v1")] 54 V1, 55 } 56 57 /// Detailed status surface supported by a hardened service. 58 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 59 #[serde(rename_all = "snake_case")] 60 pub enum ServiceStatusSurface { 61 LocalAdminServiceStatusV1, 62 } 63 64 /// Public operations surface supported by a hardened service. 65 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 66 #[serde(rename_all = "snake_case")] 67 pub enum ServiceOperationsSurface { 68 CachedLivezReadyzMetrics, 69 } 70 71 impl ServiceOperationsSurface { 72 pub const ROUTES: [&str; 3] = ["/livez", "/readyz", "/metrics"]; 73 74 #[must_use] 75 pub const fn routes(self) -> [&'static str; 3] { 76 match self { 77 Self::CachedLivezReadyzMetrics => Self::ROUTES, 78 } 79 } 80 } 81 82 /// Current evidence posture for an eligible service target. 83 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 84 #[serde(rename_all = "snake_case")] 85 pub enum ServiceSupportPosture { 86 Target, 87 } 88 89 /// Exact Linux target triples eligible for future Tier-1 qualification. 90 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] 91 pub enum ServiceTier1Target { 92 #[serde(rename = "x86_64-unknown-linux-gnu")] 93 X86_64UnknownLinuxGnu, 94 #[serde(rename = "aarch64-unknown-linux-gnu")] 95 Aarch64UnknownLinuxGnu, 96 } 97 98 impl ServiceTier1Target { 99 pub const ALL: [Self; 2] = [Self::X86_64UnknownLinuxGnu, Self::Aarch64UnknownLinuxGnu]; 100 101 #[must_use] 102 pub const fn as_str(self) -> &'static str { 103 match self { 104 Self::X86_64UnknownLinuxGnu => "x86_64-unknown-linux-gnu", 105 Self::Aarch64UnknownLinuxGnu => "aarch64-unknown-linux-gnu", 106 } 107 } 108 109 pub(crate) fn parse(value: &str) -> Option<Self> { 110 Self::ALL 111 .into_iter() 112 .find(|target| target.as_str() == value) 113 } 114 } 115 116 /// Closed metadata for one hardened standalone service target. 117 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] 118 #[serde(try_from = "HardenedServiceTargetWire")] 119 pub struct HardenedServiceTarget { 120 service_id: ServiceId, 121 instance_support: ServiceInstanceSupport, 122 config_format: ServiceConfigurationFormat, 123 state_initialization: ServiceStateInitialization, 124 run_state_policy: ServiceRunStatePolicy, 125 admin_transport: ServiceAdminTransport, 126 admin_base_path: ServiceAdminBasePath, 127 admin_contract_version: u32, 128 status_surface: ServiceStatusSurface, 129 operations_surface: ServiceOperationsSurface, 130 support_posture: ServiceSupportPosture, 131 tier_1_targets: Vec<ServiceTier1Target>, 132 } 133 134 #[derive(Deserialize)] 135 #[serde(deny_unknown_fields)] 136 struct HardenedServiceTargetWire { 137 service_id: ServiceId, 138 instance_support: ServiceInstanceSupport, 139 config_format: ServiceConfigurationFormat, 140 state_initialization: ServiceStateInitialization, 141 run_state_policy: ServiceRunStatePolicy, 142 admin_transport: ServiceAdminTransport, 143 admin_base_path: ServiceAdminBasePath, 144 admin_contract_version: u32, 145 status_surface: ServiceStatusSurface, 146 operations_surface: ServiceOperationsSurface, 147 support_posture: ServiceSupportPosture, 148 tier_1_targets: Vec<ServiceTier1Target>, 149 } 150 151 impl HardenedServiceTarget { 152 #[must_use] 153 pub fn service_id(&self) -> &ServiceId { 154 &self.service_id 155 } 156 157 #[must_use] 158 pub const fn instance_support(&self) -> ServiceInstanceSupport { 159 self.instance_support 160 } 161 162 #[must_use] 163 pub const fn config_format(&self) -> ServiceConfigurationFormat { 164 self.config_format 165 } 166 167 #[must_use] 168 pub const fn state_initialization(&self) -> ServiceStateInitialization { 169 self.state_initialization 170 } 171 172 #[must_use] 173 pub const fn run_state_policy(&self) -> ServiceRunStatePolicy { 174 self.run_state_policy 175 } 176 177 #[must_use] 178 pub const fn admin_transport(&self) -> ServiceAdminTransport { 179 self.admin_transport 180 } 181 182 #[must_use] 183 pub const fn admin_base_path(&self) -> ServiceAdminBasePath { 184 self.admin_base_path 185 } 186 187 #[must_use] 188 pub const fn admin_contract_version(&self) -> u32 { 189 self.admin_contract_version 190 } 191 192 #[must_use] 193 pub const fn status_surface(&self) -> ServiceStatusSurface { 194 self.status_surface 195 } 196 197 #[must_use] 198 pub const fn operations_surface(&self) -> ServiceOperationsSurface { 199 self.operations_surface 200 } 201 202 #[must_use] 203 pub const fn support_posture(&self) -> ServiceSupportPosture { 204 self.support_posture 205 } 206 207 #[must_use] 208 pub fn tier_1_targets(&self) -> &[ServiceTier1Target] { 209 &self.tier_1_targets 210 } 211 212 fn has_exact_common_contract(&self) -> bool { 213 [ 214 self.instance_support == ServiceInstanceSupport::Multiple, 215 self.config_format == ServiceConfigurationFormat::Toml, 216 self.state_initialization == ServiceStateInitialization::Explicit, 217 self.run_state_policy == ServiceRunStatePolicy::ExistingOnly, 218 self.admin_transport == ServiceAdminTransport::Http11OverUnixDomainSocket, 219 self.admin_base_path == ServiceAdminBasePath::V1, 220 self.admin_contract_version == 1, 221 self.status_surface == ServiceStatusSurface::LocalAdminServiceStatusV1, 222 self.operations_surface == ServiceOperationsSurface::CachedLivezReadyzMetrics, 223 self.support_posture == ServiceSupportPosture::Target, 224 self.tier_1_targets == ServiceTier1Target::ALL, 225 ] 226 .into_iter() 227 .all(core::convert::identity) 228 } 229 } 230 231 impl TryFrom<HardenedServiceTargetWire> for HardenedServiceTarget { 232 type Error = &'static str; 233 234 fn try_from(wire: HardenedServiceTargetWire) -> Result<Self, Self::Error> { 235 let target = Self { 236 service_id: wire.service_id, 237 instance_support: wire.instance_support, 238 config_format: wire.config_format, 239 state_initialization: wire.state_initialization, 240 run_state_policy: wire.run_state_policy, 241 admin_transport: wire.admin_transport, 242 admin_base_path: wire.admin_base_path, 243 admin_contract_version: wire.admin_contract_version, 244 status_surface: wire.status_surface, 245 operations_surface: wire.operations_surface, 246 support_posture: wire.support_posture, 247 tier_1_targets: wire.tier_1_targets, 248 }; 249 if !matches!(target.service_id.as_str(), "myc" | "rhi") 250 || !target.has_exact_common_contract() 251 { 252 return Err("hardened service target does not match the v1 contract"); 253 } 254 Ok(target) 255 } 256 } 257 258 /// Validated exact Myc/RHI service-target inventory. 259 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] 260 #[serde(try_from = "BTreeMap<String, HardenedServiceTarget>")] 261 pub struct HardenedServiceTargets(BTreeMap<String, HardenedServiceTarget>); 262 263 impl HardenedServiceTargets { 264 #[must_use] 265 pub fn get(&self, service_id: &ServiceId) -> Option<&HardenedServiceTarget> { 266 self.0.get(service_id.as_str()) 267 } 268 269 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &HardenedServiceTarget)> { 270 self.0.iter().map(|(key, value)| (key.as_str(), value)) 271 } 272 273 #[must_use] 274 pub fn len(&self) -> usize { 275 self.0.len() 276 } 277 278 #[must_use] 279 pub fn is_empty(&self) -> bool { 280 self.0.is_empty() 281 } 282 } 283 284 impl TryFrom<BTreeMap<String, HardenedServiceTarget>> for HardenedServiceTargets { 285 type Error = &'static str; 286 287 fn try_from(targets: BTreeMap<String, HardenedServiceTarget>) -> Result<Self, Self::Error> { 288 const REQUIRED_SERVICES: [&str; 2] = ["myc", "rhi"]; 289 290 if targets.len() != REQUIRED_SERVICES.len() { 291 return Err("hardened service target inventory must contain exactly Myc and RHI"); 292 } 293 for service in REQUIRED_SERVICES { 294 let Some(target) = targets.get(service) else { 295 return Err("hardened service target inventory is incomplete"); 296 }; 297 if target.service_id.as_str() != service || !target.has_exact_common_contract() { 298 return Err("hardened service target metadata does not match the v1 contract"); 299 } 300 } 301 if targets 302 .iter() 303 .any(|(key, target)| key != target.service_id.as_str()) 304 { 305 return Err("hardened service target key does not match its service identifier"); 306 } 307 308 Ok(Self(targets)) 309 } 310 }