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:
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);