commit 1871733edd8c81a1a2f907cafb1fae1ef270fdfc
parent 1db1a0d7047463c39ceb7b6cc4a551d3ed1abd7b
Author: triesap <tyson@radroots.org>
Date: Tue, 11 Aug 2026 09:12:13 +0000
runtime-distribution: define hardened service targets
- freeze exact metadata-only Myc and RHI target contracts
- require explicit instance contexts and keep manager inspection non-I/O
- reject Myc and RHI artifact rows until distribution authority lands
- add durable fixtures, boundary tests, and breaking-contract guidance
Diffstat:
21 files changed, 1124 insertions(+), 448 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
@@ -167,6 +167,14 @@ Before editing code:
names are validated single path components, and ordinary manager errors and
`Debug` output must not expose filesystem paths, file contents, or raw
dependency-owned causes.
+- Runtime-distribution and runtime-management service metadata is the sealed
+ exact Myc/RHI v1 inventory. Both services support multiple validated
+ instances, one TOML config, explicit initialization with existing-only run,
+ detailed HTTP/1.1-over-Unix local administration, cached
+ `/livez`/`readyz`/`metrics`, and only Linux x86_64/aarch64 Tier-1 eligibility
+ in `target` posture. This metadata does not authorize service registration,
+ PID/config/log probing, lifecycle actions, artifact names, channels, archive
+ resolution, or a `qualified` support claim.
- Library code must not initialize a tracing subscriber, parse a process CLI,
read service configuration from environment variables, install signal
handlers, create a Tokio runtime, call `process::exit`, or spawn arbitrary
diff --git a/Cargo.lock b/Cargo.lock
@@ -3788,6 +3788,7 @@ dependencies = [
name = "radroots_runtime_distribution"
version = "0.1.0-alpha"
dependencies = [
+ "radroots_runtime_paths",
"serde",
"thiserror 1.0.69",
"toml 0.8.23",
@@ -3798,6 +3799,7 @@ name = "radroots_runtime_manager"
version = "0.1.0-alpha"
dependencies = [
"flate2",
+ "radroots_runtime_distribution",
"radroots_runtime_paths",
"serde",
"tar",
diff --git a/crates/runtime_distribution/Cargo.toml b/crates/runtime_distribution/Cargo.toml
@@ -13,6 +13,7 @@ documentation = "https://docs.rs/radroots_runtime_distribution"
readme = "README"
[dependencies]
+radroots_runtime_paths = { workspace = true }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
toml = { workspace = true }
diff --git a/crates/runtime_distribution/README b/crates/runtime_distribution/README
@@ -9,8 +9,15 @@ distribution contract resolution for the `radroots` core libraries.
archive formats;
* a schema constant and resolver for selecting a matching runtime artifact;
* request and resolved-artifact types used by bootstrap and installer code;
+ * an exact, typed Myc/RHI service-target inventory for multiple-instance TOML
+ services with explicit existing-state startup, Unix local administration,
+ cached operations endpoints, and Linux x86_64/aarch64 Tier-1 eligibility;
* TOML-backed contract handling for modular runtime deployment metadata.
+The hardened service-target inventory is metadata-only. It deliberately does
+not define Myc or RHI binaries, packages, archives, channels, artifact names,
+or qualified support claims.
+
## Copyright
Except as otherwise noted, all files in the `radroots_runtime_distribution`
diff --git a/crates/runtime_distribution/src/error.rs b/crates/runtime_distribution/src/error.rs
@@ -1,51 +1,37 @@
use thiserror::Error;
-#[derive(Debug, Error, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)]
pub enum RadrootsRuntimeDistributionError {
- #[error("parse runtime distribution contract: {0}")]
- Parse(String),
- #[error("runtime distribution schema `{found}` does not match `{expected}`")]
- UnexpectedSchema {
- expected: &'static str,
- found: String,
- },
- #[error("runtime `{0}` not found in distribution contract")]
- UnknownRuntime(String),
- #[error("runtime `{0}` is not installable through the local runtime distribution contract")]
- RuntimeNotInstallable(String),
- #[error("runtime `{0}` has no target set in the distribution contract")]
- MissingTargetSet(String),
- #[error("runtime `{runtime_id}` references unknown artifact adapter `{adapter_id}`")]
- UnknownArtifactAdapter {
- runtime_id: String,
- adapter_id: String,
- },
- #[error("channel `{0}` is not defined in the runtime distribution contract")]
- UnknownChannel(String),
- #[error("channel `{0}` is defined but not active in the runtime distribution contract")]
- InactiveChannel(String),
- #[error(
- "target set `{target_set_id}` for runtime `{runtime_id}` references unknown target `{target_id}`"
- )]
- UnknownTarget {
- runtime_id: String,
- target_set_id: String,
- target_id: String,
- },
- #[error("runtime `{runtime_id}` does not support os `{os}` arch `{arch}`")]
- UnsupportedPlatform {
- runtime_id: String,
- os: String,
- arch: String,
- },
- #[error("target `{target_id}` references unknown archive format `{archive_format_id}`")]
- UnknownArchiveFormat {
- target_id: String,
- archive_format_id: String,
- },
- #[error("target `{target_id}` for runtime `{runtime_id}` does not define an archive format")]
- MissingArchiveFormat {
- runtime_id: String,
- target_id: String,
- },
+ #[error("parse runtime distribution contract failed")]
+ Parse,
+ #[error("runtime distribution schema is unsupported")]
+ UnexpectedSchema,
+ #[error("runtime distribution schema version is unsupported")]
+ UnexpectedSchemaVersion,
+ #[error("runtime is not present in the distribution contract")]
+ UnknownRuntime,
+ #[error("runtime is not installable through the distribution contract")]
+ RuntimeNotInstallable,
+ #[error("hardened service artifact authority is deferred")]
+ HardenedServiceArtifactDeferred,
+ #[error("runtime has no target set in the distribution contract")]
+ MissingTargetSet,
+ #[error("runtime references an unknown artifact adapter")]
+ UnknownArtifactAdapter,
+ #[error("channel is not defined in the distribution contract")]
+ UnknownChannel,
+ #[error("channel is defined but not active in the distribution contract")]
+ InactiveChannel,
+ #[error("runtime target set references an unknown target")]
+ UnknownTarget,
+ #[error("runtime does not support the requested platform")]
+ UnsupportedPlatform,
+ #[error("target references an unknown archive format")]
+ UnknownArchiveFormat,
+ #[error("target does not define an archive format")]
+ MissingArchiveFormat,
+ #[error("service is not a hardened distribution target")]
+ UnsupportedService,
+ #[error("service target is not eligible for Tier-1 qualification")]
+ UnsupportedServiceTarget,
}
diff --git a/crates/runtime_distribution/src/lib.rs b/crates/runtime_distribution/src/lib.rs
@@ -3,6 +3,7 @@
pub mod error;
pub mod model;
pub mod resolve;
+pub mod service;
pub use error::RadrootsRuntimeDistributionError;
pub use model::{
@@ -10,19 +11,34 @@ pub use model::{
RadrootsRuntimeDistributionContract, RuntimeDistributionEntry, TargetSet, TargetSpec,
};
pub use resolve::{
- RUNTIME_DISTRIBUTION_SCHEMA, RadrootsRuntimeDistributionResolver, ResolvedRuntimeArtifact,
- RuntimeArtifactRequest,
+ RUNTIME_DISTRIBUTION_SCHEMA, RUNTIME_DISTRIBUTION_SCHEMA_VERSION,
+ RadrootsRuntimeDistributionResolver, ResolvedRuntimeArtifact, ResolvedServiceTarget,
+ RuntimeArtifactRequest, ServiceTargetRequest,
+};
+pub use service::{
+ HardenedServiceTarget, HardenedServiceTargets, ServiceAdminBasePath, ServiceAdminTransport,
+ ServiceConfigurationFormat, ServiceInstanceSupport, ServiceOperationsSurface,
+ ServiceRunStatePolicy, ServiceStateInitialization, ServiceStatusSurface, ServiceSupportPosture,
+ ServiceTier1Target,
};
#[cfg(test)]
mod tests {
+ use radroots_runtime_paths::ServiceId;
use toml::Value;
use super::{
- RUNTIME_DISTRIBUTION_SCHEMA, RadrootsRuntimeDistributionError,
- RadrootsRuntimeDistributionResolver, RuntimeArtifactRequest,
+ HardenedServiceTarget, RUNTIME_DISTRIBUTION_SCHEMA, RadrootsRuntimeDistributionContract,
+ RadrootsRuntimeDistributionError, RadrootsRuntimeDistributionResolver,
+ RuntimeArtifactRequest, RuntimeDistributionEntry, ServiceConfigurationFormat,
+ ServiceInstanceSupport, ServiceOperationsSurface, ServiceRunStatePolicy,
+ ServiceStateInitialization, ServiceSupportPosture, ServiceTargetRequest,
+ ServiceTier1Target,
};
+ const HARDENED_SERVICE_CONTRACT: &str =
+ include_str!("../tests/fixtures/hardened_service_targets.v1.toml");
+
const CONTRACT: &str = r#"
schema = "radroots-runtime-distribution"
schema_version = 1
@@ -184,6 +200,34 @@ artifact_adapter = "mojo_workspace_archive"
target_set = "mojo_workspace_default"
default_channel = "stable"
human_installable = false
+
+[service_targets.myc]
+service_id = "myc"
+instance_support = "multiple"
+config_format = "toml"
+state_initialization = "explicit"
+run_state_policy = "existing_only"
+admin_transport = "http11_over_unix_domain_socket"
+admin_base_path = "/v1"
+admin_contract_version = 1
+status_surface = "local_admin_service_status_v1"
+operations_surface = "cached_livez_readyz_metrics"
+support_posture = "target"
+tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
+
+[service_targets.rhi]
+service_id = "rhi"
+instance_support = "multiple"
+config_format = "toml"
+state_initialization = "explicit"
+run_state_policy = "existing_only"
+admin_transport = "http11_over_unix_domain_socket"
+admin_base_path = "/v1"
+admin_contract_version = 1
+status_surface = "local_admin_service_status_v1"
+operations_surface = "cached_livez_readyz_metrics"
+support_posture = "target"
+tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
"#;
fn contract_value() -> Value {
@@ -217,10 +261,7 @@ human_installable = false
fn parse_str_rejects_invalid_toml() {
let err = RadrootsRuntimeDistributionResolver::parse_str("schema = [")
.expect_err("invalid toml should fail");
- assert_eq!(
- std::mem::discriminant(&err),
- std::mem::discriminant(&RadrootsRuntimeDistributionError::Parse(String::new()))
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::Parse);
}
#[test]
@@ -232,13 +273,7 @@ human_installable = false
let err = RadrootsRuntimeDistributionResolver::parse_str(&raw)
.expect_err("unexpected schema should fail");
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::UnexpectedSchema {
- expected: RUNTIME_DISTRIBUTION_SCHEMA,
- found: "wrong-schema".to_string(),
- }
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::UnexpectedSchema);
}
#[test]
@@ -319,12 +354,7 @@ human_installable = false
})
.expect_err("mobile runtime should not be installable");
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::RuntimeNotInstallable(
- "community-app-ios".to_string()
- )
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::RuntimeNotInstallable);
}
#[test]
@@ -342,10 +372,7 @@ human_installable = false
})
.expect_err("bootstrap runtime should not be installable");
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::RuntimeNotInstallable("hyf".to_string())
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::RuntimeNotInstallable);
}
#[test]
@@ -363,10 +390,7 @@ human_installable = false
})
.expect_err("candidate channel should be inactive");
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::InactiveChannel("candidate".to_string())
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::InactiveChannel);
}
#[test]
@@ -385,10 +409,7 @@ human_installable = false
},
);
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::UnknownRuntime("missing-runtime".to_string())
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::UnknownRuntime);
}
#[test]
@@ -407,10 +428,7 @@ human_installable = false
},
);
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::UnknownChannel("beta".to_string())
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::UnknownChannel);
}
#[test]
@@ -428,14 +446,7 @@ human_installable = false
})
.expect_err("windows target should be unsupported");
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::UnsupportedPlatform {
- runtime_id: "radrootsd".to_string(),
- os: "windows".to_string(),
- arch: "amd64".to_string(),
- }
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::UnsupportedPlatform);
}
#[test]
@@ -461,10 +472,7 @@ human_installable = false
},
);
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::MissingTargetSet("community-app-ios".to_string())
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::MissingTargetSet);
}
#[test]
@@ -492,10 +500,7 @@ human_installable = false
assert_eq!(
err,
- RadrootsRuntimeDistributionError::UnknownArtifactAdapter {
- runtime_id: "cli".to_string(),
- adapter_id: "missing_adapter".to_string(),
- }
+ RadrootsRuntimeDistributionError::UnknownArtifactAdapter
);
}
@@ -522,14 +527,7 @@ human_installable = false
},
);
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::UnsupportedPlatform {
- runtime_id: "cli".to_string(),
- os: "linux".to_string(),
- arch: "amd64".to_string(),
- }
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::UnsupportedPlatform);
}
#[test]
@@ -550,14 +548,7 @@ human_installable = false
},
);
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::UnknownTarget {
- runtime_id: "cli".to_string(),
- target_set_id: "cli_default".to_string(),
- target_id: "missing-target".to_string(),
- }
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::UnknownTarget);
}
#[test]
@@ -603,13 +594,7 @@ human_installable = false
},
);
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::UnknownArchiveFormat {
- target_id: "x86_64-unknown-linux-gnu".to_string(),
- archive_format_id: "tar.xz".to_string(),
- }
- );
+ assert_eq!(err, RadrootsRuntimeDistributionError::UnknownArchiveFormat);
}
#[test]
@@ -632,12 +617,181 @@ human_installable = false
},
);
- assert_eq!(
- err,
- RadrootsRuntimeDistributionError::MissingArchiveFormat {
- runtime_id: "community-app-desktop".to_string(),
- target_id: "aarch64-apple-darwin".to_string(),
+ assert_eq!(err, RadrootsRuntimeDistributionError::MissingArchiveFormat);
+ }
+
+ #[test]
+ fn durable_contract_resolves_exact_hardened_service_metadata() {
+ let resolver = RadrootsRuntimeDistributionResolver::parse_str(HARDENED_SERVICE_CONTRACT)
+ .expect("hardened service contract");
+
+ for service in ["myc", "rhi"] {
+ let service_id = ServiceId::new(service).expect("service id");
+ let metadata = resolver
+ .service_target(&service_id)
+ .expect("service metadata");
+ assert_eq!(metadata.service_id(), &service_id);
+ assert_eq!(
+ metadata.instance_support(),
+ ServiceInstanceSupport::Multiple
+ );
+ assert_eq!(metadata.config_format(), ServiceConfigurationFormat::Toml);
+ assert_eq!(
+ metadata.state_initialization(),
+ ServiceStateInitialization::Explicit
+ );
+ assert_eq!(
+ metadata.run_state_policy(),
+ ServiceRunStatePolicy::ExistingOnly
+ );
+ assert_eq!(
+ metadata.operations_surface(),
+ ServiceOperationsSurface::CachedLivezReadyzMetrics
+ );
+ assert_eq!(
+ metadata.operations_surface().routes(),
+ ["/livez", "/readyz", "/metrics"]
+ );
+ assert_eq!(metadata.support_posture(), ServiceSupportPosture::Target);
+ assert_eq!(metadata.tier_1_targets(), ServiceTier1Target::ALL);
+
+ for target in ServiceTier1Target::ALL {
+ let resolved = resolver
+ .resolve_service_target(&ServiceTargetRequest {
+ service_id: &service_id,
+ target_id: target.as_str(),
+ })
+ .expect("eligible target");
+ assert_eq!(resolved.service_id(), &service_id);
+ assert_eq!(resolved.target(), target);
}
+ }
+ }
+
+ #[test]
+ fn hardened_services_are_metadata_only_and_reject_unsupported_targets() {
+ let resolver = RadrootsRuntimeDistributionResolver::parse_str(HARDENED_SERVICE_CONTRACT)
+ .expect("hardened service contract");
+ let myc = ServiceId::new("myc").expect("myc");
+
+ for target_id in ["aarch64-apple-darwin", "x86_64-pc-windows-msvc", "linux-64"] {
+ assert_eq!(
+ resolver.resolve_service_target(&ServiceTargetRequest {
+ service_id: &myc,
+ target_id,
+ }),
+ Err(RadrootsRuntimeDistributionError::UnsupportedServiceTarget)
+ );
+ }
+ assert_eq!(
+ resolver.resolve_artifact(&RuntimeArtifactRequest {
+ runtime_id: "myc",
+ os: "linux",
+ arch: "amd64",
+ version: "1.0.0",
+ channel: None,
+ }),
+ Err(RadrootsRuntimeDistributionError::UnknownRuntime)
);
}
+
+ #[test]
+ fn hardened_services_reject_parsed_and_direct_artifact_authority() {
+ let raw = format!(
+ "{HARDENED_SERVICE_CONTRACT}\n\
+ [[runtime]]\n\
+ id = \"myc\"\n\
+ distribution_state = \"defined\"\n\
+ release_unit = \"myc\"\n\
+ package_name = \"radroots_myc\"\n\
+ artifact_adapter = \"rust_binary_archive\"\n\
+ default_channel = \"stable\"\n\
+ human_installable = true\n"
+ );
+ assert_eq!(
+ RadrootsRuntimeDistributionResolver::parse_str(&raw).expect_err("parsed bypass"),
+ RadrootsRuntimeDistributionError::HardenedServiceArtifactDeferred
+ );
+
+ let mut contract =
+ toml::from_str::<RadrootsRuntimeDistributionContract>(HARDENED_SERVICE_CONTRACT)
+ .expect("direct contract");
+ contract.runtime.push(RuntimeDistributionEntry {
+ id: "rhi".to_owned(),
+ distribution_state: "defined".to_owned(),
+ release_unit: "rhi".to_owned(),
+ package_name: "radroots_rhi".to_owned(),
+ binary_name: None,
+ artifact_adapter: "rust_binary_archive".to_owned(),
+ target_set: None,
+ default_channel: "stable".to_owned(),
+ human_installable: true,
+ notes: None,
+ });
+ assert_eq!(
+ RadrootsRuntimeDistributionResolver::new(contract).expect_err("direct bypass"),
+ RadrootsRuntimeDistributionError::HardenedServiceArtifactDeferred
+ );
+ }
+
+ #[test]
+ fn hardened_service_contract_rejects_schema_drift_unknown_fields_and_inventory_drift() {
+ for raw in [
+ HARDENED_SERVICE_CONTRACT.replace("schema_version = 1", "schema_version = 2"),
+ format!("{HARDENED_SERVICE_CONTRACT}\nunknown = true\n"),
+ HARDENED_SERVICE_CONTRACT.replace("service_id = \"rhi\"", "service_id = \"other\""),
+ HARDENED_SERVICE_CONTRACT.replace(
+ " \"aarch64-unknown-linux-gnu\",\n]",
+ " \"aarch64-apple-darwin\",\n]",
+ ),
+ ] {
+ assert!(RadrootsRuntimeDistributionResolver::parse_str(&raw).is_err());
+ }
+ }
+
+ #[test]
+ fn standalone_hardened_service_target_rejects_contract_drift() {
+ let contract: Value =
+ toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
+ let target =
+ toml::to_string(&contract["service_targets"]["myc"]).expect("standalone target");
+ let parsed = toml::from_str::<HardenedServiceTarget>(&target).expect("valid target");
+ assert_eq!(parsed.service_id().as_str(), "myc");
+
+ for raw in [
+ target.replace("admin_contract_version = 1", "admin_contract_version = 99"),
+ target.replace("service_id = \"myc\"", "service_id = \"unsupported\""),
+ target.replace(
+ "\"x86_64-unknown-linux-gnu\", \"aarch64-unknown-linux-gnu\"",
+ "\"aarch64-unknown-linux-gnu\"",
+ ),
+ ] {
+ assert!(toml::from_str::<HardenedServiceTarget>(&raw).is_err());
+ }
+ }
+
+ #[test]
+ fn distribution_errors_do_not_expose_contract_values_or_parser_causes() {
+ use std::error::Error as _;
+
+ for (raw, secret) in [
+ (
+ HARDENED_SERVICE_CONTRACT.replace(
+ "schema = \"radroots-runtime-distribution\"",
+ "schema = \"secret-contract-value\"",
+ ),
+ "secret-contract-value",
+ ),
+ (
+ "credential = 'secret-value'\ninvalid = [".to_owned(),
+ "secret-value",
+ ),
+ ] {
+ let error =
+ RadrootsRuntimeDistributionResolver::parse_str(&raw).expect_err("invalid contract");
+ let rendered = format!("{error} {error:?}");
+ assert!(!rendered.contains(secret));
+ assert!(error.source().is_none());
+ }
+ }
}
diff --git a/crates/runtime_distribution/src/model.rs b/crates/runtime_distribution/src/model.rs
@@ -2,7 +2,10 @@ use std::collections::BTreeMap;
use serde::Deserialize;
+use crate::service::HardenedServiceTargets;
+
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct RadrootsRuntimeDistributionContract {
pub schema: String,
pub schema_version: u32,
@@ -20,9 +23,11 @@ pub struct RadrootsRuntimeDistributionContract {
pub targets: BTreeMap<String, TargetSpec>,
#[serde(default)]
pub runtime: Vec<RuntimeDistributionEntry>,
+ pub service_targets: HardenedServiceTargets,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct DistributionFamily {
pub id: String,
pub canonical_installer_engine: String,
@@ -34,6 +39,7 @@ pub struct DistributionFamily {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct ChannelSet {
#[serde(default)]
pub active: Vec<String>,
@@ -42,6 +48,7 @@ pub struct ChannelSet {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct ArtifactAdapter {
pub kind: String,
#[serde(default)]
@@ -50,6 +57,7 @@ pub struct ArtifactAdapter {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct ArchiveFormat {
pub extension: String,
#[serde(default)]
@@ -57,12 +65,14 @@ pub struct ArchiveFormat {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct TargetSet {
#[serde(default)]
pub targets: Vec<String>,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct TargetSpec {
pub os: String,
pub arch: String,
@@ -70,6 +80,7 @@ pub struct TargetSpec {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct RuntimeDistributionEntry {
pub id: String,
pub distribution_state: String,
diff --git a/crates/runtime_distribution/src/resolve.rs b/crates/runtime_distribution/src/resolve.rs
@@ -2,8 +2,35 @@ use crate::error::RadrootsRuntimeDistributionError;
use crate::model::{
ArtifactAdapter, RadrootsRuntimeDistributionContract, RuntimeDistributionEntry, TargetSpec,
};
+use crate::service::{HardenedServiceTarget, ServiceTier1Target};
+use radroots_runtime_paths::ServiceId;
pub const RUNTIME_DISTRIBUTION_SCHEMA: &str = "radroots-runtime-distribution";
+pub const RUNTIME_DISTRIBUTION_SCHEMA_VERSION: u32 = 1;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ServiceTargetRequest<'a> {
+ pub service_id: &'a ServiceId,
+ pub target_id: &'a str,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ResolvedServiceTarget {
+ service_id: ServiceId,
+ target: ServiceTier1Target,
+}
+
+impl ResolvedServiceTarget {
+ #[must_use]
+ pub fn service_id(&self) -> &ServiceId {
+ &self.service_id
+ }
+
+ #[must_use]
+ pub const fn target(&self) -> ServiceTier1Target {
+ self.target
+ }
+}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RuntimeArtifactRequest<'a> {
@@ -40,7 +67,7 @@ pub struct RadrootsRuntimeDistributionResolver {
impl RadrootsRuntimeDistributionResolver {
pub fn parse_str(raw: &str) -> Result<Self, RadrootsRuntimeDistributionError> {
let contract = toml::from_str::<RadrootsRuntimeDistributionContract>(raw)
- .map_err(|err| RadrootsRuntimeDistributionError::Parse(err.to_string()))?;
+ .map_err(|_| RadrootsRuntimeDistributionError::Parse)?;
Self::new(contract)
}
@@ -48,10 +75,18 @@ impl RadrootsRuntimeDistributionResolver {
contract: RadrootsRuntimeDistributionContract,
) -> Result<Self, RadrootsRuntimeDistributionError> {
if contract.schema != RUNTIME_DISTRIBUTION_SCHEMA {
- return Err(RadrootsRuntimeDistributionError::UnexpectedSchema {
- expected: RUNTIME_DISTRIBUTION_SCHEMA,
- found: contract.schema.clone(),
- });
+ return Err(RadrootsRuntimeDistributionError::UnexpectedSchema);
+ }
+ if contract.schema_version != RUNTIME_DISTRIBUTION_SCHEMA_VERSION {
+ return Err(RadrootsRuntimeDistributionError::UnexpectedSchemaVersion);
+ }
+ if contract.runtime.iter().any(|runtime| {
+ contract
+ .service_targets
+ .iter()
+ .any(|(_, service)| runtime.id == service.service_id().as_str())
+ }) {
+ return Err(RadrootsRuntimeDistributionError::HardenedServiceArtifactDeferred);
}
Ok(Self { contract })
}
@@ -60,6 +95,30 @@ impl RadrootsRuntimeDistributionResolver {
&self.contract
}
+ pub fn service_target(
+ &self,
+ service_id: &ServiceId,
+ ) -> Result<&HardenedServiceTarget, RadrootsRuntimeDistributionError> {
+ self.contract
+ .service_targets
+ .get(service_id)
+ .ok_or(RadrootsRuntimeDistributionError::UnsupportedService)
+ }
+
+ pub fn resolve_service_target(
+ &self,
+ request: &ServiceTargetRequest<'_>,
+ ) -> Result<ResolvedServiceTarget, RadrootsRuntimeDistributionError> {
+ let service = self.service_target(request.service_id)?;
+ let target = ServiceTier1Target::parse(request.target_id)
+ .filter(|target| service.tier_1_targets().contains(target))
+ .ok_or(RadrootsRuntimeDistributionError::UnsupportedServiceTarget)?;
+ Ok(ResolvedServiceTarget {
+ service_id: request.service_id.clone(),
+ target,
+ })
+ }
+
pub fn resolve_artifact(
&self,
request: &RuntimeArtifactRequest<'_>,
@@ -69,33 +128,25 @@ impl RadrootsRuntimeDistributionResolver {
.runtime
.iter()
.find(|runtime| runtime.id == request.runtime_id)
- .ok_or_else(|| {
- RadrootsRuntimeDistributionError::UnknownRuntime(request.runtime_id.to_string())
- })?;
+ .ok_or(RadrootsRuntimeDistributionError::UnknownRuntime)?;
if !runtime.human_installable {
- return Err(RadrootsRuntimeDistributionError::RuntimeNotInstallable(
- runtime.id.clone(),
- ));
+ return Err(RadrootsRuntimeDistributionError::RuntimeNotInstallable);
}
let channel = request.channel.unwrap_or(runtime.default_channel.as_str());
self.ensure_channel_is_active(channel)?;
- let target_set_id = runtime.target_set.as_ref().ok_or_else(|| {
- RadrootsRuntimeDistributionError::MissingTargetSet(runtime.id.clone())
- })?;
+ let target_set_id = runtime
+ .target_set
+ .as_ref()
+ .ok_or(RadrootsRuntimeDistributionError::MissingTargetSet)?;
let adapter = self
.contract
.artifact_adapters
.get(&runtime.artifact_adapter)
- .ok_or_else(
- || RadrootsRuntimeDistributionError::UnknownArtifactAdapter {
- runtime_id: runtime.id.clone(),
- adapter_id: runtime.artifact_adapter.clone(),
- },
- )?;
+ .ok_or(RadrootsRuntimeDistributionError::UnknownArtifactAdapter)?;
let (target_id, target) =
self.select_target(runtime, target_set_id, request.os, request.arch)?;
@@ -105,10 +156,7 @@ impl RadrootsRuntimeDistributionResolver {
.contract
.archive_formats
.get(&normalized_contract_key(archive_format_id))
- .ok_or_else(|| RadrootsRuntimeDistributionError::UnknownArchiveFormat {
- target_id: target_id.to_string(),
- archive_format_id: archive_format_id.to_string(),
- })?;
+ .ok_or(RadrootsRuntimeDistributionError::UnknownArchiveFormat)?;
let artifact_stem = format!("{}-{}-{}", runtime.release_unit, request.version, target_id);
let artifact_file_name = format!("{artifact_stem}{}", archive_format.extension);
@@ -142,9 +190,7 @@ impl RadrootsRuntimeDistributionResolver {
.iter()
.any(|entry| entry == channel)
{
- return Err(RadrootsRuntimeDistributionError::UnknownChannel(
- channel.to_string(),
- ));
+ return Err(RadrootsRuntimeDistributionError::UnknownChannel);
}
if !self
.contract
@@ -153,16 +199,14 @@ impl RadrootsRuntimeDistributionResolver {
.iter()
.any(|entry| entry == channel)
{
- return Err(RadrootsRuntimeDistributionError::InactiveChannel(
- channel.to_string(),
- ));
+ return Err(RadrootsRuntimeDistributionError::InactiveChannel);
}
Ok(())
}
fn select_target<'a>(
&'a self,
- runtime: &RuntimeDistributionEntry,
+ _runtime: &RuntimeDistributionEntry,
target_set_id: &str,
os: &str,
arch: &str,
@@ -171,21 +215,15 @@ impl RadrootsRuntimeDistributionResolver {
.contract
.target_sets
.get(target_set_id)
- .ok_or_else(|| RadrootsRuntimeDistributionError::UnsupportedPlatform {
- runtime_id: runtime.id.clone(),
- os: os.to_string(),
- arch: arch.to_string(),
- })?;
+ .ok_or(RadrootsRuntimeDistributionError::UnsupportedPlatform)?;
let mut found_match = None;
for target_id in &target_set.targets {
- let target = self.contract.targets.get(target_id).ok_or_else(|| {
- RadrootsRuntimeDistributionError::UnknownTarget {
- runtime_id: runtime.id.clone(),
- target_set_id: target_set_id.to_string(),
- target_id: target_id.clone(),
- }
- })?;
+ let target = self
+ .contract
+ .targets
+ .get(target_id)
+ .ok_or(RadrootsRuntimeDistributionError::UnknownTarget)?;
if target.os == os && target.arch == arch {
found_match = Some((target_id.as_str(), target));
@@ -193,17 +231,13 @@ impl RadrootsRuntimeDistributionResolver {
}
}
- found_match.ok_or_else(|| RadrootsRuntimeDistributionError::UnsupportedPlatform {
- runtime_id: runtime.id.clone(),
- os: os.to_string(),
- arch: arch.to_string(),
- })
+ found_match.ok_or(RadrootsRuntimeDistributionError::UnsupportedPlatform)
}
fn resolve_archive_format_id<'a>(
&self,
- runtime: &RuntimeDistributionEntry,
- target_id: &'a str,
+ _runtime: &RuntimeDistributionEntry,
+ _target_id: &'a str,
target: &'a TargetSpec,
adapter: &'a ArtifactAdapter,
) -> Result<&'a str, RadrootsRuntimeDistributionError> {
@@ -215,10 +249,7 @@ impl RadrootsRuntimeDistributionResolver {
return Ok(adapter.supported_archive_formats[0].as_str());
}
- Err(RadrootsRuntimeDistributionError::MissingArchiveFormat {
- runtime_id: runtime.id.clone(),
- target_id: target_id.to_string(),
- })
+ Err(RadrootsRuntimeDistributionError::MissingArchiveFormat)
}
}
diff --git a/crates/runtime_distribution/src/service.rs b/crates/runtime_distribution/src/service.rs
@@ -0,0 +1,306 @@
+use std::collections::BTreeMap;
+
+use radroots_runtime_paths::ServiceId;
+use serde::Deserialize;
+
+/// Instance cardinality supported by a hardened service target.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ServiceInstanceSupport {
+ Multiple,
+}
+
+/// Configuration document format supported by a hardened service target.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ServiceConfigurationFormat {
+ Toml,
+}
+
+impl ServiceConfigurationFormat {
+ #[must_use]
+ pub const fn as_str(self) -> &'static str {
+ match self {
+ Self::Toml => "toml",
+ }
+ }
+}
+
+/// State initialization policy supported by a hardened service target.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ServiceStateInitialization {
+ Explicit,
+}
+
+/// Daemon state-open policy supported by a hardened service target.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ServiceRunStatePolicy {
+ ExistingOnly,
+}
+
+/// Detailed local-administration transport supported by a hardened service.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ServiceAdminTransport {
+ Http11OverUnixDomainSocket,
+}
+
+/// Versioned base path for detailed local administration.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+pub enum ServiceAdminBasePath {
+ #[serde(rename = "/v1")]
+ V1,
+}
+
+/// Detailed status surface supported by a hardened service.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ServiceStatusSurface {
+ LocalAdminServiceStatusV1,
+}
+
+/// Public operations surface supported by a hardened service.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ServiceOperationsSurface {
+ CachedLivezReadyzMetrics,
+}
+
+impl ServiceOperationsSurface {
+ pub const ROUTES: [&str; 3] = ["/livez", "/readyz", "/metrics"];
+
+ #[must_use]
+ pub const fn routes(self) -> [&'static str; 3] {
+ match self {
+ Self::CachedLivezReadyzMetrics => Self::ROUTES,
+ }
+ }
+}
+
+/// Current evidence posture for an eligible service target.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ServiceSupportPosture {
+ Target,
+}
+
+/// Exact Linux target triples eligible for future Tier-1 qualification.
+#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
+pub enum ServiceTier1Target {
+ #[serde(rename = "x86_64-unknown-linux-gnu")]
+ X86_64UnknownLinuxGnu,
+ #[serde(rename = "aarch64-unknown-linux-gnu")]
+ Aarch64UnknownLinuxGnu,
+}
+
+impl ServiceTier1Target {
+ pub const ALL: [Self; 2] = [Self::X86_64UnknownLinuxGnu, Self::Aarch64UnknownLinuxGnu];
+
+ #[must_use]
+ pub const fn as_str(self) -> &'static str {
+ match self {
+ Self::X86_64UnknownLinuxGnu => "x86_64-unknown-linux-gnu",
+ Self::Aarch64UnknownLinuxGnu => "aarch64-unknown-linux-gnu",
+ }
+ }
+
+ pub(crate) fn parse(value: &str) -> Option<Self> {
+ Self::ALL
+ .into_iter()
+ .find(|target| target.as_str() == value)
+ }
+}
+
+/// Closed metadata for one hardened standalone service target.
+#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(try_from = "HardenedServiceTargetWire")]
+pub struct HardenedServiceTarget {
+ service_id: ServiceId,
+ instance_support: ServiceInstanceSupport,
+ config_format: ServiceConfigurationFormat,
+ state_initialization: ServiceStateInitialization,
+ run_state_policy: ServiceRunStatePolicy,
+ admin_transport: ServiceAdminTransport,
+ admin_base_path: ServiceAdminBasePath,
+ admin_contract_version: u32,
+ status_surface: ServiceStatusSurface,
+ operations_surface: ServiceOperationsSurface,
+ support_posture: ServiceSupportPosture,
+ tier_1_targets: Vec<ServiceTier1Target>,
+}
+
+#[derive(Deserialize)]
+#[serde(deny_unknown_fields)]
+struct HardenedServiceTargetWire {
+ service_id: ServiceId,
+ instance_support: ServiceInstanceSupport,
+ config_format: ServiceConfigurationFormat,
+ state_initialization: ServiceStateInitialization,
+ run_state_policy: ServiceRunStatePolicy,
+ admin_transport: ServiceAdminTransport,
+ admin_base_path: ServiceAdminBasePath,
+ admin_contract_version: u32,
+ status_surface: ServiceStatusSurface,
+ operations_surface: ServiceOperationsSurface,
+ support_posture: ServiceSupportPosture,
+ tier_1_targets: Vec<ServiceTier1Target>,
+}
+
+impl HardenedServiceTarget {
+ #[must_use]
+ pub fn service_id(&self) -> &ServiceId {
+ &self.service_id
+ }
+
+ #[must_use]
+ pub const fn instance_support(&self) -> ServiceInstanceSupport {
+ self.instance_support
+ }
+
+ #[must_use]
+ pub const fn config_format(&self) -> ServiceConfigurationFormat {
+ self.config_format
+ }
+
+ #[must_use]
+ pub const fn state_initialization(&self) -> ServiceStateInitialization {
+ self.state_initialization
+ }
+
+ #[must_use]
+ pub const fn run_state_policy(&self) -> ServiceRunStatePolicy {
+ self.run_state_policy
+ }
+
+ #[must_use]
+ pub const fn admin_transport(&self) -> ServiceAdminTransport {
+ self.admin_transport
+ }
+
+ #[must_use]
+ pub const fn admin_base_path(&self) -> ServiceAdminBasePath {
+ self.admin_base_path
+ }
+
+ #[must_use]
+ pub const fn admin_contract_version(&self) -> u32 {
+ self.admin_contract_version
+ }
+
+ #[must_use]
+ pub const fn status_surface(&self) -> ServiceStatusSurface {
+ self.status_surface
+ }
+
+ #[must_use]
+ pub const fn operations_surface(&self) -> ServiceOperationsSurface {
+ self.operations_surface
+ }
+
+ #[must_use]
+ pub const fn support_posture(&self) -> ServiceSupportPosture {
+ self.support_posture
+ }
+
+ #[must_use]
+ pub fn tier_1_targets(&self) -> &[ServiceTier1Target] {
+ &self.tier_1_targets
+ }
+
+ fn has_exact_common_contract(&self) -> bool {
+ self.instance_support == ServiceInstanceSupport::Multiple
+ && self.config_format == ServiceConfigurationFormat::Toml
+ && self.state_initialization == ServiceStateInitialization::Explicit
+ && self.run_state_policy == ServiceRunStatePolicy::ExistingOnly
+ && self.admin_transport == ServiceAdminTransport::Http11OverUnixDomainSocket
+ && self.admin_base_path == ServiceAdminBasePath::V1
+ && self.admin_contract_version == 1
+ && self.status_surface == ServiceStatusSurface::LocalAdminServiceStatusV1
+ && self.operations_surface == ServiceOperationsSurface::CachedLivezReadyzMetrics
+ && self.support_posture == ServiceSupportPosture::Target
+ && self.tier_1_targets == ServiceTier1Target::ALL
+ }
+}
+
+impl TryFrom<HardenedServiceTargetWire> for HardenedServiceTarget {
+ type Error = &'static str;
+
+ fn try_from(wire: HardenedServiceTargetWire) -> Result<Self, Self::Error> {
+ let target = Self {
+ service_id: wire.service_id,
+ instance_support: wire.instance_support,
+ config_format: wire.config_format,
+ state_initialization: wire.state_initialization,
+ run_state_policy: wire.run_state_policy,
+ admin_transport: wire.admin_transport,
+ admin_base_path: wire.admin_base_path,
+ admin_contract_version: wire.admin_contract_version,
+ status_surface: wire.status_surface,
+ operations_surface: wire.operations_surface,
+ support_posture: wire.support_posture,
+ tier_1_targets: wire.tier_1_targets,
+ };
+ if !matches!(target.service_id.as_str(), "myc" | "rhi")
+ || !target.has_exact_common_contract()
+ {
+ return Err("hardened service target does not match the v1 contract");
+ }
+ Ok(target)
+ }
+}
+
+/// Validated exact Myc/RHI service-target inventory.
+#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(try_from = "BTreeMap<String, HardenedServiceTarget>")]
+pub struct HardenedServiceTargets(BTreeMap<String, HardenedServiceTarget>);
+
+impl HardenedServiceTargets {
+ #[must_use]
+ pub fn get(&self, service_id: &ServiceId) -> Option<&HardenedServiceTarget> {
+ self.0.get(service_id.as_str())
+ }
+
+ pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &HardenedServiceTarget)> {
+ self.0.iter().map(|(key, value)| (key.as_str(), value))
+ }
+
+ #[must_use]
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ #[must_use]
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+}
+
+impl TryFrom<BTreeMap<String, HardenedServiceTarget>> for HardenedServiceTargets {
+ type Error = &'static str;
+
+ fn try_from(targets: BTreeMap<String, HardenedServiceTarget>) -> Result<Self, Self::Error> {
+ const REQUIRED_SERVICES: [&str; 2] = ["myc", "rhi"];
+
+ if targets.len() != REQUIRED_SERVICES.len() {
+ return Err("hardened service target inventory must contain exactly Myc and RHI");
+ }
+ for service in REQUIRED_SERVICES {
+ let Some(target) = targets.get(service) else {
+ return Err("hardened service target inventory is incomplete");
+ };
+ if target.service_id.as_str() != service || !target.has_exact_common_contract() {
+ return Err("hardened service target metadata does not match the v1 contract");
+ }
+ }
+ if targets
+ .iter()
+ .any(|(key, target)| key != target.service_id.as_str())
+ {
+ return Err("hardened service target key does not match its service identifier");
+ }
+
+ Ok(Self(targets))
+ }
+}
diff --git a/crates/runtime_distribution/tests/fixtures/hardened_service_targets.v1.toml b/crates/runtime_distribution/tests/fixtures/hardened_service_targets.v1.toml
@@ -0,0 +1,51 @@
+schema = "radroots-runtime-distribution"
+schema_version = 1
+owner_doc = "service-hardening-v1"
+runtime_registry = "registry.toml"
+
+[family]
+id = "radroots_runtime-family"
+canonical_installer_engine = "single_runtime_selected"
+human_install_facade = "delivery_publication_only"
+tooling_consumption = "shared_distribution_library"
+independent_runtime_versions = true
+version_resolution = "runtime_scoped_channel_latest"
+artifact_verification_required = true
+
+[channels]
+active = []
+defined = []
+
+[service_targets.myc]
+service_id = "myc"
+instance_support = "multiple"
+config_format = "toml"
+state_initialization = "explicit"
+run_state_policy = "existing_only"
+admin_transport = "http11_over_unix_domain_socket"
+admin_base_path = "/v1"
+admin_contract_version = 1
+status_surface = "local_admin_service_status_v1"
+operations_surface = "cached_livez_readyz_metrics"
+support_posture = "target"
+tier_1_targets = [
+ "x86_64-unknown-linux-gnu",
+ "aarch64-unknown-linux-gnu",
+]
+
+[service_targets.rhi]
+service_id = "rhi"
+instance_support = "multiple"
+config_format = "toml"
+state_initialization = "explicit"
+run_state_policy = "existing_only"
+admin_transport = "http11_over_unix_domain_socket"
+admin_base_path = "/v1"
+admin_contract_version = 1
+status_surface = "local_admin_service_status_v1"
+operations_surface = "cached_livez_readyz_metrics"
+support_posture = "target"
+tier_1_targets = [
+ "x86_64-unknown-linux-gnu",
+ "aarch64-unknown-linux-gnu",
+]
diff --git a/crates/runtime_distribution/tests/package_boundary.rs b/crates/runtime_distribution/tests/package_boundary.rs
@@ -0,0 +1,34 @@
+const SERVICE_SOURCE: &str = include_str!("../src/service.rs");
+const SERVICE_FIXTURE: &str = include_str!("fixtures/hardened_service_targets.v1.toml");
+
+#[test]
+fn hardened_service_metadata_has_no_artifact_or_runtime_authority() {
+ for forbidden in [
+ "binary_name",
+ "package_name",
+ "artifact_adapter",
+ "default_channel",
+ "[[runtime]]",
+ "qualified",
+ ] {
+ assert!(
+ !SERVICE_SOURCE.contains(forbidden) && !SERVICE_FIXTURE.contains(forbidden),
+ "hardened service metadata contains deferred authority `{forbidden}`"
+ );
+ }
+
+ for forbidden in [
+ "std::fs",
+ "std::process",
+ "tokio",
+ "hyper",
+ "reqwest",
+ "UnixListener",
+ "TcpListener",
+ ] {
+ assert!(
+ !SERVICE_SOURCE.contains(forbidden),
+ "service metadata owns forbidden runtime behavior `{forbidden}`"
+ );
+ }
+}
diff --git a/crates/runtime_manager/Cargo.toml b/crates/runtime_manager/Cargo.toml
@@ -14,6 +14,7 @@ readme = "README"
[dependencies]
flate2 = { workspace = true }
+radroots_runtime_distribution = { workspace = true }
radroots_runtime_paths = { workspace = true }
serde = { workspace = true, features = ["derive"] }
tar = { workspace = true }
diff --git a/crates/runtime_manager/README b/crates/runtime_manager/README
@@ -8,16 +8,19 @@ runtime lifecycle and inspection helpers for the `radroots` core libraries.
* schema-checked management contract parsing and shared management constants;
* manager-owned shared and per-instance tracking paths kept separate from
canonical service-instance paths supplied by a sealed `RuntimeContext`;
- * typed, deterministic registry load, save, lookup, and upsert helpers that
+ * typed, deterministic registry load, save, and lookup helpers that
persist service/instance identities without duplicating service paths or
secrets;
* lifecycle helpers for contained archive install, process start and stop,
and context-bound non-secret configuration writes; the manager has no
credential read/write authority;
- * context-bound runtime inspection and target-resolution helpers for status,
- logs, config, and action availability; and
+ * sealed Myc/RHI target metadata and context resolution without service
+ registration, lifecycle admission, or PID/config/log probing; and
* cleanup behavior limited to manager-owned install and tracking artifacts,
- preserving canonical service state and secrets.
+preserving canonical service state and secrets.
+
+Myc and RHI remain metadata-only management targets until their public CLI,
+Unix-admin, status, and artifact integrations are separately implemented.
## Copyright
diff --git a/crates/runtime_manager/src/error.rs b/crates/runtime_manager/src/error.rs
@@ -11,10 +11,18 @@ pub enum RadrootsRuntimeManagerError {
Parse,
#[error("runtime management schema is unsupported")]
UnexpectedSchema,
+ #[error("runtime management schema version is unsupported")]
+ UnexpectedSchemaVersion,
+ #[error("runtime management contract violates the hardened service inventory")]
+ InvalidContract,
#[error("management mode does not support the selected profile")]
UnsupportedProfile,
#[error("runtime has no bootstrap entry in runtime management contract")]
UnknownBootstrapRuntime,
+ #[error("runtime is not a hardened service target")]
+ UnsupportedServiceTarget,
+ #[error("hardened service target is metadata-only until service integration is complete")]
+ MetadataOnlyServiceTarget,
#[error("runtime context does not share the manager path scope")]
RuntimeContextMismatch,
#[error("read runtime instance registry failed: {kind}")]
diff --git a/crates/runtime_manager/src/lib.rs b/crates/runtime_manager/src/lib.rs
@@ -31,6 +31,10 @@ pub use paths::{ManagedRuntimeInstancePaths, ManagedRuntimeSharedPaths, bootstra
pub use registry::{instance, load_registry, save_registry};
pub const RUNTIME_MANAGEMENT_SCHEMA: &str = "radroots-runtime-management";
+pub const RUNTIME_MANAGEMENT_SCHEMA_VERSION: u32 = 1;
+
+pub(crate) const HARDENED_MANAGEMENT_CONTRACT: &str =
+ include_str!("../tests/fixtures/hardened_service_management.v1.toml");
pub fn parse_contract_str(
raw: &str,
@@ -40,69 +44,32 @@ pub fn parse_contract_str(
if contract.schema != RUNTIME_MANAGEMENT_SCHEMA {
return Err(RadrootsRuntimeManagerError::UnexpectedSchema);
}
+ if contract.schema_version != RUNTIME_MANAGEMENT_SCHEMA_VERSION {
+ return Err(RadrootsRuntimeManagerError::UnexpectedSchemaVersion);
+ }
+ validate_hardened_management_contract(&contract)?;
Ok(contract)
}
+pub(crate) fn validate_hardened_management_contract(
+ contract: &RadrootsRuntimeManagementContract,
+) -> Result<(), RadrootsRuntimeManagerError> {
+ let expected =
+ toml::from_str::<RadrootsRuntimeManagementContract>(HARDENED_MANAGEMENT_CONTRACT)
+ .map_err(|_| RadrootsRuntimeManagerError::InvalidContract)?;
+ if contract != &expected {
+ return Err(RadrootsRuntimeManagerError::InvalidContract);
+ }
+ Ok(())
+}
+
#[cfg(test)]
mod tests {
use std::error::Error as _;
- use super::{RUNTIME_MANAGEMENT_SCHEMA, parse_contract_str};
-
- const CONTRACT: &str = r#"
-schema = "radroots-runtime-management"
-schema_version = 1
-owner_doc = "owner"
-runtime_registry = "registry"
-distribution_contract = "distribution"
-capabilities_contract = "capabilities"
-
-[defaults]
-instance_cardinality = "multiple"
-managed_runtime_lookup = "typed_instance_registry"
-explicit_runtime_endpoint_overrides_precede_managed_instance_binding = true
-global_path_mutation_forbidden = true
-
-[management_clients]
-active = ["cli"]
-
-[managed_runtime_targets]
-defined = ["myc", "rhi"]
-
-[lifecycle]
-actions = ["status"]
-health_states = ["running"]
-
-[mode.interactive]
-contract_state = "active"
-platforms = ["linux"]
-supported_profiles = ["repo_local"]
-service_manager_integration = false
-uses_absolute_binary_paths = true
-default_instance_cardinality = "multiple"
-
-[paths.interactive]
-shared_namespace = "obsolete"
-instance_registry_root_class = "config"
-instance_registry_rel = "obsolete"
-artifact_cache_root_class = "cache"
-artifact_cache_rel = "obsolete"
-install_root_class = "data"
-install_root_rel = "obsolete"
-state_root_class = "data"
-state_root_rel = "obsolete"
-logs_root_class = "logs"
-logs_root_rel = "obsolete"
-run_root_class = "run"
-run_root_rel = "obsolete"
-secrets_root_class = "secrets"
-secrets_namespace_rel = "obsolete"
-
-[instance_metadata]
-required_fields = ["service_id", "instance_id"]
+ use super::{HARDENED_MANAGEMENT_CONTRACT, RUNTIME_MANAGEMENT_SCHEMA, parse_contract_str};
-[bootstrap]
-"#;
+ const CONTRACT: &str = HARDENED_MANAGEMENT_CONTRACT;
#[test]
fn contract_parser_accepts_only_the_expected_schema() {
diff --git a/crates/runtime_manager/src/managed.rs b/crates/runtime_manager/src/managed.rs
@@ -1,10 +1,10 @@
use core::fmt;
use std::path::Path;
+use radroots_runtime_distribution::HardenedServiceTarget;
use radroots_runtime_paths::{RadrootsPathProfile, RuntimeContext, RuntimeContextSource};
-use crate::paths::{resolve_instance_paths, resolve_shared_paths};
-use crate::registry::{remove_instance, upsert_instance};
+use crate::paths::resolve_shared_paths;
use crate::{
BootstrapRuntimeContract, ManagedRuntimeHealthState, ManagedRuntimeInstallState,
ManagedRuntimeInstancePaths, ManagedRuntimeInstanceRecord, ManagedRuntimeInstanceRegistry,
@@ -44,14 +44,13 @@ impl ManagedRuntimeContext {
pub fn register_instance(
&mut self,
runtime_context: &RuntimeContext,
- install_state: ManagedRuntimeInstallState,
+ _install_state: ManagedRuntimeInstallState,
) -> Result<(), RadrootsRuntimeManagerError> {
ensure_context_scope(&self.manager_context, runtime_context)?;
- upsert_instance(
- &mut self.registry,
- ManagedRuntimeInstanceRecord::new(runtime_context, install_state),
- );
- Ok(())
+ match self.contract.service_targets.get(runtime_context.service()) {
+ Some(_) => Err(RadrootsRuntimeManagerError::MetadataOnlyServiceTarget),
+ None => Err(RadrootsRuntimeManagerError::UnsupportedServiceTarget),
+ }
}
pub fn remove_instance(
@@ -59,11 +58,10 @@ impl ManagedRuntimeContext {
runtime_context: &RuntimeContext,
) -> Result<Option<ManagedRuntimeInstanceRecord>, RadrootsRuntimeManagerError> {
ensure_context_scope(&self.manager_context, runtime_context)?;
- Ok(remove_instance(
- &mut self.registry,
- runtime_context.service(),
- runtime_context.instance(),
- ))
+ match self.contract.service_targets.get(runtime_context.service()) {
+ Some(_) => Err(RadrootsRuntimeManagerError::MetadataOnlyServiceTarget),
+ None => Err(RadrootsRuntimeManagerError::UnsupportedServiceTarget),
+ }
}
}
@@ -129,6 +127,7 @@ pub struct ManagedRuntimeTarget {
context: RuntimeContext,
instance_source: RuntimeContextSource,
runtime_group: ManagedRuntimeGroup,
+ service_target: HardenedServiceTarget,
management_mode: Option<String>,
mode_contract: Option<ManagementModeContract>,
bootstrap: Option<BootstrapRuntimeContract>,
@@ -153,6 +152,11 @@ impl ManagedRuntimeTarget {
}
#[must_use]
+ pub fn service_target(&self) -> &HardenedServiceTarget {
+ &self.service_target
+ }
+
+ #[must_use]
pub fn management_mode(&self) -> Option<&str> {
self.management_mode.as_deref()
}
@@ -184,6 +188,7 @@ impl fmt::Debug for ManagedRuntimeTarget {
.debug_struct("ManagedRuntimeTarget")
.field("context", &self.context)
.field("runtime_group", &self.runtime_group)
+ .field("service_target", &self.service_target)
.field("predicted_paths", &self.predicted_paths)
.finish_non_exhaustive()
}
@@ -293,6 +298,7 @@ pub fn load_management_context(
contract: RadrootsRuntimeManagementContract,
manager_context: RuntimeContext,
) -> Result<ManagedRuntimeContext, RadrootsRuntimeManagerError> {
+ crate::validate_hardened_management_contract(&contract)?;
active_management_mode_for_profile(&contract, manager_context.profile())?;
let shared_paths = resolve_shared_paths(&manager_context);
let registry = load_registry(shared_paths.instance_registry_path())?;
@@ -330,29 +336,28 @@ pub fn resolve_runtime_target(
ensure_context_scope(&context.manager_context, &runtime_context)?;
let runtime_id = runtime_context.service().as_str();
let runtime_group = runtime_group(&context.contract, runtime_id);
+ let service_target = context
+ .contract
+ .service_targets
+ .get(runtime_context.service())
+ .cloned()
+ .ok_or(RadrootsRuntimeManagerError::UnsupportedServiceTarget)?;
let bootstrap = context.contract.bootstrap.get(runtime_id).cloned();
- let management_mode = bootstrap
- .as_ref()
- .map(|entry| entry.management_mode.clone());
+ let management_mode = Some(
+ active_management_mode_for_profile(&context.contract, runtime_context.profile())?
+ .to_owned(),
+ );
let mode_contract = management_mode
.as_ref()
.and_then(|mode_id| context.contract.mode.get(mode_id).cloned());
- let instance_record = context
- .registry
- .instances
- .iter()
- .find(|record| record.matches_context(&runtime_context))
- .cloned();
- let predicted_paths = matches!(
- runtime_group,
- ManagedRuntimeGroup::ActiveManagedTarget | ManagedRuntimeGroup::DefinedManagedTarget
- )
- .then(|| resolve_instance_paths(&context.shared_paths, &runtime_context));
+ let instance_record = None;
+ let predicted_paths = None;
Ok(ManagedRuntimeTarget {
instance_source: runtime_context.sources().instance(),
context: runtime_context,
runtime_group,
+ service_target,
management_mode,
mode_contract,
bootstrap,
@@ -400,11 +405,7 @@ pub fn inspect_runtime_status(
target: &ManagedRuntimeTarget,
lifecycle_actions: &[String],
) -> ManagedRuntimeInspection<ManagedRuntimeStatusInspection> {
- let availability = if target.runtime_group == ManagedRuntimeGroup::Unknown {
- ManagedRuntimeInspectionAvailability::Unconfigured
- } else {
- ManagedRuntimeInspectionAvailability::Success
- };
+ let availability = managed_inspection_availability(target);
let (health_state, health_source) = infer_health_state(target);
ManagedRuntimeInspection {
@@ -430,7 +431,7 @@ pub fn inspect_runtime_status(
preferred_cli_binding: target
.bootstrap
.as_ref()
- .map(|entry| entry.preferred_cli_binding),
+ .map(BootstrapRuntimeContract::preferred_cli_binding),
install_state: target
.instance_record
.as_ref()
@@ -453,14 +454,16 @@ pub fn inspect_runtime_logs(
target: &ManagedRuntimeTarget,
) -> ManagedRuntimeInspection<ManagedRuntimeLogsInspection> {
let availability = managed_inspection_availability(target);
- let stdout_log_present = target
- .predicted_paths
- .as_ref()
- .is_some_and(|paths| paths.stdout_log_path().exists());
- let stderr_log_present = target
- .predicted_paths
- .as_ref()
- .is_some_and(|paths| paths.stderr_log_path().exists());
+ let stdout_log_present = (availability == ManagedRuntimeInspectionAvailability::Success)
+ && target
+ .predicted_paths
+ .as_ref()
+ .is_some_and(|paths| paths.stdout_log_path().exists());
+ let stderr_log_present = (availability == ManagedRuntimeInspectionAvailability::Success)
+ && target
+ .predicted_paths
+ .as_ref()
+ .is_some_and(|paths| paths.stderr_log_path().exists());
ManagedRuntimeInspection {
availability,
@@ -483,12 +486,15 @@ pub fn inspect_runtime_config(
target: &ManagedRuntimeTarget,
) -> ManagedRuntimeInspection<ManagedRuntimeConfigInspection> {
let availability = managed_inspection_availability(target);
- let config_path = target.instance_record.as_ref().and_then(|_| {
- target
- .predicted_paths
- .as_ref()
- .map(ManagedRuntimeInstancePaths::config_path)
- });
+ let config_path = (availability == ManagedRuntimeInspectionAvailability::Success)
+ .then_some(())
+ .and(target.instance_record.as_ref())
+ .and_then(|_| {
+ target
+ .predicted_paths
+ .as_ref()
+ .map(ManagedRuntimeInstancePaths::config_path)
+ });
let config_present = config_path.as_deref().is_some_and(Path::exists);
ManagedRuntimeInspection {
@@ -507,23 +513,11 @@ pub fn inspect_runtime_config(
},
source: "runtime context + typed instance registry".to_owned(),
detail: config_detail(target, config_path.is_some()),
- config_format: target
- .bootstrap
- .as_ref()
- .map(|entry| entry.config_format.clone()),
+ config_format: Some(target.service_target.config_format().as_str().to_owned()),
config_present,
- requires_bootstrap_secret: target
- .bootstrap
- .as_ref()
- .map(|entry| entry.requires_bootstrap_secret),
- requires_config_bootstrap: target
- .bootstrap
- .as_ref()
- .map(|entry| entry.requires_config_bootstrap),
- requires_signer_provider: target
- .bootstrap
- .as_ref()
- .map(|entry| entry.requires_signer_provider),
+ requires_bootstrap_secret: None,
+ requires_config_bootstrap: Some(true),
+ requires_signer_provider: None,
},
}
}
@@ -693,6 +687,12 @@ fn unknown_runtime_detail(target: &ManagedRuntimeTarget) -> String {
}
fn infer_health_state(target: &ManagedRuntimeTarget) -> (&'static str, &'static str) {
+ if target.runtime_group != ManagedRuntimeGroup::ActiveManagedTarget {
+ return (
+ health_state_label(ManagedRuntimeHealthState::NotInstalled),
+ "metadata_only",
+ );
+ }
let Some(record) = &target.instance_record else {
return (
health_state_label(ManagedRuntimeHealthState::NotInstalled),
@@ -795,82 +795,12 @@ mod tests {
inspect_runtime_logs, inspect_runtime_status, load_management_context,
resolve_runtime_target, runtime_group,
};
- use crate::{ManagedRuntimeInstallState, RadrootsRuntimeManagerError, parse_contract_str};
-
- const CONTRACT: &str = r#"
-schema = "radroots-runtime-management"
-schema_version = 1
-owner_doc = "owner"
-runtime_registry = "registry.toml"
-distribution_contract = "distribution.toml"
-capabilities_contract = "capabilities.toml"
-
-[defaults]
-instance_cardinality = "multiple"
-managed_runtime_lookup = "typed_instance_registry"
-explicit_runtime_endpoint_overrides_precede_managed_instance_binding = true
-global_path_mutation_forbidden = true
-
-[management_clients]
-active = ["cli"]
-
-[managed_runtime_targets]
-active = ["radrootsd"]
-defined = ["myc", "rhi"]
-bootstrap_only = ["hyf"]
-
-[lifecycle]
-actions = ["install", "start"]
-health_states = ["not_installed", "running"]
-
-[mode.interactive_user_managed]
-contract_state = "active"
-platforms = ["linux"]
-supported_profiles = ["repo_local"]
-service_manager_integration = false
-uses_absolute_binary_paths = true
-default_instance_cardinality = "multiple"
-
-[mode.service_host_managed]
-contract_state = "defined"
-platforms = ["linux"]
-supported_profiles = ["service_host"]
-service_manager_integration = true
-uses_absolute_binary_paths = true
-default_instance_cardinality = "multiple"
-
-[paths.interactive_user_managed]
-shared_namespace = "obsolete"
-instance_registry_root_class = "config"
-instance_registry_rel = "obsolete"
-artifact_cache_root_class = "cache"
-artifact_cache_rel = "obsolete"
-install_root_class = "data"
-install_root_rel = "obsolete"
-state_root_class = "data"
-state_root_rel = "obsolete"
-logs_root_class = "logs"
-logs_root_rel = "obsolete"
-run_root_class = "run"
-run_root_rel = "obsolete"
-secrets_root_class = "secrets"
-secrets_namespace_rel = "obsolete"
-
-[instance_metadata]
-required_fields = ["service_id", "instance_id"]
-
-[bootstrap.radrootsd]
-runtime_id = "radrootsd"
-management_mode = "interactive_user_managed"
-default_instance_id = "local"
-install_strategy = "archive_unpack"
-config_format = "toml"
-requires_bootstrap_secret = true
-requires_config_bootstrap = true
-requires_signer_provider = false
-health_surface = "jsonrpc_status"
-preferred_cli_binding = true
-"#;
+ use crate::{
+ HARDENED_MANAGEMENT_CONTRACT, ManagedRuntimeInstallState, RadrootsRuntimeManagerError,
+ parse_contract_str,
+ };
+
+ const CONTRACT: &str = HARDENED_MANAGEMENT_CONTRACT;
fn context(service: &str, instance: &str, root: &std::path::Path) -> RuntimeContext {
RuntimeContext::resolve(
@@ -918,9 +848,10 @@ preferred_cli_binding = true
.expect("active mode"),
"interactive_user_managed"
);
- assert!(
+ assert_eq!(
active_management_mode_for_profile(&contract, RadrootsPathProfile::ServiceHost)
- .is_err()
+ .expect("service-host mode"),
+ "service_host_managed"
);
}
@@ -930,26 +861,32 @@ preferred_cli_binding = true
let mut manager = manager(dir.path());
let primary = context("myc", "primary", dir.path());
let secondary = context("myc", "secondary", dir.path());
- manager
- .register_instance(&primary, ManagedRuntimeInstallState::Configured)
- .expect("register primary");
let primary_target = resolve_runtime_target(&manager, primary.clone()).expect("primary");
let secondary_target =
resolve_runtime_target(&manager, secondary.clone()).expect("secondary");
- assert!(primary_target.instance_record.is_some());
+ assert!(primary_target.instance_record.is_none());
assert!(secondary_target.instance_record.is_none());
assert_eq!(primary_target.context, primary);
assert_eq!(secondary_target.context, secondary);
assert_ne!(
- primary_target.predicted_paths,
- secondary_target.predicted_paths
+ primary_target.context.paths(),
+ secondary_target.context.paths()
);
assert_eq!(
primary_target.runtime_group,
ManagedRuntimeGroup::DefinedManagedTarget
);
- assert!(primary_target.predicted_paths.is_some());
+ assert!(primary_target.predicted_paths.is_none());
+ assert_eq!(primary_target.service_target().service_id().as_str(), "myc");
+ assert_eq!(
+ manager.register_instance(&primary, ManagedRuntimeInstallState::Configured),
+ Err(RadrootsRuntimeManagerError::MetadataOnlyServiceTarget)
+ );
+ assert_eq!(
+ manager.remove_instance(&primary),
+ Err(RadrootsRuntimeManagerError::MetadataOnlyServiceTarget)
+ );
}
#[test]
@@ -977,46 +914,41 @@ preferred_cli_binding = true
let contract = manager.contract();
assert_eq!(
runtime_group(contract, "radrootsd"),
- ManagedRuntimeGroup::ActiveManagedTarget
+ ManagedRuntimeGroup::Unknown
);
assert_eq!(
runtime_group(contract, "myc"),
ManagedRuntimeGroup::DefinedManagedTarget
);
- assert_eq!(
- runtime_group(contract, "hyf"),
- ManagedRuntimeGroup::BootstrapOnly
- );
+ assert_eq!(runtime_group(contract, "hyf"), ManagedRuntimeGroup::Unknown);
assert_eq!(
runtime_group(contract, "unknown"),
ManagedRuntimeGroup::Unknown
);
- let unknown = resolve_runtime_target(&manager, context("unknown", "default", dir.path()))
- .expect("unknown target");
- assert!(unknown.predicted_paths.is_none());
assert_eq!(
- inspect_runtime_status(&unknown, &[]).availability,
- ManagedRuntimeInspectionAvailability::Unconfigured
+ resolve_runtime_target(&manager, context("unknown", "default", dir.path()))
+ .expect_err("unknown target"),
+ RadrootsRuntimeManagerError::UnsupportedServiceTarget
);
}
#[test]
- fn status_uses_manager_tracking_without_disclosing_paths() {
+ fn status_is_metadata_only_without_probing_manager_tracking() {
let dir = tempdir().expect("tempdir");
- let mut manager = manager(dir.path());
- let service = context("radrootsd", "local", dir.path());
- manager
- .register_instance(&service, ManagedRuntimeInstallState::Configured)
- .expect("register service");
+ let manager = manager(dir.path());
+ let service = context("myc", "primary", dir.path());
let target = resolve_runtime_target(&manager, service).expect("target");
- let paths = target.predicted_paths.as_ref().expect("paths");
- fs::create_dir_all(paths.run_dir()).expect("run dir");
- fs::write(paths.pid_file_path(), std::process::id().to_string()).expect("pid");
+ assert!(target.predicted_paths().is_none());
let status = inspect_runtime_status(&target, &["start".to_owned()]);
- assert_eq!(status.view.health_state, "running");
- assert_eq!(status.view.health_source, "process_probe");
+ assert_eq!(
+ status.availability,
+ ManagedRuntimeInspectionAvailability::Unsupported
+ );
+ assert_eq!(status.view.health_state, "not_installed");
+ assert_eq!(status.view.health_source, "metadata_only");
+ assert!(status.view.lifecycle_actions.is_empty());
assert_eq!(
status.view.instance_source,
RuntimeContextSource::BootstrapCli
@@ -1027,26 +959,35 @@ preferred_cli_binding = true
}
#[test]
- fn log_and_config_inspections_use_manager_and_service_context_paths() {
+ fn log_and_config_inspections_remain_non_io_for_metadata_only_services() {
let dir = tempdir().expect("tempdir");
- let mut manager = manager(dir.path());
- let service = context("radrootsd", "local", dir.path());
- manager
- .register_instance(&service, ManagedRuntimeInstallState::Configured)
- .expect("register service");
+ let manager = manager(dir.path());
+ let service = context("rhi", "default", dir.path());
let target = resolve_runtime_target(&manager, service).expect("target");
- let paths = target.predicted_paths.as_ref().expect("paths");
- fs::create_dir_all(paths.logs_dir()).expect("logs");
- fs::write(paths.stdout_log_path(), "stdout").expect("stdout");
- let config_path = paths.config_path();
- fs::create_dir_all(config_path.parent().expect("config parent")).expect("config parent");
- fs::write(&config_path, "enabled = true").expect("config");
+ assert!(target.predicted_paths().is_none());
+ fs::create_dir_all(target.context().paths().logs()).expect("service logs");
+ fs::write(target.context().paths().logs().join("stdout.log"), "stdout")
+ .expect("service stdout");
+ fs::create_dir_all(target.context().paths().config()).expect("service config");
+ fs::write(
+ target.context().paths().config().join("config.toml"),
+ "enabled = true",
+ )
+ .expect("service config");
let logs = inspect_runtime_logs(&target);
- assert!(logs.view.stdout_log_present);
+ assert_eq!(
+ logs.availability,
+ ManagedRuntimeInspectionAvailability::Unsupported
+ );
+ assert!(!logs.view.stdout_log_present);
assert!(!logs.view.stderr_log_present);
let config = inspect_runtime_config(&target);
- assert!(config.view.config_present);
+ assert_eq!(
+ config.availability,
+ ManagedRuntimeInspectionAvailability::Unsupported
+ );
+ assert!(!config.view.config_present);
assert_eq!(config.view.config_format.as_deref(), Some("toml"));
for rendered in [format!("{logs:?}"), format!("{config:?}")] {
assert!(!rendered.contains(dir.path().to_string_lossy().as_ref()));
@@ -1057,24 +998,51 @@ preferred_cli_binding = true
fn actions_do_not_mutate_bindings_for_any_group() {
let dir = tempdir().expect("tempdir");
let manager = manager(dir.path());
- for (service, expected) in [
- (
- "radrootsd",
- ManagedRuntimeInspectionAvailability::Unsupported,
- ),
- ("myc", ManagedRuntimeInspectionAvailability::Unsupported),
- ("hyf", ManagedRuntimeInspectionAvailability::Unsupported),
- (
- "unknown",
- ManagedRuntimeInspectionAvailability::Unconfigured,
- ),
- ] {
+ for service in ["myc", "rhi"] {
let target = resolve_runtime_target(&manager, context(service, "default", dir.path()))
.expect("target");
let action = inspect_runtime_action(&target, ManagedRuntimeLifecycleAction::ConfigSet);
- assert_eq!(action.availability, expected);
+ assert_eq!(
+ action.availability,
+ ManagedRuntimeInspectionAvailability::Unsupported
+ );
assert!(!action.view.mutates_bindings);
assert!(action.view.next_step.is_none());
}
}
+
+ #[test]
+ fn durable_management_contract_requires_explicit_instances_and_rejects_any_drift() {
+ let contract = parse_contract_str(HARDENED_MANAGEMENT_CONTRACT).expect("contract");
+ assert_eq!(contract.service_targets.len(), 2);
+ assert!(contract.bootstrap.is_empty());
+
+ for raw in [
+ HARDENED_MANAGEMENT_CONTRACT.replace("schema_version = 1", "schema_version = 2"),
+ HARDENED_MANAGEMENT_CONTRACT.replace(
+ "defined = [\"myc\", \"rhi\"]",
+ "active = [\"myc\"]\ndefined = [\"rhi\"]",
+ ),
+ HARDENED_MANAGEMENT_CONTRACT.replace(
+ "managed_runtime_lookup = \"typed_instance_registry\"",
+ "managed_runtime_lookup = \"different\"",
+ ),
+ HARDENED_MANAGEMENT_CONTRACT.replace("active = [\"cli\"]", "active = [\"other\"]"),
+ HARDENED_MANAGEMENT_CONTRACT.replace(
+ "supported_profiles = [\"interactive\", \"repo_local\"]",
+ "supported_profiles = [\"interactive\"]",
+ ),
+ HARDENED_MANAGEMENT_CONTRACT.replace(
+ "required_fields = [\"service_id\", \"instance_id\"]",
+ "required_fields = [\"service_id\"]",
+ ),
+ HARDENED_MANAGEMENT_CONTRACT.replace(
+ "distribution_contract = \"hardened-service-targets.v1.toml\"",
+ "distribution_contract = \"different.toml\"",
+ ),
+ format!("{HARDENED_MANAGEMENT_CONTRACT}\nunknown = true\n"),
+ ] {
+ assert!(parse_contract_str(&raw).is_err());
+ }
+ }
}
diff --git a/crates/runtime_manager/src/model.rs b/crates/runtime_manager/src/model.rs
@@ -1,8 +1,10 @@
+use radroots_runtime_distribution::HardenedServiceTargets;
use radroots_runtime_paths::{InstanceId, RuntimeContext, ServiceId};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct RadrootsRuntimeManagementContract {
pub schema: String,
pub schema_version: u32,
@@ -13,6 +15,7 @@ pub struct RadrootsRuntimeManagementContract {
pub defaults: ManagementDefaults,
pub management_clients: RuntimeGroups,
pub managed_runtime_targets: RuntimeGroups,
+ pub service_targets: HardenedServiceTargets,
pub lifecycle: LifecycleContract,
pub mode: BTreeMap<String, ManagementModeContract>,
pub paths: BTreeMap<String, ManagementPathContract>,
@@ -21,6 +24,7 @@ pub struct RadrootsRuntimeManagementContract {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct ManagementDefaults {
pub instance_cardinality: String,
pub managed_runtime_lookup: String,
@@ -29,6 +33,7 @@ pub struct ManagementDefaults {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)]
+#[serde(deny_unknown_fields)]
pub struct RuntimeGroups {
#[serde(default)]
pub active: Vec<String>,
@@ -39,6 +44,7 @@ pub struct RuntimeGroups {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct LifecycleContract {
#[serde(default)]
pub actions: Vec<String>,
@@ -49,6 +55,7 @@ pub struct LifecycleContract {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct ManagementModeContract {
pub contract_state: String,
#[serde(default)]
@@ -63,6 +70,7 @@ pub struct ManagementModeContract {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct ManagementPathContract {
pub shared_namespace: String,
pub instance_registry_root_class: String,
@@ -82,6 +90,7 @@ pub struct ManagementPathContract {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct InstanceMetadataContract {
#[serde(default)]
pub required_fields: Vec<String>,
@@ -90,18 +99,28 @@ pub struct InstanceMetadataContract {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
+#[serde(deny_unknown_fields)]
pub struct BootstrapRuntimeContract {
- pub runtime_id: String,
- pub management_mode: String,
- pub default_instance_id: String,
- pub install_strategy: String,
- pub config_format: String,
- pub requires_bootstrap_secret: bool,
- pub requires_config_bootstrap: bool,
- pub requires_signer_provider: bool,
- pub health_surface: String,
- pub preferred_cli_binding: bool,
- pub notes: Option<String>,
+ service_id: ServiceId,
+ default_instance_id: InstanceId,
+ preferred_cli_binding: bool,
+}
+
+impl BootstrapRuntimeContract {
+ #[must_use]
+ pub fn service_id(&self) -> &ServiceId {
+ &self.service_id
+ }
+
+ #[must_use]
+ pub fn default_instance_id(&self) -> &InstanceId {
+ &self.default_instance_id
+ }
+
+ #[must_use]
+ pub const fn preferred_cli_binding(&self) -> bool {
+ self.preferred_cli_binding
+ }
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
@@ -144,6 +163,7 @@ pub struct ManagedRuntimeInstanceRecord {
}
impl ManagedRuntimeInstanceRecord {
+ #[cfg(test)]
#[must_use]
pub(crate) fn new(context: &RuntimeContext, install_state: ManagedRuntimeInstallState) -> Self {
Self {
diff --git a/crates/runtime_manager/src/paths.rs b/crates/runtime_manager/src/paths.rs
@@ -187,6 +187,7 @@ pub(crate) fn resolve_shared_paths(context: &RuntimeContext) -> ManagedRuntimeSh
}
#[must_use]
+#[cfg(test)]
pub(crate) fn resolve_instance_paths(
shared: &ManagedRuntimeSharedPaths,
context: &RuntimeContext,
diff --git a/crates/runtime_manager/src/registry.rs b/crates/runtime_manager/src/registry.rs
@@ -87,6 +87,7 @@ fn normalize_registry(
Ok(registry)
}
+#[cfg(test)]
pub(crate) fn upsert_instance(
registry: &mut ManagedRuntimeInstanceRegistry,
record: ManagedRuntimeInstanceRecord,
@@ -117,6 +118,7 @@ pub fn instance<'a>(
.find(|record| record.service_id() == service_id && record.instance_id() == instance_id)
}
+#[cfg(test)]
pub(crate) fn remove_instance(
registry: &mut ManagedRuntimeInstanceRegistry,
service_id: &ServiceId,
diff --git a/crates/runtime_manager/tests/fixtures/hardened_service_management.v1.toml b/crates/runtime_manager/tests/fixtures/hardened_service_management.v1.toml
@@ -0,0 +1,90 @@
+schema = "radroots-runtime-management"
+schema_version = 1
+owner_doc = "service-hardening-v1"
+runtime_registry = "registry.toml"
+distribution_contract = "hardened-service-targets.v1.toml"
+capabilities_contract = "service-capabilities.v1.toml"
+
+[defaults]
+instance_cardinality = "multiple"
+managed_runtime_lookup = "typed_instance_registry"
+explicit_runtime_endpoint_overrides_precede_managed_instance_binding = true
+global_path_mutation_forbidden = true
+
+[management_clients]
+active = ["cli"]
+
+[managed_runtime_targets]
+defined = ["myc", "rhi"]
+
+[lifecycle]
+actions = []
+destructive_actions = []
+health_states = []
+
+[mode.interactive_user_managed]
+contract_state = "active"
+platforms = ["linux", "macos"]
+supported_profiles = ["interactive", "repo_local"]
+service_manager_integration = false
+uses_absolute_binary_paths = true
+default_instance_cardinality = "multiple"
+
+[mode.service_host_managed]
+contract_state = "active"
+platforms = ["linux"]
+supported_profiles = ["service_host"]
+service_manager_integration = true
+uses_absolute_binary_paths = true
+default_instance_cardinality = "multiple"
+
+[paths.context_bound]
+shared_namespace = "services"
+instance_registry_root_class = "config"
+instance_registry_rel = "instances.toml"
+artifact_cache_root_class = "cache"
+artifact_cache_rel = "artifacts"
+install_root_class = "state"
+install_root_rel = "installs"
+state_root_class = "state"
+state_root_rel = "state.sqlite"
+logs_root_class = "logs"
+logs_root_rel = "instances"
+run_root_class = "run"
+run_root_rel = "admin.sock"
+secrets_root_class = "secrets"
+secrets_namespace_rel = "credentials"
+
+[instance_metadata]
+required_fields = ["service_id", "instance_id"]
+optional_fields = []
+
+[service_targets.myc]
+service_id = "myc"
+instance_support = "multiple"
+config_format = "toml"
+state_initialization = "explicit"
+run_state_policy = "existing_only"
+admin_transport = "http11_over_unix_domain_socket"
+admin_base_path = "/v1"
+admin_contract_version = 1
+status_surface = "local_admin_service_status_v1"
+operations_surface = "cached_livez_readyz_metrics"
+support_posture = "target"
+tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
+
+[service_targets.rhi]
+service_id = "rhi"
+instance_support = "multiple"
+config_format = "toml"
+state_initialization = "explicit"
+run_state_policy = "existing_only"
+admin_transport = "http11_over_unix_domain_socket"
+admin_base_path = "/v1"
+admin_contract_version = 1
+status_surface = "local_admin_service_status_v1"
+operations_surface = "cached_livez_readyz_metrics"
+support_posture = "target"
+tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
+
+[bootstrap]
diff --git a/crates/runtime_manager/tests/service_target_boundary.rs b/crates/runtime_manager/tests/service_target_boundary.rs
@@ -0,0 +1,25 @@
+const MANAGEMENT_FIXTURE: &str = include_str!("fixtures/hardened_service_management.v1.toml");
+
+#[test]
+fn hardened_services_remain_metadata_only_in_management_contract() {
+ for forbidden in [
+ "active = [\"myc",
+ "active = [\"rhi",
+ "install_strategy",
+ "binary_name",
+ "artifact_adapter",
+ "qualified",
+ "default_instance_id",
+ "preferred_cli_binding",
+ ] {
+ assert!(
+ !MANAGEMENT_FIXTURE.contains(forbidden),
+ "management fixture contains deferred authority `{forbidden}`"
+ );
+ }
+
+ assert!(MANAGEMENT_FIXTURE.contains("defined = [\"myc\", \"rhi\"]"));
+ assert!(MANAGEMENT_FIXTURE.contains("actions = []"));
+ assert!(MANAGEMENT_FIXTURE.contains("destructive_actions = []"));
+ assert!(MANAGEMENT_FIXTURE.contains("[bootstrap]"));
+}