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:
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]