lib

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

commit 5d6bd6e014cac9b59b7c57cda9f5689ff220b872
parent 9efa42f9f42afae1f30965d724433f3abdebf9fe
Author: triesap <tyson@radroots.org>
Date:   Wed,  8 Jul 2026 07:47:41 +0000

transport: harden final source guards

- add generic transport status source-boundary guard

- keep Nostr-specific relay detail outside generic guard scope

- assert LocalImport plus PublishAck observations in Nostr outbox tests

- validate transport stack and release gates

Diffstat:
Mcrates/transport/tests/source_boundary.rs | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/transport_nostr/tests/transport.rs | 38+++++++++++++++++++++++++++++++++-----
2 files changed, 128 insertions(+), 5 deletions(-)

diff --git a/crates/transport/tests/source_boundary.rs b/crates/transport/tests/source_boundary.rs @@ -16,6 +16,15 @@ const TRANSPORT_HARDENING_CRATE_SOURCE_ROOTS: &[&str] = &[ "outbox/src", ]; +const GENERIC_TRANSPORT_STATUS_SOURCE_ROOTS: &[&str] = &[ + "event_store/src", + "mesh_agent_proto/src", + "outbox/src", + "transport/src", + "transport_publish_protocol/src", + "transport_reticulum/src", +]; + const FORBIDDEN_TRANSPORT_CONCEPTS: &[ForbiddenConcept] = &[ ForbiddenConcept { pattern: "\"radrootsd_proxy\"", @@ -57,6 +66,61 @@ const FORBIDDEN_TRANSPORT_CONCEPTS: &[ForbiddenConcept] = &[ pattern: "PublishRelaySource", reason: "old relay-shaped publish source names must not return", }, + ForbiddenConcept { + pattern: concat!("Nostr", "Fetch"), + reason: "generic transport observations must use transport-neutral fetch naming", + }, + ForbiddenConcept { + pattern: concat!("Nostr", "Subscription"), + reason: "generic transport observations must use transport-neutral subscription naming", + }, + ForbiddenConcept { + pattern: concat!("Nostr", "PublishAck"), + reason: "generic transport observations must use transport-neutral publish ack naming", + }, + ForbiddenConcept { + pattern: concat!("nostr", "_fetch"), + reason: "generic transport observation storage strings must be transport-neutral", + }, + ForbiddenConcept { + pattern: concat!("nostr", "_subscription"), + reason: "generic transport observation storage strings must be transport-neutral", + }, + ForbiddenConcept { + pattern: concat!("nostr", "_publish_ack"), + reason: "generic transport observation storage strings must be transport-neutral", + }, +]; + +const FORBIDDEN_GENERIC_TRANSPORT_STATUS_CONCEPTS: &[ForbiddenConcept] = &[ + ForbiddenConcept { + pattern: concat!("configured_nostr", "_relay", "_count"), + reason: "generic status surfaces must expose configured transport target counts", + }, + ForbiddenConcept { + pattern: concat!("configured_nostr", "_relays"), + reason: "generic status surfaces must expose configured transport targets", + }, + ForbiddenConcept { + pattern: concat!("target", "_relays"), + reason: "generic transport target surfaces must use endpoint terminology", + }, + ForbiddenConcept { + pattern: concat!("connected", "_relays"), + reason: "generic transport attempt surfaces must use endpoint terminology", + }, + ForbiddenConcept { + pattern: concat!("acknowledged", "_relays"), + reason: "generic transport acknowledgement surfaces must use endpoint terminology", + }, + ForbiddenConcept { + pattern: concat!("failed", "_relays"), + reason: "generic transport failure surfaces must use target terminology", + }, + ForbiddenConcept { + pattern: concat!("relay", "_count"), + reason: "generic transport status counts must use transport target terminology", + }, ]; #[test] @@ -97,6 +161,37 @@ fn transport_hardening_sources_reject_removed_protocol_identifiers() { } #[test] +fn generic_transport_status_sources_reject_retired_relay_shaped_names() { + let crates_root = Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("transport crate parent"); + let mut findings = Vec::new(); + + for relative_root in GENERIC_TRANSPORT_STATUS_SOURCE_ROOTS { + for path in rust_source_files(crates_root.join(relative_root).as_path()) { + let source_raw = read_source(path.as_path()); + let source = production_source(source_raw.as_str()); + let relative_path = relative_path(crates_root, path.as_path()); + + for concept in FORBIDDEN_GENERIC_TRANSPORT_STATUS_CONCEPTS { + if contains_forbidden_concept(source, concept.pattern) { + findings.push(format!( + "{} contains retired generic transport status concept `{}`: {}", + relative_path, concept.pattern, concept.reason + )); + } + } + } + } + + assert!( + findings.is_empty(), + "generic transport status source-boundary violations:\n{}", + findings.join("\n") + ); +} + +#[test] fn transport_publish_capabilities_keep_readiness_and_usability_fields() { let source_raw = read_source( Path::new(env!("CARGO_MANIFEST_DIR")) diff --git a/crates/transport_nostr/tests/transport.rs b/crates/transport_nostr/tests/transport.rs @@ -1,7 +1,8 @@ use futures::future::BoxFuture; use nostr::JsonUtil; use radroots_event_store::{ - RadrootsEventStore, RadrootsEventVerificationStatus, RadrootsTransportObservationType, + RadrootsEventStore, RadrootsEventVerificationStatus, RadrootsTransportObservationRow, + RadrootsTransportObservationType, }; use radroots_events::draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent}; use radroots_events::kinds::KIND_POST; @@ -109,6 +110,30 @@ fn signed_post(content: &str) -> RadrootsSignedNostrEvent { signed_event_with_kind_and_hashtag(content, KIND_POST, "soil") } +fn assert_outbox_publish_observations( + observations: &[RadrootsTransportObservationRow], + publish_ack_count: usize, +) { + assert_eq!(observations.len(), publish_ack_count + 1); + assert_eq!( + observations + .iter() + .filter(|observation| observation.observation_type + == RadrootsTransportObservationType::LocalImport + && observation.endpoint_uri.as_str() == "local:outbox") + .count(), + 1 + ); + assert_eq!( + observations + .iter() + .filter(|observation| observation.observation_type + == RadrootsTransportObservationType::PublishAck) + .count(), + publish_ack_count + ); +} + fn signed_event_with_kind_and_hashtag( content: &str, kind: u32, @@ -1410,7 +1435,7 @@ async fn outbox_publish_persists_partial_success_and_skips_accepted_retry() { .observations_for_event(signed.id.as_str()) .await .expect("observations"); - assert_eq!(observations.len(), 3); + assert_outbox_publish_observations(&observations, 3); } #[tokio::test] @@ -1649,8 +1674,11 @@ async fn outbox_publish_ignores_unknown_adapter_receipts() { .observations_for_event(signed.id.as_str()) .await .expect("observations"); - assert_eq!(observations.len(), 1); - assert_eq!(observations[0].endpoint_uri.as_str(), RELAY_PRIMARY_WSS); + assert_outbox_publish_observations(&observations, 1); + assert!(observations.iter().any(|observation| { + observation.observation_type == RadrootsTransportObservationType::PublishAck + && observation.endpoint_uri.as_str() == RELAY_PRIMARY_WSS + })); } #[tokio::test] @@ -1833,7 +1861,7 @@ async fn outbox_publish_marks_published_when_delivery_plan_satisfaction_is_met_w .observations_for_event(signed.id.as_str()) .await .expect("observations"); - assert_eq!(observations.len(), 2); + assert_outbox_publish_observations(&observations, 2); } #[tokio::test]