lib

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

commit cb2ef06a6fd3fa2157c87f6aa4c16d390c01e8f3
parent d287d41c2cd97cd0e455445da90f22180029f089
Author: triesap <tyson@radroots.org>
Date:   Tue, 25 Aug 2026 06:06:29 +0000

test(release): close promotion qualification gaps

Diffstat:
Mcrates/runtime_distribution/src/lib.rs | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mcrates/runtime_manager/src/managed.rs | 43+++++++++++++++++++++++++++++++++++++------
Mcrates/transport_nostr/src/client.rs | 2+-
Mcrates/transport_nostr/src/subscription.rs | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 207 insertions(+), 12 deletions(-)

diff --git a/crates/runtime_distribution/src/lib.rs b/crates/runtime_distribution/src/lib.rs @@ -899,6 +899,16 @@ tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] #[test] fn hardened_service_artifacts_reject_every_identity_and_inventory_drift() { for (needle, replacement) in [ + ( + "release_contract = \"contracts/services_hardening/native_release.v2.json\"", + "release_contract = \"contracts/services_hardening/untrusted.json\"", + ), + ( + "f5ebb390a480830d51d502facc623bd1b10eda27b12dad9f3dbb6a1f1f949217", + "f5ebb390a480830d51d502facc623bd1b10eda27b12dad9f3dbb6a1f1f949218", + ), + ("package_name = \"myc\"", "package_name = \"other\""), + ("binary_name = \"myc\"", "binary_name = \"other\""), ("version = \"0.1.0\"", "version = \"0.1.1\""), ("channel = \"stable\"", "channel = \"candidate\""), ( @@ -906,6 +916,10 @@ tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] "binary_archive_name = \"myc.tar.gz\"", ), ( + "artifact_manifest_name = \"artifact-manifest.v1.json\"", + "artifact_manifest_name = \"untrusted.json\"", + ), + ( "checksums_name = \"SHA256SUMS\"", "checksums_name = \"checksums.txt\"", ), @@ -930,12 +944,56 @@ tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] assert!(RadrootsRuntimeDistributionResolver::parse_str(&missing_member).is_err()); let uppercase_hash = HARDENED_SERVICE_CONTRACT.replacen("4b3ba578", "4B3BA578", 1); assert!(RadrootsRuntimeDistributionResolver::parse_str(&uppercase_hash).is_err()); + let short_hash = HARDENED_SERVICE_CONTRACT.replacen( + "4b3ba5789fac6aa219e84e1e5c002cf8230b72f95fd6d95a6419d2fdf2915f83", + "00", + 1, + ); + assert!(RadrootsRuntimeDistributionResolver::parse_str(&short_hash).is_err()); let unknown = HARDENED_SERVICE_CONTRACT.replacen( "[service_artifacts.myc]", "[service_artifacts.myc]\nunknown = true", 1, ); assert!(RadrootsRuntimeDistributionResolver::parse_str(&unknown).is_err()); + + let mut unsupported_service: Value = + toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value"); + unsupported_service["service_artifacts"]["myc"]["service_id"] = + Value::String("unsupported".to_owned()); + assert!( + RadrootsRuntimeDistributionResolver::parse_str( + &toml::to_string(&unsupported_service).expect("unsupported-service contract") + ) + .is_err() + ); + + let mut missing_artifact: Value = + toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value"); + let artifacts = missing_artifact["service_artifacts"] + .as_table_mut() + .expect("service artifact table"); + let rhi = artifacts.remove("rhi").expect("RHI artifact"); + artifacts.insert("other".to_owned(), rhi); + assert!( + RadrootsRuntimeDistributionResolver::parse_str( + &toml::to_string(&missing_artifact).expect("missing-artifact contract") + ) + .is_err() + ); + + let mut empty_artifacts: Value = + toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value"); + empty_artifacts["service_artifacts"] + .as_table_mut() + .expect("service artifact table") + .clear(); + assert!( + RadrootsRuntimeDistributionResolver::parse_str( + &toml::to_string(&empty_artifacts).expect("empty-artifact contract") + ) + .is_err() + ); } #[test] @@ -956,10 +1014,11 @@ tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] let mut missing_service: Value = toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value"); - missing_service["service_targets"] + let targets = missing_service["service_targets"] .as_table_mut() - .expect("service target table") - .remove("rhi"); + .expect("service target table"); + let rhi = targets.remove("rhi").expect("RHI target"); + targets.insert("other".to_owned(), rhi); assert!( RadrootsRuntimeDistributionResolver::parse_str( &toml::to_string(&missing_service).expect("missing-service contract") @@ -967,6 +1026,19 @@ tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] .is_err() ); + let mut empty_targets: Value = + toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value"); + empty_targets["service_targets"] + .as_table_mut() + .expect("service target table") + .clear(); + assert!( + RadrootsRuntimeDistributionResolver::parse_str( + &toml::to_string(&empty_targets).expect("empty-target contract") + ) + .is_err() + ); + let mut mismatched_service: Value = toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value"); let targets = mismatched_service["service_targets"] @@ -982,6 +1054,23 @@ tier_1_targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] } #[test] + fn hardened_service_contract_requires_stable_in_both_channel_inventories() { + for channel_inventory in ["active", "defined"] { + let mut contract: Value = + toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value"); + contract["channels"][channel_inventory] = + Value::Array(vec![Value::String("candidate".to_owned())]); + assert_eq!( + RadrootsRuntimeDistributionResolver::parse_str( + &toml::to_string(&contract).expect("channel-drift contract") + ) + .expect_err("stable channel is mandatory"), + RadrootsRuntimeDistributionError::InvalidServiceArtifactContract + ); + } + } + + #[test] fn standalone_hardened_service_target_rejects_contract_drift() { let contract: Value = toml::from_str(HARDENED_SERVICE_CONTRACT).expect("contract fixture value"); diff --git a/crates/runtime_manager/src/managed.rs b/crates/runtime_manager/src/managed.rs @@ -159,10 +159,6 @@ pub fn resolve_runtime_target( .get(management_mode) .cloned() .ok_or(RadrootsRuntimeManagerError::InvalidContract)?; - if service_target.service_id() != runtime_context.service() { - return Err(RadrootsRuntimeManagerError::ContextMismatch); - } - Ok(ManagedRuntimeTarget { context: runtime_context, service_target, @@ -198,9 +194,16 @@ mod tests { InstanceId, RadrootsHostEnvironment, RadrootsPathProfile, RadrootsPathResolver, RadrootsPlatform, RuntimeContext, RuntimeContextBootstrap, RuntimeContextSource, ServiceId, }; + #[cfg(any(target_os = "linux", target_os = "macos"))] + use radroots_service_host::AdminTransportLimits; - use super::{ManagedRuntimeContext, resolve_runtime_target}; - use crate::{HARDENED_MANAGEMENT_CONTRACT, RadrootsRuntimeManagerError, parse_contract_str}; + use super::{ + ManagedRuntimeContext, active_management_mode_for_profile, resolve_runtime_target, + }; + use crate::{ + HARDENED_MANAGEMENT_CONTRACT, ManagedCliCommand, RadrootsRuntimeManagerError, + parse_contract_str, + }; fn management_context() -> ManagedRuntimeContext { ManagedRuntimeContext::new( @@ -268,6 +271,19 @@ mod tests { assert_eq!(myc.service_target().service_id(), myc.service_id()); assert_eq!(myc.management_mode(), "interactive_user_managed"); assert!(!myc.mode_contract().service_manager_integration); + let invocation = myc + .cli_invocation(ManagedCliCommand::Doctor) + .expect("Myc doctor invocation"); + assert_eq!(invocation.profile(), RadrootsPathProfile::RepoLocal); + assert_eq!(invocation.command(), ManagedCliCommand::Doctor); + + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + let status_client = myc + .status_client(AdminTransportLimits::DEFAULT) + .expect("Myc status client"); + assert!(!format!("{status_client:?}").contains("sensitive")); + } assert_eq!(rhi.service_id().as_str(), "rhi"); assert_eq!(rhi.instance_id().as_str(), "secondary"); @@ -305,6 +321,21 @@ mod tests { } #[test] + fn inactive_management_mode_never_matches_a_supported_profile() { + let mut contract = parse_contract_str(HARDENED_MANAGEMENT_CONTRACT).expect("contract"); + contract + .mode + .get_mut("interactive_user_managed") + .expect("interactive mode") + .contract_state = "inactive".to_owned(); + + assert_eq!( + active_management_mode_for_profile(&contract, RadrootsPathProfile::RepoLocal), + Err(RadrootsRuntimeManagerError::UnsupportedProfile) + ); + } + + #[test] fn production_manager_has_no_direct_io_process_or_artifact_authority() { let source = include_str!("managed.rs") .split("\n#[cfg(test)]") diff --git a/crates/transport_nostr/src/client.rs b/crates/transport_nostr/src/client.rs @@ -315,7 +315,7 @@ impl NostrTransport { pub(crate) fn next_subscription_sequence(&self) -> Result<u64, radroots_transport::Error> { self.subscription_sequence - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| { + .try_update(Ordering::SeqCst, Ordering::SeqCst, |current| { current.checked_add(1) }) .map(|previous| previous + 1) diff --git a/crates/transport_nostr/src/subscription.rs b/crates/transport_nostr/src/subscription.rs @@ -893,6 +893,15 @@ mod tests { let selector = FetchSelector::all() .with_kinds(vec![1]) .expect("kind") + .with_authors(vec![ + *radroots_event_codec::decode::signed_event(&signed_event( + "subscription-author", + 1_800_000_000, + )) + .expect("signed author event") + .pubkey(), + ]) + .expect("author") .with_exact_tag_value('d', "trade-1") .expect("tag") .with_since_unix_seconds(1_700_000_000) @@ -940,7 +949,7 @@ mod tests { signed_event("equal-c", 1_800_000_000), ]; events.sort_by_key(|event| event_id(event)); - let base_request = request(&[relay], 2); + let base_request = request(&[relay], 3); let target = base_request.target_set().targets()[0].fingerprint().clone(); let middle_cursor = RelayCursor::new(1_800_000_000, event_id(&events[1])).expect("cursor"); let checkpoint = SubscriptionCheckpoint::new( @@ -956,9 +965,15 @@ mod tests { .with_checkpoints([checkpoint]) .expect("checkpointed request"); let relay_url = RelayUrl::parse(relay, RelayUrlPolicy::Public).expect("relay"); + let older = signed_event("older", 1_799_999_999); + let later = signed_event("later", 1_800_000_001); let client = Arc::new(MockSubscriptionClient::new([ ScriptedItem::Item(RelaySubscriptionItem::Event { relay: relay_url.clone(), + raw: older, + }), + ScriptedItem::Item(RelaySubscriptionItem::Event { + relay: relay_url.clone(), raw: events[0].clone(), }), ScriptedItem::Item(RelaySubscriptionItem::Event { @@ -966,9 +981,13 @@ mod tests { raw: events[1].clone(), }), ScriptedItem::Item(RelaySubscriptionItem::Event { - relay: relay_url, + relay: relay_url.clone(), raw: events[2].clone(), }), + ScriptedItem::Item(RelaySubscriptionItem::Event { + relay: relay_url, + raw: later.clone(), + }), ])); let transport = NostrTransport::with_subscription_client(configured(&[relay]), client.clone()); @@ -989,6 +1008,10 @@ mod tests { event.checkpoint().cursor().as_str().split(':').nth(2), Some(event_id(&events[2]).as_str()) ); + let SubscriptionNext::Event(event) = subscription.next().await.expect("next event") else { + panic!("event expected"); + }; + assert_eq!(event.observed().event().id_str(), event_id(&later)); assert_eq!( client.query().targets[0].since_unix_seconds, Some(1_800_000_000) @@ -1010,6 +1033,10 @@ mod tests { raw: events[1].clone(), }), ScriptedItem::Item(RelaySubscriptionItem::Event { + relay: relay_url.clone(), + raw: events[1].clone(), + }), + ScriptedItem::Item(RelaySubscriptionItem::Event { relay: relay_url, raw: events[0].clone(), }), @@ -1062,6 +1089,17 @@ mod tests { &RelayCursor::new(10, "a".repeat(64)).expect("cursor"), ) .expect("scoped cursor"); + for malformed_cursor in [ + FetchCursor::parse("nostr-live-v1:10").expect("opaque missing-field cursor"), + FetchCursor::parse(format!("{}:extra", scoped.as_str())) + .expect("opaque extra-field cursor"), + ] { + let checkpoint = SubscriptionCheckpoint::new(target.clone(), malformed_cursor); + assert_eq!( + parse_cursor(&base, &checkpoint), + Err(radroots_transport::Error::InvalidFetchCursor) + ); + } let mismatched = base .with_selector(other_selector) .with_checkpoints([SubscriptionCheckpoint::new(target, scoped)]) @@ -1147,6 +1185,26 @@ mod tests { } #[tokio::test] + async fn an_admitted_subscription_that_expires_before_next_is_deadline_bounded() { + let relay = "wss://one.example"; + let client = Arc::new(MockSubscriptionClient::new([])); + let transport = NostrTransport::with_subscription_client(configured(&[relay]), client); + let request = SubscriptionRequest::new( + "expires-after-admission", + target_set(&[relay]), + SubscriptionBounds::new(1, unix_time_ms() + 50).expect("bounds"), + ) + .expect("request"); + let mut subscription = transport.subscribe(request).await.expect("subscription"); + tokio::time::sleep(Duration::from_millis(75)).await; + + let SubscriptionNext::End(terminal) = subscription.next().await.expect("deadline") else { + panic!("terminal expected"); + }; + assert_eq!(terminal.reason(), SubscriptionEndReason::Deadline); + } + + #[tokio::test] async fn expired_unrepresentable_closed_and_failed_sources_are_bounded() { let relay = "wss://one.example"; let client = Arc::new(MockSubscriptionClient::new([])); @@ -1230,6 +1288,23 @@ mod tests { assert_eq!(terminal.reason(), SubscriptionEndReason::SourceClosed); assert_eq!(client.cancellations.load(AtomicOrdering::SeqCst), 0); + let unexpected_close = Arc::new(MockSubscriptionClient::new([ScriptedItem::Item( + RelaySubscriptionItem::Closed { + relay: RelayUrl::parse("wss://unexpected.example", RelayUrlPolicy::Public) + .expect("unexpected relay"), + }, + )])); + let unexpected_transport = + NostrTransport::with_subscription_client(configured(&[relays[0]]), unexpected_close); + let mut unexpected = unexpected_transport + .subscribe(request(&[relays[0]], 1)) + .await + .expect("subscription"); + assert_eq!( + unexpected.next().await, + Err(radroots_transport::Error::SubscriptionUnavailable) + ); + let error_client = Arc::new(MockSubscriptionClient::new([ScriptedItem::Error])); let error_transport = NostrTransport::with_subscription_client(configured(&[relays[0]]), error_client);