lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

lib.rs (40444B)


      1 #![forbid(unsafe_code)]
      2 
      3 pub mod error;
      4 pub mod model;
      5 pub mod resolve;
      6 pub mod service;
      7 mod service_artifact;
      8 
      9 pub use error::RadrootsRuntimeDistributionError;
     10 pub use model::{
     11     ArchiveFormat, ArtifactAdapter, ChannelSet, DistributionFamily,
     12     RadrootsRuntimeDistributionContract, RuntimeDistributionEntry, TargetSet, TargetSpec,
     13 };
     14 pub use resolve::{
     15     RUNTIME_DISTRIBUTION_CONTRACT_MAX_UTF8_BYTES, RUNTIME_DISTRIBUTION_SCHEMA,
     16     RUNTIME_DISTRIBUTION_SCHEMA_VERSION, RadrootsRuntimeDistributionResolver,
     17     ResolvedRuntimeArtifact, ResolvedServiceArtifact, ResolvedServiceTarget,
     18     RuntimeArtifactRequest, ServiceArtifactRequest, ServiceTargetRequest,
     19 };
     20 pub use service::{
     21     HardenedServiceTarget, HardenedServiceTargets, ServiceAdminBasePath, ServiceAdminTransport,
     22     ServiceConfigurationFormat, ServiceInstanceSupport, ServiceOperationsSurface,
     23     ServiceRunStatePolicy, ServiceStateInitialization, ServiceStatusSurface, ServiceSupportPosture,
     24     ServiceTier1Target,
     25 };
     26 pub use service_artifact::{
     27     HardenedServiceArtifact, HardenedServiceArtifacts, ServiceArtifactChannel,
     28     ServiceArtifactSha256,
     29 };
     30 
     31 #[cfg(test)]
     32 mod tests {
     33     use radroots_runtime_paths::ServiceId;
     34     use toml::Value;
     35 
     36     use super::{
     37         HardenedServiceTarget, RUNTIME_DISTRIBUTION_CONTRACT_MAX_UTF8_BYTES,
     38         RUNTIME_DISTRIBUTION_SCHEMA, RadrootsRuntimeDistributionContract,
     39         RadrootsRuntimeDistributionError, RadrootsRuntimeDistributionResolver,
     40         RuntimeArtifactRequest, RuntimeDistributionEntry, ServiceAdminBasePath,
     41         ServiceAdminTransport, ServiceArtifactRequest, ServiceArtifactSha256,
     42         ServiceConfigurationFormat, ServiceInstanceSupport, ServiceOperationsSurface,
     43         ServiceRunStatePolicy, ServiceStateInitialization, ServiceStatusSurface,
     44         ServiceSupportPosture, ServiceTargetRequest, ServiceTier1Target,
     45     };
     46 
     47     const HARDENED_SERVICE_CONTRACT: &str =
     48         include_str!("../tests/fixtures/hardened_service_targets.v1.toml");
     49 
     50     const CONTRACT: &str = r#"
     51 schema = "radroots-runtime-distribution"
     52 schema_version = 1
     53 owner_doc = "docs/execution/rcl/radroots-modular-runtime-management-bootstrap-rcl.md"
     54 runtime_registry = "registry.toml"
     55 
     56 [family]
     57 id = "radroots_runtime-family"
     58 canonical_installer_engine = "single_runtime_selected"
     59 human_install_facade = "delivery_publication_only"
     60 tooling_consumption = "shared_distribution_library"
     61 independent_runtime_versions = true
     62 version_resolution = "runtime_scoped_channel_latest"
     63 artifact_verification_required = true
     64 
     65 [channels]
     66 active = ["stable"]
     67 defined = ["stable", "candidate", "nightly"]
     68 
     69 [artifact_adapters.rust_binary_archive]
     70 kind = "binary_archive"
     71 supported_archive_formats = ["tar.gz", "zip"]
     72 layout = "single_binary_plus_supporting_files"
     73 
     74 [artifact_adapters.desktop_bundle]
     75 kind = "desktop_bundle"
     76 supported_archive_formats = ["tar.gz", "zip", "dmg"]
     77 layout = "host_native_bundle"
     78 
     79 [artifact_adapters.mobile_store_package]
     80 kind = "mobile_store_package"
     81 supported_archive_formats = []
     82 layout = "platform_store_managed"
     83 
     84 [artifact_adapters.mojo_workspace_archive]
     85 kind = "workspace_archive"
     86 supported_archive_formats = ["tar.gz"]
     87 layout = "workspace_tree"
     88 
     89 [archive_formats.tar_gz]
     90 extension = ".tar.gz"
     91 platforms = ["linux", "macos"]
     92 
     93 [archive_formats.zip]
     94 extension = ".zip"
     95 platforms = ["windows"]
     96 
     97 [archive_formats.dmg]
     98 extension = ".dmg"
     99 platforms = ["macos"]
    100 
    101 [target_sets.server_default]
    102 targets = [
    103   "x86_64-unknown-linux-gnu",
    104   "aarch64-unknown-linux-gnu",
    105   "x86_64-apple-darwin",
    106   "aarch64-apple-darwin",
    107 ]
    108 
    109 [target_sets.cli_default]
    110 targets = [
    111   "x86_64-unknown-linux-gnu",
    112   "aarch64-unknown-linux-gnu",
    113   "x86_64-apple-darwin",
    114   "aarch64-apple-darwin",
    115 ]
    116 
    117 [target_sets.desktop_default]
    118 targets = [
    119   "x86_64-apple-darwin",
    120   "aarch64-apple-darwin",
    121 ]
    122 
    123 [target_sets.mojo_workspace_default]
    124 targets = [
    125   "osx-arm64",
    126   "linux-64",
    127 ]
    128 
    129 [targets.x86_64-unknown-linux-gnu]
    130 os = "linux"
    131 arch = "amd64"
    132 archive_format = "tar.gz"
    133 
    134 [targets.aarch64-unknown-linux-gnu]
    135 os = "linux"
    136 arch = "arm64"
    137 archive_format = "tar.gz"
    138 
    139 [targets.x86_64-apple-darwin]
    140 os = "macos"
    141 arch = "amd64"
    142 archive_format = "tar.gz"
    143 
    144 [targets.aarch64-apple-darwin]
    145 os = "macos"
    146 arch = "arm64"
    147 archive_format = "tar.gz"
    148 
    149 [targets.osx-arm64]
    150 os = "macos"
    151 arch = "arm64"
    152 archive_format = "tar.gz"
    153 
    154 [targets.linux-64]
    155 os = "linux"
    156 arch = "amd64"
    157 archive_format = "tar.gz"
    158 
    159 [[runtime]]
    160 id = "cli"
    161 distribution_state = "active"
    162 release_unit = "cli"
    163 package_name = "radroots_cli"
    164 binary_name = "radroots"
    165 artifact_adapter = "rust_binary_archive"
    166 target_set = "cli_default"
    167 default_channel = "stable"
    168 human_installable = true
    169 
    170 [[runtime]]
    171 id = "radrootsd"
    172 distribution_state = "active"
    173 release_unit = "radrootsd"
    174 package_name = "radrootsd"
    175 binary_name = "radrootsd"
    176 artifact_adapter = "rust_binary_archive"
    177 target_set = "server_default"
    178 default_channel = "stable"
    179 human_installable = true
    180 
    181 [[runtime]]
    182 id = "fixture-desktop-bundle"
    183 distribution_state = "defined"
    184 release_unit = "fixture-desktop-bundle"
    185 package_name = "fixture_desktop_bundle"
    186 binary_name = "fixture_desktop_bundle"
    187 artifact_adapter = "desktop_bundle"
    188 target_set = "desktop_default"
    189 default_channel = "stable"
    190 human_installable = true
    191 
    192 [[runtime]]
    193 id = "fixture-mobile-package"
    194 distribution_state = "external_platform_managed"
    195 release_unit = "fixture-mobile-package"
    196 package_name = "fixture_mobile_package"
    197 artifact_adapter = "mobile_store_package"
    198 default_channel = "stable"
    199 human_installable = false
    200 
    201 [[runtime]]
    202 id = "hyf"
    203 distribution_state = "bootstrap_only"
    204 release_unit = "hyf"
    205 package_name = "hyf"
    206 binary_name = "hyf"
    207 artifact_adapter = "mojo_workspace_archive"
    208 target_set = "mojo_workspace_default"
    209 default_channel = "stable"
    210 human_installable = false
    211 
    212 [service_targets.myc]
    213 service_id = "myc"
    214 instance_support = "multiple"
    215 config_format = "toml"
    216 state_initialization = "explicit"
    217 run_state_policy = "existing_only"
    218 admin_transport = "http11_over_unix_domain_socket"
    219 admin_base_path = "/v1"
    220 admin_contract_version = 1
    221 status_surface = "local_admin_service_status_v1"
    222 operations_surface = "cached_livez_readyz_metrics"
    223 support_posture = "target"
    224 tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
    225 
    226 [service_targets.rhi]
    227 service_id = "rhi"
    228 instance_support = "multiple"
    229 config_format = "toml"
    230 state_initialization = "explicit"
    231 run_state_policy = "existing_only"
    232 admin_transport = "http11_over_unix_domain_socket"
    233 admin_base_path = "/v1"
    234 admin_contract_version = 1
    235 status_surface = "local_admin_service_status_v1"
    236 operations_surface = "cached_livez_readyz_metrics"
    237 support_posture = "target"
    238 tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
    239 
    240 [service_artifacts.myc]
    241 service_id = "myc"
    242 service_revision = "77b381648ed1e586efb696888beb05b9215c69cf"
    243 release_contract = "contracts/services_hardening/native_release.v2.json"
    244 release_contract_sha256 = "4b3ba5789fac6aa219e84e1e5c002cf8230b72f95fd6d95a6419d2fdf2915f83"
    245 source_lock_sha256 = "f5ebb390a480830d51d502facc623bd1b10eda27b12dad9f3dbb6a1f1f949217"
    246 package_name = "myc"
    247 binary_name = "myc"
    248 version = "0.1.0"
    249 channel = "stable"
    250 binary_archive_name = "binary.tar.gz"
    251 artifact_manifest_name = "artifact-manifest.v1.json"
    252 checksums_name = "SHA256SUMS"
    253 output_inventory = ["LICENSE", "SHA256SUMS", "THIRD-PARTY-NOTICES.txt", "artifact-manifest.v1.json", "binary.tar.gz", "config.example.toml", "config.schema.json", "provenance-input.v1.json", "radroots.service.source-lock.v2.toml", "sbom.cdx.json", "service-source.tar.gz", "systemd.service"]
    254 tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
    255 
    256 [service_artifacts.rhi]
    257 service_id = "rhi"
    258 service_revision = "07aa6ea988da5372654bb3d1ee183ac099a77cae"
    259 release_contract = "contracts/services_hardening/native_release.v1.json"
    260 release_contract_sha256 = "06a973176b4b8c11dad13000604576527df829dd0bbe2f501158662f75e70b94"
    261 source_lock_sha256 = "3cc8bfac0d98730937754abae2ccfe20e40d0a9bbdefe02ebd94264c20f0d0ff"
    262 package_name = "rhi"
    263 binary_name = "rhi"
    264 version = "0.1.0"
    265 channel = "stable"
    266 binary_archive_name = "binary.tar.gz"
    267 artifact_manifest_name = "artifact-manifest.v1.json"
    268 checksums_name = "SHA256SUMS"
    269 output_inventory = ["LICENSE", "SHA256SUMS", "THIRD-PARTY-NOTICES.txt", "artifact-manifest.v1.json", "binary.tar.gz", "config.example.toml", "config.schema.json", "provenance-input.v1.json", "radroots.service.source-lock.v2.toml", "sbom.cdx.json", "service-source.tar.gz", "systemd.service"]
    270 tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
    271 "#;
    272 
    273     fn contract_value() -> Value {
    274         toml::from_str(CONTRACT).expect("parse contract value")
    275     }
    276 
    277     fn resolver_from_value(value: Value) -> RadrootsRuntimeDistributionResolver {
    278         let raw = toml::to_string(&value).expect("serialize contract");
    279         RadrootsRuntimeDistributionResolver::parse_str(&raw).expect("parse resolver")
    280     }
    281 
    282     fn resolve_error(
    283         resolver: &RadrootsRuntimeDistributionResolver,
    284         request: RuntimeArtifactRequest<'_>,
    285     ) -> RadrootsRuntimeDistributionError {
    286         resolver
    287             .resolve_artifact(&request)
    288             .expect_err("request should fail")
    289     }
    290 
    291     #[test]
    292     fn parse_str_accepts_the_expected_schema() {
    293         let resolver =
    294             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    295 
    296         assert_eq!(resolver.contract().schema, RUNTIME_DISTRIBUTION_SCHEMA);
    297         assert_eq!(resolver.contract().runtime.len(), 5);
    298     }
    299 
    300     #[test]
    301     fn parse_str_rejects_invalid_toml() {
    302         let err = RadrootsRuntimeDistributionResolver::parse_str("schema = [")
    303             .expect_err("invalid toml should fail");
    304         assert_eq!(err, RadrootsRuntimeDistributionError::Parse);
    305     }
    306 
    307     #[test]
    308     fn parse_str_caps_the_complete_document_before_toml_parsing() {
    309         let mut exact = CONTRACT.to_owned();
    310         exact.push('#');
    311         exact.extend(std::iter::repeat_n(
    312             'x',
    313             RUNTIME_DISTRIBUTION_CONTRACT_MAX_UTF8_BYTES - exact.len(),
    314         ));
    315         assert_eq!(exact.len(), RUNTIME_DISTRIBUTION_CONTRACT_MAX_UTF8_BYTES);
    316         RadrootsRuntimeDistributionResolver::parse_str(&exact)
    317             .expect("exact maximum contract remains admissible");
    318 
    319         exact.push('x');
    320         assert_eq!(
    321             RadrootsRuntimeDistributionResolver::parse_str(&exact)
    322                 .expect_err("maximum plus one must fail"),
    323             RadrootsRuntimeDistributionError::ContractTooLarge
    324         );
    325 
    326         let very_large = format!("{}#{}", CONTRACT, "x".repeat(4 * 1024 * 1024));
    327         assert_eq!(
    328             RadrootsRuntimeDistributionResolver::parse_str(&very_large)
    329                 .expect_err("very large contract must fail"),
    330             RadrootsRuntimeDistributionError::ContractTooLarge
    331         );
    332     }
    333 
    334     #[test]
    335     fn new_rejects_unexpected_schema() {
    336         let mut contract = contract_value();
    337         contract["schema"] = Value::String("wrong-schema".to_string());
    338 
    339         let raw = toml::to_string(&contract).expect("serialize contract");
    340         let err = RadrootsRuntimeDistributionResolver::parse_str(&raw)
    341             .expect_err("unexpected schema should fail");
    342 
    343         assert_eq!(err, RadrootsRuntimeDistributionError::UnexpectedSchema);
    344     }
    345 
    346     #[test]
    347     fn resolves_cli_linux_artifact_with_explicit_channel() {
    348         let resolver =
    349             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    350 
    351         let artifact = resolver
    352             .resolve_artifact(&RuntimeArtifactRequest {
    353                 runtime_id: "cli",
    354                 os: "linux",
    355                 arch: "amd64",
    356                 version: "1.0.0-alpha.1",
    357                 channel: Some("stable"),
    358             })
    359             .expect("resolve cli artifact");
    360 
    361         assert_eq!(artifact.binary_name.as_deref(), Some("radroots"));
    362         assert_eq!(artifact.target_id, "x86_64-unknown-linux-gnu");
    363         assert_eq!(artifact.archive_extension, ".tar.gz");
    364         assert_eq!(
    365             artifact.artifact_file_name,
    366             "cli-1.0.0-alpha.1-x86_64-unknown-linux-gnu.tar.gz"
    367         );
    368     }
    369 
    370     #[test]
    371     fn resolves_radrootsd_linux_arm64_using_default_channel() {
    372         let resolver =
    373             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    374 
    375         let artifact = resolver
    376             .resolve_artifact(&RuntimeArtifactRequest {
    377                 runtime_id: "radrootsd",
    378                 os: "linux",
    379                 arch: "arm64",
    380                 version: "1.0.0-alpha.1",
    381                 channel: None,
    382             })
    383             .expect("resolve radrootsd artifact");
    384 
    385         assert_eq!(artifact.channel, "stable");
    386         assert_eq!(artifact.target_id, "aarch64-unknown-linux-gnu");
    387         assert_eq!(artifact.binary_name.as_deref(), Some("radrootsd"));
    388     }
    389 
    390     #[test]
    391     fn resolves_desktop_bundle_for_macos_arm64() {
    392         let resolver =
    393             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    394 
    395         let artifact = resolver
    396             .resolve_artifact(&RuntimeArtifactRequest {
    397                 runtime_id: "fixture-desktop-bundle",
    398                 os: "macos",
    399                 arch: "arm64",
    400                 version: "1.0.0-alpha.1",
    401                 channel: Some("stable"),
    402             })
    403             .expect("resolve desktop artifact");
    404 
    405         assert_eq!(artifact.target_id, "aarch64-apple-darwin");
    406         assert_eq!(artifact.package_name, "fixture_desktop_bundle");
    407     }
    408 
    409     #[test]
    410     fn rejects_non_installable_mobile_runtime() {
    411         let resolver =
    412             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    413 
    414         let err = resolver
    415             .resolve_artifact(&RuntimeArtifactRequest {
    416                 runtime_id: "fixture-mobile-package",
    417                 os: "macos",
    418                 arch: "arm64",
    419                 version: "1.0.0-alpha.1",
    420                 channel: Some("stable"),
    421             })
    422             .expect_err("mobile runtime should not be installable");
    423 
    424         assert_eq!(err, RadrootsRuntimeDistributionError::RuntimeNotInstallable);
    425     }
    426 
    427     #[test]
    428     fn rejects_bootstrap_only_runtime() {
    429         let resolver =
    430             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    431 
    432         let err = resolver
    433             .resolve_artifact(&RuntimeArtifactRequest {
    434                 runtime_id: "hyf",
    435                 os: "macos",
    436                 arch: "arm64",
    437                 version: "0.1.0",
    438                 channel: Some("stable"),
    439             })
    440             .expect_err("bootstrap runtime should not be installable");
    441 
    442         assert_eq!(err, RadrootsRuntimeDistributionError::RuntimeNotInstallable);
    443     }
    444 
    445     #[test]
    446     fn rejects_inactive_channel() {
    447         let resolver =
    448             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    449 
    450         let err = resolver
    451             .resolve_artifact(&RuntimeArtifactRequest {
    452                 runtime_id: "cli",
    453                 os: "linux",
    454                 arch: "amd64",
    455                 version: "1.0.0-alpha.1",
    456                 channel: Some("candidate"),
    457             })
    458             .expect_err("candidate channel should be inactive");
    459 
    460         assert_eq!(err, RadrootsRuntimeDistributionError::InactiveChannel);
    461     }
    462 
    463     #[test]
    464     fn rejects_unknown_runtime() {
    465         let resolver =
    466             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    467 
    468         let err = resolve_error(
    469             &resolver,
    470             RuntimeArtifactRequest {
    471                 runtime_id: "missing-runtime",
    472                 os: "linux",
    473                 arch: "amd64",
    474                 version: "1.0.0-alpha.1",
    475                 channel: Some("stable"),
    476             },
    477         );
    478 
    479         assert_eq!(err, RadrootsRuntimeDistributionError::UnknownRuntime);
    480     }
    481 
    482     #[test]
    483     fn rejects_unknown_channel() {
    484         let resolver =
    485             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    486 
    487         let err = resolve_error(
    488             &resolver,
    489             RuntimeArtifactRequest {
    490                 runtime_id: "cli",
    491                 os: "linux",
    492                 arch: "amd64",
    493                 version: "1.0.0-alpha.1",
    494                 channel: Some("beta"),
    495             },
    496         );
    497 
    498         assert_eq!(err, RadrootsRuntimeDistributionError::UnknownChannel);
    499     }
    500 
    501     #[test]
    502     fn rejects_unsupported_platform() {
    503         let resolver =
    504             RadrootsRuntimeDistributionResolver::parse_str(CONTRACT).expect("parse contract");
    505 
    506         let err = resolver
    507             .resolve_artifact(&RuntimeArtifactRequest {
    508                 runtime_id: "radrootsd",
    509                 os: "windows",
    510                 arch: "amd64",
    511                 version: "1.0.0-alpha.1",
    512                 channel: Some("stable"),
    513             })
    514             .expect_err("windows target should be unsupported");
    515 
    516         assert_eq!(err, RadrootsRuntimeDistributionError::UnsupportedPlatform);
    517     }
    518 
    519     #[test]
    520     fn rejects_runtime_with_missing_target_set() {
    521         let mut contract = contract_value();
    522         let runtime = contract["runtime"]
    523             .as_array_mut()
    524             .expect("runtime array")
    525             .iter_mut()
    526             .find(|runtime| runtime["id"].as_str() == Some("fixture-mobile-package"))
    527             .expect("ios runtime");
    528         runtime["human_installable"] = Value::Boolean(true);
    529 
    530         let resolver = resolver_from_value(contract);
    531         let err = resolve_error(
    532             &resolver,
    533             RuntimeArtifactRequest {
    534                 runtime_id: "fixture-mobile-package",
    535                 os: "ios",
    536                 arch: "arm64",
    537                 version: "1.0.0-alpha.1",
    538                 channel: Some("stable"),
    539             },
    540         );
    541 
    542         assert_eq!(err, RadrootsRuntimeDistributionError::MissingTargetSet);
    543     }
    544 
    545     #[test]
    546     fn rejects_unknown_artifact_adapter() {
    547         let mut contract = contract_value();
    548         let runtime = contract["runtime"]
    549             .as_array_mut()
    550             .expect("runtime array")
    551             .iter_mut()
    552             .find(|runtime| runtime["id"].as_str() == Some("cli"))
    553             .expect("cli runtime");
    554         runtime["artifact_adapter"] = Value::String("missing_adapter".to_string());
    555 
    556         let resolver = resolver_from_value(contract);
    557         let err = resolve_error(
    558             &resolver,
    559             RuntimeArtifactRequest {
    560                 runtime_id: "cli",
    561                 os: "linux",
    562                 arch: "amd64",
    563                 version: "1.0.0-alpha.1",
    564                 channel: Some("stable"),
    565             },
    566         );
    567 
    568         assert_eq!(
    569             err,
    570             RadrootsRuntimeDistributionError::UnknownArtifactAdapter
    571         );
    572     }
    573 
    574     #[test]
    575     fn rejects_missing_target_set_definition() {
    576         let mut contract = contract_value();
    577         let runtime = contract["runtime"]
    578             .as_array_mut()
    579             .expect("runtime array")
    580             .iter_mut()
    581             .find(|runtime| runtime["id"].as_str() == Some("cli"))
    582             .expect("cli runtime");
    583         runtime["target_set"] = Value::String("missing-target-set".to_string());
    584 
    585         let resolver = resolver_from_value(contract);
    586         let err = resolve_error(
    587             &resolver,
    588             RuntimeArtifactRequest {
    589                 runtime_id: "cli",
    590                 os: "linux",
    591                 arch: "amd64",
    592                 version: "1.0.0-alpha.1",
    593                 channel: Some("stable"),
    594             },
    595         );
    596 
    597         assert_eq!(err, RadrootsRuntimeDistributionError::UnsupportedPlatform);
    598     }
    599 
    600     #[test]
    601     fn rejects_target_set_with_unknown_target() {
    602         let mut contract = contract_value();
    603         contract["target_sets"]["cli_default"]["targets"] =
    604             Value::Array(vec![Value::String("missing-target".to_string())]);
    605 
    606         let resolver = resolver_from_value(contract);
    607         let err = resolve_error(
    608             &resolver,
    609             RuntimeArtifactRequest {
    610                 runtime_id: "cli",
    611                 os: "linux",
    612                 arch: "amd64",
    613                 version: "1.0.0-alpha.1",
    614                 channel: Some("stable"),
    615             },
    616         );
    617 
    618         assert_eq!(err, RadrootsRuntimeDistributionError::UnknownTarget);
    619     }
    620 
    621     #[test]
    622     fn infers_archive_format_from_single_supported_adapter_format() {
    623         let mut contract = contract_value();
    624         contract["targets"]["x86_64-unknown-linux-gnu"]
    625             .as_table_mut()
    626             .expect("target table")
    627             .remove("archive_format");
    628         contract["artifact_adapters"]["rust_binary_archive"]["supported_archive_formats"] =
    629             Value::Array(vec![Value::String("tar.gz".to_string())]);
    630 
    631         let resolver = resolver_from_value(contract);
    632         let artifact = resolver
    633             .resolve_artifact(&RuntimeArtifactRequest {
    634                 runtime_id: "cli",
    635                 os: "linux",
    636                 arch: "amd64",
    637                 version: "1.0.0-alpha.1",
    638                 channel: Some("stable"),
    639             })
    640             .expect("single supported format should be inferred");
    641 
    642         assert_eq!(artifact.archive_format, "tar.gz");
    643         assert_eq!(artifact.archive_extension, ".tar.gz");
    644     }
    645 
    646     #[test]
    647     fn rejects_unknown_archive_format_reference() {
    648         let mut contract = contract_value();
    649         contract["targets"]["x86_64-unknown-linux-gnu"]["archive_format"] =
    650             Value::String("tar.xz".to_string());
    651 
    652         let resolver = resolver_from_value(contract);
    653         let err = resolve_error(
    654             &resolver,
    655             RuntimeArtifactRequest {
    656                 runtime_id: "cli",
    657                 os: "linux",
    658                 arch: "amd64",
    659                 version: "1.0.0-alpha.1",
    660                 channel: Some("stable"),
    661             },
    662         );
    663 
    664         assert_eq!(err, RadrootsRuntimeDistributionError::UnknownArchiveFormat);
    665     }
    666 
    667     #[test]
    668     fn rejects_missing_archive_format_when_adapter_is_ambiguous() {
    669         let mut contract = contract_value();
    670         contract["targets"]["aarch64-apple-darwin"]
    671             .as_table_mut()
    672             .expect("target table")
    673             .remove("archive_format");
    674 
    675         let resolver = resolver_from_value(contract);
    676         let err = resolve_error(
    677             &resolver,
    678             RuntimeArtifactRequest {
    679                 runtime_id: "fixture-desktop-bundle",
    680                 os: "macos",
    681                 arch: "arm64",
    682                 version: "1.0.0-alpha.1",
    683                 channel: Some("stable"),
    684             },
    685         );
    686 
    687         assert_eq!(err, RadrootsRuntimeDistributionError::MissingArchiveFormat);
    688     }
    689 
    690     #[test]
    691     fn durable_contract_resolves_exact_hardened_service_metadata() {
    692         let resolver = RadrootsRuntimeDistributionResolver::parse_str(HARDENED_SERVICE_CONTRACT)
    693             .expect("hardened service contract");
    694 
    695         for service in ["myc", "rhi"] {
    696             let service_id = ServiceId::new(service).expect("service id");
    697             let metadata = resolver
    698                 .service_target(&service_id)
    699                 .expect("service metadata");
    700             assert_eq!(metadata.service_id(), &service_id);
    701             assert_eq!(
    702                 metadata.instance_support(),
    703                 ServiceInstanceSupport::Multiple
    704             );
    705             assert_eq!(metadata.config_format(), ServiceConfigurationFormat::Toml);
    706             assert_eq!(metadata.config_format().as_str(), "toml");
    707             assert_eq!(
    708                 metadata.state_initialization(),
    709                 ServiceStateInitialization::Explicit
    710             );
    711             assert_eq!(
    712                 metadata.run_state_policy(),
    713                 ServiceRunStatePolicy::ExistingOnly
    714             );
    715             assert_eq!(
    716                 metadata.admin_transport(),
    717                 ServiceAdminTransport::Http11OverUnixDomainSocket
    718             );
    719             assert_eq!(metadata.admin_base_path(), ServiceAdminBasePath::V1);
    720             assert_eq!(metadata.admin_contract_version(), 1);
    721             assert_eq!(
    722                 metadata.status_surface(),
    723                 ServiceStatusSurface::LocalAdminServiceStatusV1
    724             );
    725             assert_eq!(
    726                 metadata.operations_surface(),
    727                 ServiceOperationsSurface::CachedLivezReadyzMetrics
    728             );
    729             assert_eq!(
    730                 metadata.operations_surface().routes(),
    731                 ["/livez", "/readyz", "/metrics"]
    732             );
    733             assert_eq!(metadata.support_posture(), ServiceSupportPosture::Target);
    734             assert_eq!(metadata.tier_1_targets(), ServiceTier1Target::ALL);
    735 
    736             for target in ServiceTier1Target::ALL {
    737                 let resolved = resolver
    738                     .resolve_service_target(&ServiceTargetRequest {
    739                         service_id: &service_id,
    740                         target_id: target.as_str(),
    741                     })
    742                     .expect("eligible target");
    743                 assert_eq!(resolved.service_id(), &service_id);
    744                 assert_eq!(resolved.target(), target);
    745             }
    746         }
    747 
    748         let targets = &resolver.contract().service_targets;
    749         assert_eq!(targets.len(), 2);
    750         assert!(!targets.is_empty());
    751         assert_eq!(
    752             targets
    753                 .iter()
    754                 .map(|(service, _)| service)
    755                 .collect::<Vec<_>>(),
    756             ["myc", "rhi"]
    757         );
    758         let artifacts = &resolver.contract().service_artifacts;
    759         assert_eq!(artifacts.len(), 2);
    760         assert!(!artifacts.is_empty());
    761         assert_eq!(
    762             artifacts
    763                 .iter()
    764                 .map(|(service, _)| service)
    765                 .collect::<Vec<_>>(),
    766             ["myc", "rhi"]
    767         );
    768 
    769         let literal = ServiceArtifactSha256::from_bytes([0x5a; 32]);
    770         assert_eq!(literal.as_bytes(), &[0x5a; 32]);
    771         assert_eq!(format!("{literal:?}"), "ServiceArtifactSha256(<redacted>)");
    772     }
    773 
    774     #[test]
    775     fn hardened_services_resolve_exact_native_artifacts_and_reject_unsupported_targets() {
    776         let resolver = RadrootsRuntimeDistributionResolver::parse_str(HARDENED_SERVICE_CONTRACT)
    777             .expect("hardened service contract");
    778         let myc = ServiceId::new("myc").expect("myc");
    779 
    780         for target_id in ["aarch64-apple-darwin", "x86_64-pc-windows-msvc", "linux-64"] {
    781             assert_eq!(
    782                 resolver.resolve_service_target(&ServiceTargetRequest {
    783                     service_id: &myc,
    784                     target_id,
    785                 }),
    786                 Err(RadrootsRuntimeDistributionError::UnsupportedServiceTarget)
    787             );
    788         }
    789         let artifact = resolver
    790             .resolve_service_artifact(&ServiceArtifactRequest {
    791                 service_id: &myc,
    792                 target_id: "x86_64-unknown-linux-gnu",
    793             })
    794             .expect("Myc artifact");
    795         assert_eq!(artifact.service_id(), &myc);
    796         assert_eq!(artifact.target(), ServiceTier1Target::X86_64UnknownLinuxGnu);
    797         assert_eq!(artifact.version(), "0.1.0");
    798         assert_eq!(artifact.package_name(), "myc");
    799         assert_eq!(artifact.binary_name(), "myc");
    800         assert_eq!(artifact.channel(), "stable");
    801         assert_eq!(artifact.binary_archive_name(), "binary.tar.gz");
    802         assert_eq!(artifact.binary_archive_format(), "tar.gz");
    803         assert_eq!(
    804             artifact.binary_archive_member(),
    805             "myc-0.1.0-x86_64-unknown-linux-gnu/myc"
    806         );
    807         assert_eq!(
    808             artifact.artifact_manifest_name(),
    809             "artifact-manifest.v1.json"
    810         );
    811         assert_eq!(artifact.checksums_name(), "SHA256SUMS");
    812         assert_eq!(artifact.checksum_algorithm(), "sha256");
    813         assert_eq!(
    814             artifact.checksum_format(),
    815             "sha256_lower_hex_two_spaces_path_lf_sorted_by_path"
    816         );
    817         assert_eq!(artifact.output_inventory().len(), 12);
    818         assert_eq!(artifact.output_inventory()[0], "LICENSE");
    819         assert_eq!(artifact.output_inventory()[11], "systemd.service");
    820         assert_eq!(
    821             artifact.release_contract_sha256(),
    822             resolver
    823                 .service_artifact(&myc)
    824                 .expect("Myc release")
    825                 .release_contract_sha256()
    826         );
    827         assert_eq!(
    828             artifact.source_lock_sha256(),
    829             resolver
    830                 .service_artifact(&myc)
    831                 .expect("Myc release")
    832                 .source_lock_sha256()
    833         );
    834         let rendered = format!("{artifact:?}");
    835         assert!(!rendered.contains("4b3ba578"));
    836         assert!(!rendered.contains("f5ebb390"));
    837         let rhi = ServiceId::new("rhi").expect("rhi");
    838         let rhi_artifact = resolver
    839             .resolve_service_artifact(&ServiceArtifactRequest {
    840                 service_id: &rhi,
    841                 target_id: "aarch64-unknown-linux-gnu",
    842             })
    843             .expect("RHI artifact");
    844         assert_eq!(
    845             rhi_artifact.binary_archive_member(),
    846             "rhi-0.1.0-aarch64-unknown-linux-gnu/rhi"
    847         );
    848         assert_eq!(
    849             resolver.resolve_artifact(&RuntimeArtifactRequest {
    850                 runtime_id: "myc",
    851                 os: "linux",
    852                 arch: "amd64",
    853                 version: "1.0.0",
    854                 channel: None,
    855             }),
    856             Err(RadrootsRuntimeDistributionError::UnknownRuntime)
    857         );
    858     }
    859 
    860     #[test]
    861     fn hardened_services_reject_parsed_and_direct_artifact_authority() {
    862         let raw = format!(
    863             "{HARDENED_SERVICE_CONTRACT}\n\
    864              [[runtime]]\n\
    865              id = \"myc\"\n\
    866              distribution_state = \"defined\"\n\
    867              release_unit = \"myc\"\n\
    868              package_name = \"radroots_myc\"\n\
    869              artifact_adapter = \"rust_binary_archive\"\n\
    870              default_channel = \"stable\"\n\
    871              human_installable = true\n"
    872         );
    873         assert_eq!(
    874             RadrootsRuntimeDistributionResolver::parse_str(&raw).expect_err("parsed bypass"),
    875             RadrootsRuntimeDistributionError::HardenedServiceLegacyArtifactRow
    876         );
    877 
    878         let mut contract =
    879             toml::from_str::<RadrootsRuntimeDistributionContract>(HARDENED_SERVICE_CONTRACT)
    880                 .expect("direct contract");
    881         contract.runtime.push(RuntimeDistributionEntry {
    882             id: "rhi".to_owned(),
    883             distribution_state: "defined".to_owned(),
    884             release_unit: "rhi".to_owned(),
    885             package_name: "radroots_rhi".to_owned(),
    886             binary_name: None,
    887             artifact_adapter: "rust_binary_archive".to_owned(),
    888             target_set: None,
    889             default_channel: "stable".to_owned(),
    890             human_installable: true,
    891             notes: None,
    892         });
    893         assert_eq!(
    894             RadrootsRuntimeDistributionResolver::new(contract).expect_err("direct bypass"),
    895             RadrootsRuntimeDistributionError::HardenedServiceLegacyArtifactRow
    896         );
    897     }
    898 
    899     #[test]
    900     fn hardened_service_artifacts_reject_every_identity_and_inventory_drift() {
    901         for (needle, replacement) in [
    902             (
    903                 "release_contract = \"contracts/services_hardening/native_release.v2.json\"",
    904                 "release_contract = \"contracts/services_hardening/untrusted.json\"",
    905             ),
    906             (
    907                 "f5ebb390a480830d51d502facc623bd1b10eda27b12dad9f3dbb6a1f1f949217",
    908                 "f5ebb390a480830d51d502facc623bd1b10eda27b12dad9f3dbb6a1f1f949218",
    909             ),
    910             ("package_name = \"myc\"", "package_name = \"other\""),
    911             ("binary_name = \"myc\"", "binary_name = \"other\""),
    912             ("version = \"0.1.0\"", "version = \"0.1.1\""),
    913             ("channel = \"stable\"", "channel = \"candidate\""),
    914             (
    915                 "binary_archive_name = \"binary.tar.gz\"",
    916                 "binary_archive_name = \"myc.tar.gz\"",
    917             ),
    918             (
    919                 "artifact_manifest_name = \"artifact-manifest.v1.json\"",
    920                 "artifact_manifest_name = \"untrusted.json\"",
    921             ),
    922             (
    923                 "checksums_name = \"SHA256SUMS\"",
    924                 "checksums_name = \"checksums.txt\"",
    925             ),
    926             (
    927                 "77b381648ed1e586efb696888beb05b9215c69cf",
    928                 "77b381648ed1e586efb696888beb05b9215c69ce",
    929             ),
    930             (
    931                 "4b3ba5789fac6aa219e84e1e5c002cf8230b72f95fd6d95a6419d2fdf2915f83",
    932                 "4b3ba5789fac6aa219e84e1e5c002cf8230b72f95fd6d95a6419d2fdf2915f84",
    933             ),
    934         ] {
    935             let drift = HARDENED_SERVICE_CONTRACT.replacen(needle, replacement, 1);
    936             assert!(
    937                 RadrootsRuntimeDistributionResolver::parse_str(&drift).is_err(),
    938                 "drift `{needle}` must fail"
    939             );
    940         }
    941 
    942         let missing_member =
    943             HARDENED_SERVICE_CONTRACT.replacen("\"systemd.service\"", "\"unexpected\"", 1);
    944         assert!(RadrootsRuntimeDistributionResolver::parse_str(&missing_member).is_err());
    945         let uppercase_hash = HARDENED_SERVICE_CONTRACT.replacen("4b3ba578", "4B3BA578", 1);
    946         assert!(RadrootsRuntimeDistributionResolver::parse_str(&uppercase_hash).is_err());
    947         let short_hash = HARDENED_SERVICE_CONTRACT.replacen(
    948             "4b3ba5789fac6aa219e84e1e5c002cf8230b72f95fd6d95a6419d2fdf2915f83",
    949             "00",
    950             1,
    951         );
    952         assert!(RadrootsRuntimeDistributionResolver::parse_str(&short_hash).is_err());
    953         let unknown = HARDENED_SERVICE_CONTRACT.replacen(
    954             "[service_artifacts.myc]",
    955             "[service_artifacts.myc]\nunknown = true",
    956             1,
    957         );
    958         assert!(RadrootsRuntimeDistributionResolver::parse_str(&unknown).is_err());
    959 
    960         let mut unsupported_service: Value =
    961             toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
    962         unsupported_service["service_artifacts"]["myc"]["service_id"] =
    963             Value::String("unsupported".to_owned());
    964         assert!(
    965             RadrootsRuntimeDistributionResolver::parse_str(
    966                 &toml::to_string(&unsupported_service).expect("unsupported-service contract")
    967             )
    968             .is_err()
    969         );
    970 
    971         let mut missing_artifact: Value =
    972             toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
    973         let artifacts = missing_artifact["service_artifacts"]
    974             .as_table_mut()
    975             .expect("service artifact table");
    976         let rhi = artifacts.remove("rhi").expect("RHI artifact");
    977         artifacts.insert("other".to_owned(), rhi);
    978         assert!(
    979             RadrootsRuntimeDistributionResolver::parse_str(
    980                 &toml::to_string(&missing_artifact).expect("missing-artifact contract")
    981             )
    982             .is_err()
    983         );
    984 
    985         let mut empty_artifacts: Value =
    986             toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
    987         empty_artifacts["service_artifacts"]
    988             .as_table_mut()
    989             .expect("service artifact table")
    990             .clear();
    991         assert!(
    992             RadrootsRuntimeDistributionResolver::parse_str(
    993                 &toml::to_string(&empty_artifacts).expect("empty-artifact contract")
    994             )
    995             .is_err()
    996         );
    997     }
    998 
    999     #[test]
   1000     fn hardened_service_contract_rejects_schema_drift_unknown_fields_and_inventory_drift() {
   1001         let mut target_drift: Value =
   1002             toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
   1003         target_drift["service_targets"]["myc"]["tier_1_targets"][1] =
   1004             Value::String("aarch64-apple-darwin".to_owned());
   1005 
   1006         for raw in [
   1007             HARDENED_SERVICE_CONTRACT.replace("schema_version = 1", "schema_version = 2"),
   1008             format!("{HARDENED_SERVICE_CONTRACT}\nunknown = true\n"),
   1009             HARDENED_SERVICE_CONTRACT.replace("service_id = \"rhi\"", "service_id = \"other\""),
   1010             toml::to_string(&target_drift).expect("target-drift contract"),
   1011         ] {
   1012             assert!(RadrootsRuntimeDistributionResolver::parse_str(&raw).is_err());
   1013         }
   1014 
   1015         let mut missing_service: Value =
   1016             toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
   1017         let targets = missing_service["service_targets"]
   1018             .as_table_mut()
   1019             .expect("service target table");
   1020         let rhi = targets.remove("rhi").expect("RHI target");
   1021         targets.insert("other".to_owned(), rhi);
   1022         assert!(
   1023             RadrootsRuntimeDistributionResolver::parse_str(
   1024                 &toml::to_string(&missing_service).expect("missing-service contract")
   1025             )
   1026             .is_err()
   1027         );
   1028 
   1029         let mut empty_targets: Value =
   1030             toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
   1031         empty_targets["service_targets"]
   1032             .as_table_mut()
   1033             .expect("service target table")
   1034             .clear();
   1035         assert!(
   1036             RadrootsRuntimeDistributionResolver::parse_str(
   1037                 &toml::to_string(&empty_targets).expect("empty-target contract")
   1038             )
   1039             .is_err()
   1040         );
   1041 
   1042         let mut mismatched_service: Value =
   1043             toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
   1044         let targets = mismatched_service["service_targets"]
   1045             .as_table_mut()
   1046             .expect("service target table");
   1047         targets["myc"]["service_id"] = Value::String("rhi".to_owned());
   1048         assert!(
   1049             RadrootsRuntimeDistributionResolver::parse_str(
   1050                 &toml::to_string(&mismatched_service).expect("mismatched-service contract")
   1051             )
   1052             .is_err()
   1053         );
   1054     }
   1055 
   1056     #[test]
   1057     fn hardened_service_contract_requires_stable_in_both_channel_inventories() {
   1058         for channel_inventory in ["active", "defined"] {
   1059             let mut contract: Value =
   1060                 toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
   1061             contract["channels"][channel_inventory] =
   1062                 Value::Array(vec![Value::String("candidate".to_owned())]);
   1063             assert_eq!(
   1064                 RadrootsRuntimeDistributionResolver::parse_str(
   1065                     &toml::to_string(&contract).expect("channel-drift contract")
   1066                 )
   1067                 .expect_err("stable channel is mandatory"),
   1068                 RadrootsRuntimeDistributionError::InvalidServiceArtifactContract
   1069             );
   1070         }
   1071     }
   1072 
   1073     #[test]
   1074     fn standalone_hardened_service_target_rejects_contract_drift() {
   1075         let contract: Value =
   1076             toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value");
   1077         let target =
   1078             toml::to_string(&contract["service_targets"]["myc"]).expect("standalone target");
   1079         let parsed = toml::from_str::<HardenedServiceTarget>(&target).expect("valid target");
   1080         assert_eq!(parsed.service_id().as_str(), "myc");
   1081 
   1082         for raw in [
   1083             target.replace("admin_contract_version = 1", "admin_contract_version = 99"),
   1084             target.replace("service_id = \"myc\"", "service_id = \"unsupported\""),
   1085             target.replace(
   1086                 "\"x86_64-unknown-linux-gnu\", \"aarch64-unknown-linux-gnu\"",
   1087                 "\"aarch64-unknown-linux-gnu\"",
   1088             ),
   1089         ] {
   1090             assert!(toml::from_str::<HardenedServiceTarget>(&raw).is_err());
   1091         }
   1092     }
   1093 
   1094     #[test]
   1095     fn distribution_errors_do_not_expose_contract_values_or_parser_causes() {
   1096         use std::error::Error as _;
   1097 
   1098         for (raw, secret) in [
   1099             (
   1100                 HARDENED_SERVICE_CONTRACT.replace(
   1101                     "schema = \"radroots-runtime-distribution\"",
   1102                     "schema = \"secret-contract-value\"",
   1103                 ),
   1104                 "secret-contract-value",
   1105             ),
   1106             (
   1107                 "credential = 'secret-value'\ninvalid = [".to_owned(),
   1108                 "secret-value",
   1109             ),
   1110         ] {
   1111             let error =
   1112                 RadrootsRuntimeDistributionResolver::parse_str(&raw).expect_err("invalid contract");
   1113             let rendered = format!("{error} {error:?}");
   1114             assert!(!rendered.contains(secret));
   1115             assert!(error.source().is_none());
   1116         }
   1117     }
   1118 }