commit 68cff3f0569132f4734339e0246e86a5b2d7175f parent 635a58f0302f9e396be5ef1c132dd12f55c1c0be Author: triesap <tyson@radroots.org> Date: Thu, 9 Jul 2026 17:08:06 +0000 transport: align SDK profile and status contracts - replace the transport-profile target policy with default/local/mesh policy variants - expose canonical transport status fields and Reticulum preview scope metadata - update SDK tests and source-boundary guards for the retired status DTO surface Diffstat:
18 files changed, 360 insertions(+), 192 deletions(-)
diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs @@ -231,11 +231,11 @@ pub use crate::trade_storage::{ }; #[cfg(feature = "runtime")] pub use crate::transport::{ - HybridProfile, NostrProfile, NostrRelayUrlPolicy, ProxyAuth, ProxyProfile, PublishMode, - ReticulumPreviewBehavior, ReticulumPreviewProfile, SDK_TRANSPORT_TARGET_MAX_COUNT, - SatisfactionPolicy, TargetPolicy, TargetSet, TransportDeliveryReceipt, - TransportDeliveryTargetStatus, TransportKind, TransportOutcome, TransportProfile, - TransportReceipt, TransportTargetReceipt, + HybridProfile, MeshScopeId, NostrProfile, NostrRelayUrlPolicy, ProxyAuth, ProxyProfile, + PublishMode, ReticulumPreviewAgentEndpoint, ReticulumPreviewBehavior, ReticulumPreviewProfile, + SDK_TRANSPORT_TARGET_MAX_COUNT, SatisfactionPolicy, TargetPolicy, TargetSet, + TransportDeliveryReceipt, TransportDeliveryTargetStatus, TransportKind, TransportOutcome, + TransportProfile, TransportReceipt, TransportTargetReceipt, }; #[cfg(feature = "runtime")] pub use radroots_trade::dvm::RadrootsTradeInventoryBinWitnessDto; diff --git a/crates/sdk/src/sync_runtime.rs b/crates/sdk/src/sync_runtime.rs @@ -36,8 +36,7 @@ use radroots_transport::RadrootsTransportSatisfactionPolicy; #[cfg(feature = "runtime")] use radroots_transport::{ RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, RadrootsTransportImplementationState, - RadrootsTransportKind, RadrootsTransportReadinessState, RadrootsTransportStatus, - RadrootsTransportTarget, + RadrootsTransportKind, RadrootsTransportStatus, RadrootsTransportTarget, }; #[cfg(all(feature = "runtime", feature = "transport-nostr-runtime"))] use radroots_transport_nostr::RadrootsNostrClientPublishAdapter; @@ -209,56 +208,40 @@ impl SyncTransportTargetSummary { #[cfg(feature = "runtime")] #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub struct SyncTransportStatusSummary { - pub transport_kind: String, + pub transport: String, pub profile_id: Option<String>, pub endpoint_uri: Option<String>, - pub implementation_state: String, - pub readiness: String, - pub publish_usable: bool, - pub fetch_usable: bool, - pub redacted_message: Option<String>, + pub configured: bool, + pub implementation: String, + pub usable_for_delivery: bool, + pub message: String, } #[cfg(feature = "runtime")] impl SyncTransportStatusSummary { fn from_transport_status(status: RadrootsTransportStatus) -> Self { Self { - transport_kind: status.kind.canonical_label(), + transport: status.kind.canonical_label(), profile_id: status.profile_id, endpoint_uri: status.endpoint_uri, - implementation_state: transport_implementation_state_label(status.implementation_state) - .to_owned(), - readiness: transport_readiness_state_label(status.readiness).to_owned(), - publish_usable: status.publish_usable, - fetch_usable: status.fetch_usable, - redacted_message: status.redacted_message, + configured: status.configured, + implementation: transport_implementation_label(status.implementation).to_owned(), + usable_for_delivery: status.usable_for_delivery, + message: status.message, } } } #[cfg(feature = "runtime")] -fn transport_implementation_state_label( - state: RadrootsTransportImplementationState, -) -> &'static str { +fn transport_implementation_label(state: RadrootsTransportImplementationState) -> &'static str { match state { - RadrootsTransportImplementationState::Available => "available", - RadrootsTransportImplementationState::Disabled => "disabled", - RadrootsTransportImplementationState::Misconfigured => "misconfigured", + RadrootsTransportImplementationState::Real => "real", + RadrootsTransportImplementationState::Mock => "mock", RadrootsTransportImplementationState::PreviewUnavailable => "preview_unavailable", } } #[cfg(feature = "runtime")] -fn transport_readiness_state_label(state: RadrootsTransportReadinessState) -> &'static str { - match state { - RadrootsTransportReadinessState::Ready => "ready", - RadrootsTransportReadinessState::Disabled => "disabled", - RadrootsTransportReadinessState::Misconfigured => "misconfigured", - RadrootsTransportReadinessState::PreviewUnavailable => "preview_unavailable", - } -} - -#[cfg(feature = "runtime")] #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)] #[non_exhaustive] pub struct SyncProjectionRefreshRequest { diff --git a/crates/sdk/src/transport.rs b/crates/sdk/src/transport.rs @@ -2,9 +2,8 @@ use crate::RadrootsSdkError; use radroots_transport::{ RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, RadrootsTransportDeliveryReceipt, RadrootsTransportImplementationState, RadrootsTransportKind, - RadrootsTransportReadinessState, RadrootsTransportSatisfactionClass, RadrootsTransportStatus, - RadrootsTransportTarget, RadrootsTransportTargetFingerprint, RadrootsTransportTargetReceipt, - RadrootsTransportTargetSet, + RadrootsTransportMeshScopeId, RadrootsTransportSatisfactionClass, RadrootsTransportStatus, + RadrootsTransportTarget, RadrootsTransportTargetReceipt, RadrootsTransportTargetSet, }; use radroots_transport_nostr::{RadrootsRelayUrl, RadrootsRelayUrlPolicy}; use serde::ser::{SerializeStruct, Serializer}; @@ -70,7 +69,9 @@ impl NostrRelayUrlPolicy { #[non_exhaustive] pub enum TargetPolicy { Explicit(TargetSet), - UseTransportProfile, + DefaultProfile, + LocalOnly, + MeshScope(MeshScopeId), } impl TargetPolicy { @@ -89,8 +90,16 @@ impl TargetPolicy { Ok(Self::Explicit(TargetSet::nostr_relays(relays, url_policy)?)) } - pub fn use_transport_profile() -> Self { - Self::UseTransportProfile + pub fn default_profile() -> Self { + Self::DefaultProfile + } + + pub fn local_only() -> Self { + Self::LocalOnly + } + + pub fn mesh_scope(scope: MeshScopeId) -> Self { + Self::MeshScope(scope) } #[cfg(any(feature = "signer-adapters", test))] @@ -112,15 +121,48 @@ impl serde::Serialize for TargetPolicy { state.serialize_field("canonical_targets", targets.canonical_targets())?; state.end() } - Self::UseTransportProfile => { + Self::DefaultProfile => { + let mut state = serializer.serialize_struct("TargetPolicy", 1)?; + state.serialize_field("kind", "default_profile")?; + state.end() + } + Self::LocalOnly => { let mut state = serializer.serialize_struct("TargetPolicy", 1)?; - state.serialize_field("kind", "use_transport_profile")?; + state.serialize_field("kind", "local_only")?; + state.end() + } + Self::MeshScope(scope) => { + let mut state = serializer.serialize_struct("TargetPolicy", 2)?; + state.serialize_field("kind", "mesh_scope")?; + state.serialize_field("scope", scope)?; state.end() } } } } +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] +#[serde(transparent)] +pub struct MeshScopeId(RadrootsTransportMeshScopeId); + +impl MeshScopeId { + pub fn parse(raw: impl AsRef<str>) -> Result<Self, RadrootsSdkError> { + Ok(Self(RadrootsTransportMeshScopeId::parse(raw)?)) + } + + pub fn local_preview() -> Self { + Self(RadrootsTransportMeshScopeId::local_preview()) + } + + pub fn as_str(&self) -> &str { + self.0.as_str() + } + + pub(crate) fn transport_scope(&self) -> RadrootsTransportMeshScopeId { + self.0.clone() + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct TargetSet { targets: Vec<RadrootsTransportTarget>, @@ -231,10 +273,7 @@ impl TargetSet { RadrootsTransportTargetSet::new(targets.clone())?; let canonical_targets = targets .iter() - .map(|target| { - RadrootsTransportTargetFingerprint::from_target(&target.kind, &target.uri) - .to_string() - }) + .map(|target| target.fingerprint.to_string()) .collect::<BTreeSet<_>>() .into_iter() .collect(); @@ -285,6 +324,8 @@ impl NostrProfile { #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub struct ReticulumPreviewProfile { endpoint_uri: String, + scope: MeshScopeId, + agent_endpoint: Option<ReticulumPreviewAgentEndpoint>, behavior: ReticulumPreviewBehavior, } @@ -292,6 +333,8 @@ impl ReticulumPreviewProfile { pub fn preview_unavailable() -> Self { Self { endpoint_uri: RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), + scope: MeshScopeId::local_preview(), + agent_endpoint: None, behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts, } } @@ -305,18 +348,59 @@ impl ReticulumPreviewProfile { self.endpoint_uri.as_str() } + pub fn scope(&self) -> &MeshScopeId { + &self.scope + } + + pub fn agent_endpoint(&self) -> Option<&ReticulumPreviewAgentEndpoint> { + self.agent_endpoint.as_ref() + } + + pub fn with_agent_endpoint(mut self, agent_endpoint: ReticulumPreviewAgentEndpoint) -> Self { + self.agent_endpoint = Some(agent_endpoint); + self + } + pub fn behavior(&self) -> ReticulumPreviewBehavior { self.behavior } pub fn target_set(&self) -> Result<TargetSet, RadrootsSdkError> { - TargetSet::transport_targets(vec![RadrootsTransportTarget::new( + TargetSet::transport_targets(vec![RadrootsTransportTarget::new_with_metadata( RadrootsTransportKind::Reticulum, self.endpoint_uri.as_str(), + Some(self.scope.transport_scope()), + None, )?]) } } +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] +#[serde(transparent)] +pub struct ReticulumPreviewAgentEndpoint(String); + +impl ReticulumPreviewAgentEndpoint { + pub fn parse(raw: impl AsRef<str>) -> Result<Self, RadrootsSdkError> { + let uri = raw.as_ref(); + if uri.is_empty() + || uri != uri.trim() + || uri + .chars() + .any(|ch| ch.is_ascii_control() || ch.is_ascii_whitespace()) + || uri.find(':').is_none() + { + return Err(RadrootsSdkError::InvalidRequest { + message: "Reticulum preview agent endpoint is invalid".to_owned(), + }); + } + Ok(Self(uri.to_owned())) + } + + pub fn as_str(&self) -> &str { + self.0.as_str() + } +} + impl Default for ReticulumPreviewProfile { fn default() -> Self { Self::preview_unavailable() @@ -513,8 +597,10 @@ impl TransportProfile { Self::LocalOnly => vec![ RadrootsTransportStatus::new( RadrootsTransportKind::Local, - RadrootsTransportImplementationState::Available, - RadrootsTransportReadinessState::Ready, + true, + RadrootsTransportImplementationState::Real, + false, + "local persistence only", ) .with_profile_id(self.transport_profile_id()), ], @@ -537,20 +623,17 @@ impl TransportProfile { vec![ RadrootsTransportStatus::new( RadrootsTransportKind::Proxy, + auth_configured, + RadrootsTransportImplementationState::Real, + auth_configured, if auth_configured { - RadrootsTransportImplementationState::Available - } else { - RadrootsTransportImplementationState::Misconfigured - }, - if auth_configured { - RadrootsTransportReadinessState::Ready + "ready" } else { - RadrootsTransportReadinessState::Misconfigured + "proxy transport requires bearer token" }, ) .with_profile_id(self.transport_profile_id()) - .with_endpoint_uri(profile.endpoint_url()) - .with_publish_usable(auth_configured), + .with_endpoint_uri(profile.endpoint_url()), ] } } @@ -568,12 +651,12 @@ impl TransportProfile { fn nostr_transport_status(profile_id: &str) -> RadrootsTransportStatus { RadrootsTransportStatus::new( RadrootsTransportKind::Nostr, - RadrootsTransportImplementationState::Available, - RadrootsTransportReadinessState::Ready, + true, + RadrootsTransportImplementationState::Real, + true, + "ready", ) .with_profile_id(profile_id) - .with_publish_usable(true) - .with_fetch_usable(true) } fn reticulum_preview_transport_status( @@ -582,12 +665,13 @@ fn reticulum_preview_transport_status( ) -> RadrootsTransportStatus { RadrootsTransportStatus::new( RadrootsTransportKind::Reticulum, + true, RadrootsTransportImplementationState::PreviewUnavailable, - RadrootsTransportReadinessState::PreviewUnavailable, + false, + RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, ) .with_profile_id(profile_id) .with_endpoint_uri(endpoint_uri) - .with_redacted_message(RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE) } impl From<RadrootsTransportDeliveryReceipt> for TransportReceipt { diff --git a/crates/sdk/src/workflow_runtime.rs b/crates/sdk/src/workflow_runtime.rs @@ -18,7 +18,8 @@ use radroots_outbox::{ RadrootsOutboxReticulumPreviewBehavior, RadrootsOutboxSignedOperationInput, }; use radroots_transport::{ - RadrootsTransportKind, RadrootsTransportSatisfactionPolicy, RadrootsTransportTarget, + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RadrootsTransportKind, + RadrootsTransportSatisfactionPolicy, RadrootsTransportTarget, }; const SDK_LOCAL_EVENT_ENDPOINT_URI: &str = "local:sdk"; @@ -174,7 +175,7 @@ fn resolved_delivery_plan( satisfaction_policy, RadrootsOutboxReticulumPreviewBehavior::RejectDeliveryAttempts, ), - TargetPolicy::UseTransportProfile => { + TargetPolicy::DefaultProfile => { let transport_profile = sdk.transport_profile(); let targets = transport_profile .target_set()? @@ -192,6 +193,35 @@ fn resolved_delivery_plan( outbox_reticulum_preview_behavior(transport_profile), ) } + TargetPolicy::LocalOnly => { + if satisfaction_policy != SatisfactionPolicy::NoWait { + return Err(RadrootsSdkError::InvalidRequest { + message: "local-only target policy requires no_wait satisfaction policy" + .to_owned(), + }); + } + delivery_plan_from_targets( + "local_only", + Vec::new(), + satisfaction_policy, + RadrootsOutboxReticulumPreviewBehavior::RejectDeliveryAttempts, + ) + } + TargetPolicy::MeshScope(scope) => { + let target_set = + TargetSet::transport_targets(vec![RadrootsTransportTarget::new_with_metadata( + RadrootsTransportKind::Reticulum, + RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, + Some(scope.transport_scope()), + None, + )?])?; + delivery_plan_from_targets( + "mesh_scope", + target_set.into_targets(), + satisfaction_policy, + RadrootsOutboxReticulumPreviewBehavior::RejectDeliveryAttempts, + ) + } } } diff --git a/crates/sdk/tests/dvm_runtime.rs b/crates/sdk/tests/dvm_runtime.rs @@ -234,7 +234,7 @@ async fn dvm_configured_enqueue_reports_prepare_and_target_errors_without_mutati deterministic_event_id("request-event"), deterministic_event_id("decision-event"), inventory_bins(), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ); let error = sdk .dvm() @@ -414,7 +414,7 @@ async fn dvm_trade_transition_proof_request_reports_prepare_and_target_errors_wi deterministic_event_id("request-event"), deterministic_event_id("decision-event"), inventory_bins(), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ); let error = sdk .dvm() diff --git a/crates/sdk/tests/farms_runtime.rs b/crates/sdk/tests/farms_runtime.rs @@ -628,7 +628,7 @@ async fn farm_enqueue_publish_stores_event_and_queues_signed_outbox_without_prof let request = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_B_D_TAG, "North Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("farm-idem-b") .expect("idempotency key"); @@ -694,7 +694,7 @@ async fn farm_enqueue_publish_returns_sanitized_signer_errors_before_mutation() let request = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_C_D_TAG, "North Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ); let error = sdk .farms() @@ -740,7 +740,7 @@ async fn farm_enqueue_publish_derives_order_independent_idempotency_key() { let first = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_D_D_TAG, "North Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY_B, RELAY], NostrRelayUrlPolicy::Public) .expect("first transport targets"); @@ -794,7 +794,7 @@ async fn farm_enqueue_publish_pushes_queued_event_with_mock_relay_sync() { let enqueue_request = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_D_D_TAG, "Sync Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY], NostrRelayUrlPolicy::Public) .expect("transport targets"); @@ -854,7 +854,7 @@ async fn farm_hybrid_profile_publishes_after_nostr_success_and_retains_reticulum FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_E_D_TAG, "Hybrid Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ), &FixtureSigner::new(FARMER), ) @@ -923,7 +923,7 @@ async fn farm_enqueue_publish_reports_preflight_idempotency_conflict_without_mut let first = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_E_D_TAG, "North Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("farm-idem-e") .expect("idempotency key"); @@ -958,7 +958,7 @@ async fn farm_enqueue_publish_reports_preflight_idempotency_conflict_without_mut let second = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_F_D_TAG, "Changed Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("farm-idem-e") .expect("idempotency key"); @@ -1032,7 +1032,7 @@ async fn farm_runtime_dtos_serialize_deterministically() { let enqueue_request = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_B_D_TAG, "Queued Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY, RELAY_B], NostrRelayUrlPolicy::Public) .expect("relay targets") @@ -1066,13 +1066,17 @@ async fn farm_runtime_dtos_serialize_deterministically() { "kind": "explicit", "targets": [ { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY, + "scope": null, + "label": null, "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af" }, { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY_B, + "scope": null, + "label": null, "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05" } ], @@ -1094,7 +1098,7 @@ async fn farm_runtime_dtos_serialize_deterministically() { let try_key_request = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_C_D_TAG, "Queued Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("farm-serialized-try-key") .expect("try idempotency key"); diff --git a/crates/sdk/tests/listings_runtime.rs b/crates/sdk/tests/listings_runtime.rs @@ -261,7 +261,7 @@ async fn enqueue_publish_stores_event_and_queues_signed_outbox_without_publish() let request = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_B_D_TAG, "Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("idem-b") .expect("idempotency key"); @@ -313,12 +313,12 @@ async fn enqueue_publish_stores_event_and_queues_signed_outbox_without_publish() } #[tokio::test] -async fn enqueue_publish_use_transport_profile_rejects_empty_transport_targets() { +async fn enqueue_publish_default_profile_rejects_empty_transport_targets() { let (_tempdir, sdk) = directory_sdk_with_relays(&[]).await; let request = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ); let error = sdk @@ -350,7 +350,7 @@ async fn prepare_then_enqueue_prepared_uses_same_event_id() { .enqueue_prepared_publish_with_explicit_signer( &actor, prepared.clone(), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), None, &FixtureSigner::new(SELLER), ) @@ -389,7 +389,7 @@ async fn enqueue_receipt_debug_omits_signed_event_payload_material() { let request = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("debug-secret-idempotency") .expect("idempotency key"); @@ -448,7 +448,7 @@ async fn listing_runtime_dtos_serialize_deterministically() { let enqueue_request = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_B_D_TAG, "Queued Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY, RELAY_B], NostrRelayUrlPolicy::Public) .expect("relay targets") @@ -462,13 +462,17 @@ async fn listing_runtime_dtos_serialize_deterministically() { enqueue_json["target_policy"]["targets"], serde_json::json!([ { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY, + "scope": null, + "label": null, "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af" }, { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY_B, + "scope": null, + "label": null, "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05" } ]) @@ -489,7 +493,7 @@ async fn listing_runtime_dtos_serialize_deterministically() { let try_key_request = ListingEnqueuePublishRequest::from_document( actor(), RadrootsListingDraftDocumentV1::new(listing(LISTING_C_D_TAG, "Queued Coffee")), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("listing-serialized-try-key") .expect("try idempotency key"); @@ -526,7 +530,7 @@ async fn enqueue_publish_convenience_matches_prepare_plus_enqueue_prepared() { .enqueue_prepared_publish_with_explicit_signer( &prepared_actor, prepared_plan, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), None, &FixtureSigner::new(SELLER), ) @@ -537,7 +541,7 @@ async fn enqueue_publish_convenience_matches_prepare_plus_enqueue_prepared() { let convenience_request = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_H_D_TAG, "Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ); let convenience_receipt = convenience_sdk .listings() @@ -563,7 +567,7 @@ async fn enqueue_prepared_publish_returns_structured_actor_errors() { .enqueue_prepared_publish_with_explicit_signer( &non_seller_actor(), prepared, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), None, &FixtureSigner::new(SELLER), ) @@ -589,7 +593,7 @@ async fn enqueue_prepared_publish_returns_sanitized_signer_errors() { .enqueue_prepared_publish_with_explicit_signer( &actor, prepared, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), None, &FixtureSigner::new(OTHER), ) @@ -613,7 +617,7 @@ async fn explicit_historical_created_at_does_not_backdate_observed_at_ms() { let request = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_K_D_TAG, "Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .with_created_at(created_at); @@ -659,7 +663,7 @@ async fn enqueue_publish_returns_sanitized_signer_errors() { let request = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_C_D_TAG, "Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ); let error = sdk .listings() @@ -682,7 +686,7 @@ async fn enqueue_publish_reports_preflight_idempotency_conflict_without_mutation let first = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_D_D_TAG, "Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("idem-d") .expect("idempotency key"); @@ -717,7 +721,7 @@ async fn enqueue_publish_reports_preflight_idempotency_conflict_without_mutation let second = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_E_D_TAG, "Changed"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("idem-d") .expect("idempotency key"); @@ -761,7 +765,7 @@ async fn enqueue_publish_derives_order_independent_idempotency_key() { let first = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_F_D_TAG, "Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY_B, RELAY], NostrRelayUrlPolicy::Public) .expect("first transport targets"); @@ -818,7 +822,7 @@ async fn listing_hybrid_profile_publishes_after_nostr_success_and_retains_reticu ListingEnqueuePublishRequest::new( actor(), listing(LISTING_G_D_TAG, "Hybrid Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ), &FixtureSigner::new(SELLER), ) diff --git a/crates/sdk/tests/market_runtime.rs b/crates/sdk/tests/market_runtime.rs @@ -150,7 +150,7 @@ async fn market_search_refreshes_local_projection_and_reads_fts() { let publish = ListingEnqueuePublishRequest::new( seller_actor(), listing("Blueberries"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY], NostrRelayUrlPolicy::Public) .expect("target relays"); diff --git a/crates/sdk/tests/orders_runtime.rs b/crates/sdk/tests/orders_runtime.rs @@ -849,7 +849,7 @@ async fn order_submit_enqueue_stores_event_queues_outbox_and_status_sees_request buyer_actor(), listing_event_ptr(), order, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -3332,7 +3332,7 @@ async fn order_submit_enqueue_returns_sanitized_signer_errors_before_mutation() buyer_actor(), listing_event_ptr(), order_request("order-submit-wrong-signer"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -3382,7 +3382,7 @@ async fn order_submit_enqueue_derives_order_independent_idempotency_key() { buyer_actor(), listing_event_ptr(), order_request("order-submit-idempotent"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -3469,7 +3469,7 @@ async fn order_submit_enqueue_pushes_queued_event_with_mock_relay_sync() { buyer_actor(), listing_event_ptr(), order_request("order-submit-sync"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -3533,7 +3533,7 @@ async fn order_submit_enqueue_reports_partial_local_mutation_after_outbox_confli buyer_actor(), listing_event_ptr(), order_request("order-submit-conflict-a"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -3550,7 +3550,7 @@ async fn order_submit_enqueue_reports_partial_local_mutation_after_outbox_confli buyer_actor(), listing_event_ptr(), order_request("order-submit-conflict-b"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -3642,7 +3642,7 @@ async fn order_submit_runtime_dtos_serialize_deterministically() { buyer_actor(), listing_event_ptr(), order_request("order-submit-serialized-enqueue"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -3661,13 +3661,17 @@ async fn order_submit_runtime_dtos_serialize_deterministically() { "kind": "explicit", "targets": [ { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY, + "scope": null, + "label": null, "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af" }, { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY_B, + "scope": null, + "label": null, "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05" } ], @@ -3692,7 +3696,7 @@ async fn order_submit_runtime_dtos_serialize_deterministically() { buyer_actor(), listing_event_ptr(), order_request("order-submit-try-idempotency"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -4663,7 +4667,7 @@ async fn order_decision_runtime_dtos_serialize_deterministically() { seller_actor(), request_event_ptr(&request_event), order_decision("order-decision-serialized-enqueue"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -4682,13 +4686,17 @@ async fn order_decision_runtime_dtos_serialize_deterministically() { "kind": "explicit", "targets": [ { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY, + "scope": null, + "label": null, "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af" }, { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY_B, + "scope": null, + "label": null, "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05" } ], @@ -4713,7 +4721,7 @@ async fn order_decision_runtime_dtos_serialize_deterministically() { seller_actor(), request_event_ptr(&request_event), order_decision("order-decision-try-idempotency"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -4866,7 +4874,7 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() { root_event.clone(), previous_event.clone(), proposal.clone(), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -4883,13 +4891,17 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() { "kind": "explicit", "targets": [ { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY, + "scope": null, + "label": null, "fingerprint": "a1997ec4596596af6ffc65e6a30ab7cffa53ea71f524c1c86d64018b96d130af" }, { - "kind": "Nostr", + "kind": "nostr", "uri": RELAY_B, + "scope": null, + "label": null, "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05" } ], @@ -4910,7 +4922,7 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() { root_event.clone(), previous_event.clone(), proposal.clone(), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -4953,7 +4965,7 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() { root_event.clone(), previous_event.clone(), revision_decision, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -4982,7 +4994,7 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() { &previous_event_id, RadrootsOrderRevisionOutcome::Accepted, ), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5014,7 +5026,7 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() { root_event.clone(), previous_event.clone(), cancellation, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5043,7 +5055,7 @@ async fn order_revision_and_cancellation_dtos_serialize_deterministically() { root_event.clone(), previous_event.clone(), order_cancellation("order-revision-dto"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5085,7 +5097,7 @@ async fn order_decision_enqueue_accept_stores_event_queues_outbox_and_updates_st seller_actor(), request_event_ptr(&request_event), order_decision("order-decision-accept"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5192,7 +5204,7 @@ async fn order_decision_enqueue_decline_stores_event_and_status_sees_declined() seller_actor(), request_event_ptr(&request_event), decision, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5236,7 +5248,7 @@ async fn order_decision_enqueue_rejects_missing_request_evidence_before_mutation seller_actor(), missing_request, order_decision("order-decision-missing-request"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5284,7 +5296,7 @@ async fn order_decision_enqueue_returns_sanitized_signer_errors_before_decision_ seller_actor(), request_event_ptr(&request_event), order_decision("order-decision-wrong-signer"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5339,7 +5351,7 @@ async fn order_decision_enqueue_rejects_existing_decision_state_before_mutation( seller_actor(), request_event_ptr(&request_event), decline, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5549,7 +5561,7 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif request_event_ptr(&request_event), request_event_ptr(&request_event), proposal, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5592,7 +5604,7 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif seller_actor(), request_event_ptr(&request_event), order_decision("order-lifecycle-pending-revision"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5620,7 +5632,7 @@ async fn order_revision_proposal_status_exposes_pending_and_blocks_follow_on_lif request_event_ptr(&request_event), order_event_ptr(&proposal_receipt.signed_event_id), blocked_proposal, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5667,7 +5679,7 @@ async fn order_declined_revision_finalizes_declined_negotiation() { request_event_ptr(&request_event), request_event_ptr(&request_event), proposal.clone(), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5692,7 +5704,7 @@ async fn order_declined_revision_finalizes_declined_negotiation() { request_event_ptr(&request_event), order_event_ptr(&proposal_receipt.signed_event_id), declined_revision, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5742,7 +5754,7 @@ async fn order_declined_revision_finalizes_declined_negotiation() { request_event_ptr(&request_event), order_event_ptr(&proposal_receipt.signed_event_id), second_decision, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5826,7 +5838,7 @@ async fn order_cancel_lifecycle_enqueue_updates_status() { request_event_ptr(&request_event), request_event_ptr(&request_event), order_cancellation("order-lifecycle-cancel"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5882,7 +5894,7 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() { &request_event_id, &request_event_id, ), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5913,7 +5925,7 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() { seller_actor(), request_event_ptr(&request_event), order_decision("order-lifecycle-invalid"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5940,7 +5952,7 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() { request_event_ptr(&request_event), order_event_ptr(&decision_receipt.signed_event_id), revision_without_proposal, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) @@ -5963,7 +5975,7 @@ async fn order_lifecycle_enqueue_rejects_invalid_state_before_mutation() { request_event_ptr(&request_event), order_event_ptr(&decision_receipt.signed_event_id), order_cancellation("order-lifecycle-invalid"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), PublishMode::EnqueueOnly, SatisfactionPolicy::NoWait, ) diff --git a/crates/sdk/tests/runtime_foundation.rs b/crates/sdk/tests/runtime_foundation.rs @@ -649,13 +649,17 @@ fn relay_target_set_validates_normalizes_preserves_order_and_caps() { "kind": "explicit", "targets": [ { - "kind": "Nostr", + "kind": "nostr", "uri": "wss://relay-b.example.com", + "scope": null, + "label": null, "fingerprint": "5136077cfe7eddcbfaddc5d7bf1f42cdbb8191f3691b86ccc3a81047851cef05" }, { - "kind": "Nostr", + "kind": "nostr", "uri": "wss://relay-a.example.com", + "scope": null, + "label": null, "fingerprint": "fc957b234632cc52e2be19cba88bc85c69966ee5a2df61742b5875ff717fd6fa" } ], diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs @@ -1506,6 +1506,8 @@ fn sdk_transport_policy_sources_reject_configured_profile_and_proxy_relay_bridge "UseConfiguredProfile", "use_configured_profile", "configured_profile()", + "UseTransportProfile", + "use_transport_profile", "impl RadrootsRelayPublishAdapter for RadrootsdProxyPublishAdapter", "proxy_relay_receipt_from_response", ] { @@ -1523,6 +1525,11 @@ fn sdk_transport_policy_sources_reject_configured_profile_and_proxy_relay_bridge fn sdk_sync_status_sources_reject_retired_relay_shaped_generic_fields() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let sync_runtime = read_source(manifest_dir.join("src/sync_runtime.rs").as_path()); + let status_summary = source_between( + sync_runtime.as_str(), + "pub struct SyncTransportStatusSummary", + "\n}\n\n#[cfg(feature = \"runtime\")]\nimpl SyncTransportStatusSummary", + ); for required in [ "pub configured_transport_target_count: usize,", @@ -1530,6 +1537,11 @@ fn sdk_sync_status_sources_reject_retired_relay_shaped_generic_fields() { "pub transport_statuses: Vec<SyncTransportStatusSummary>,", ".configured_transport_targets()?", ".transport_statuses()", + "pub transport: String,", + "pub configured: bool,", + "pub implementation: String,", + "pub usable_for_delivery: bool,", + "pub message: String,", ] { assert!( sync_runtime.contains(required), @@ -1537,6 +1549,19 @@ fn sdk_sync_status_sources_reject_retired_relay_shaped_generic_fields() { ); } + for required in [ + "pub transport: String,", + "pub configured: bool,", + "pub implementation: String,", + "pub usable_for_delivery: bool,", + "pub message: String,", + ] { + assert!( + status_summary.contains(required), + "SDK sync status summary must retain canonical status field witness `{required}`" + ); + } + for forbidden in [ concat!("configured_nostr", "_relay", "_count"), concat!("configured_nostr", "_relays"), @@ -1551,6 +1576,20 @@ fn sdk_sync_status_sources_reject_retired_relay_shaped_generic_fields() { "SDK sync status source must not expose retired relay-shaped generic field `{forbidden}`" ); } + + for forbidden in [ + concat!("pub transport", "_kind: String,"), + concat!("pub implementation", "_state: String,"), + "pub readiness: String,", + concat!("pub publish", "_usable: bool,"), + concat!("pub fetch", "_usable: bool,"), + concat!("pub redacted", "_message: Option<String>,"), + ] { + assert!( + !status_summary.contains(forbidden), + "SDK sync status summary must not expose retired status field `{forbidden}`" + ); + } } #[test] diff --git a/crates/sdk/tests/sync_runtime.rs b/crates/sdk/tests/sync_runtime.rs @@ -611,7 +611,7 @@ async fn enqueue_listing_with_policy( ListingEnqueuePublishRequest::new( actor(), listing(d_tag, title), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets(relays, url_policy) .expect("relay targets"), @@ -660,12 +660,12 @@ async fn sync_status_empty_store_reports_canonical_sources_and_transport_targets ); assert_eq!(receipt.transport_profile.transport_statuses.len(), 1); assert_eq!( - receipt.transport_profile.transport_statuses[0].transport_kind, + receipt.transport_profile.transport_statuses[0].transport, "nostr" ); assert_eq!( - receipt.transport_profile.transport_statuses[0].readiness, - "ready" + receipt.transport_profile.transport_statuses[0].implementation, + "real" ); assert_eq!( serde_json::to_value(&receipt).expect("status json"), @@ -708,14 +708,13 @@ async fn sync_status_empty_store_reports_canonical_sources_and_transport_targets } ], "transport_statuses": [{ - "transport_kind": "nostr", + "transport": "nostr", "profile_id": "nostr", "endpoint_uri": null, - "implementation_state": "available", - "readiness": "ready", - "publish_usable": true, - "fetch_usable": true, - "redacted_message": null + "configured": true, + "implementation": "real", + "usable_for_delivery": true, + "message": "ready" }] } }) @@ -757,16 +756,16 @@ async fn sync_status_reports_hybrid_transport_targets_and_statuses() { .iter() .map(|status| { ( - status.transport_kind.as_str(), - status.readiness.as_str(), - status.publish_usable, - status.fetch_usable, + status.transport.as_str(), + status.implementation.as_str(), + status.configured, + status.usable_for_delivery, ) }) .collect::<Vec<_>>(), vec![ - ("nostr", "ready", true, true), - ("reticulum", "preview_unavailable", false, false) + ("nostr", "real", true, true), + ("reticulum", "preview_unavailable", true, false) ] ); } @@ -1655,7 +1654,7 @@ async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Proxy Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ), &FixtureSigner::new(SELLER), ) @@ -1731,7 +1730,7 @@ async fn product_push_outbox_radrootsd_proxy_recovers_expired_publishing_claim_b ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Recovered Proxy Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ), &FixtureSigner::new(SELLER), ) @@ -1832,7 +1831,7 @@ async fn product_push_outbox_radrootsd_proxy_idempotency_is_attempt_scoped() { ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Retry Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ), &FixtureSigner::new(SELLER), ) @@ -1937,7 +1936,7 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Proxy Error Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ), &FixtureSigner::new(SELLER), ) @@ -2001,7 +2000,7 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out ListingEnqueuePublishRequest::new( actor(), listing(LISTING_B_D_TAG, "Terminal Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ), &FixtureSigner::new(SELLER), ) @@ -2175,7 +2174,7 @@ async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempt ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Reticulum Preview Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ), &FixtureSigner::new(SELLER), ) @@ -2428,7 +2427,7 @@ async fn push_outbox_with_adapter_scopes_duplicate_endpoint_sibling_plans() { ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Duplicate Plan Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY_A], NostrRelayUrlPolicy::Public) .expect("first targets") @@ -2714,7 +2713,7 @@ fn enqueue_publish_rejects_nonlocal_ws_relay_targets() { let error = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_C_D_TAG, "Nonlocal Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([NONLOCAL_WS_RELAY], NostrRelayUrlPolicy::Localhost) .expect_err("nonlocal ws relay target"); @@ -2724,7 +2723,7 @@ fn enqueue_publish_rejects_nonlocal_ws_relay_targets() { let error = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_C_D_TAG, "Private LAN Coffee"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([PRIVATE_LAN_WS_RELAY], NostrRelayUrlPolicy::Localhost) .expect_err("private LAN ws relay target"); diff --git a/crates/sdk/tests/unit/dvm_runtime_tests.rs b/crates/sdk/tests/unit/dvm_runtime_tests.rs @@ -230,7 +230,7 @@ fn enqueue_request_builders_and_ingest_request_builders_are_deterministic() { let idempotency_len = idempotency.as_str().len(); let request = DvmTradeTransitionProofEnqueueRequest::from_prepare( prepare.clone(), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY], NostrRelayUrlPolicy::Public) .expect("relays") @@ -254,7 +254,7 @@ fn enqueue_request_builders_and_ingest_request_builders_are_deterministic() { event_id('2'), event_id('3'), inventory_bins(), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("dvm-proof-request-2") .expect("idempotency") @@ -267,7 +267,7 @@ fn enqueue_request_builders_and_ingest_request_builders_are_deterministic() { assert!(matches!( DvmTradeTransitionProofEnqueueRequest::from_prepare( prepare, - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets(["ws://relay.example.com"], NostrRelayUrlPolicy::Public), Err(RadrootsSdkError::InvalidRelayUrl { .. }) @@ -275,7 +275,7 @@ fn enqueue_request_builders_and_ingest_request_builders_are_deterministic() { assert!(matches!( DvmTradeTransitionProofEnqueueRequest::from_prepare( proof_request(service_actor()), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key(""), Err(RadrootsSdkError::InvalidRequest { .. }) diff --git a/crates/sdk/tests/unit/farms_runtime_tests.rs b/crates/sdk/tests/unit/farms_runtime_tests.rs @@ -167,7 +167,7 @@ fn farm_runtime_request_builders_and_serializers_cover_success_paths() { let enqueue = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_B_D_TAG, "Queued Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY_A, RELAY_B], NostrRelayUrlPolicy::Public) .expect("relay targets") @@ -182,7 +182,7 @@ fn farm_runtime_request_builders_and_serializers_cover_success_paths() { let try_key = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_C_D_TAG, "Try Key Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("farm-unit-try-key") .expect("try key"); @@ -357,7 +357,7 @@ fn farm_request_builders_reject_invalid_options_and_timestamp_bounds() { let invalid_relays = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_A_D_TAG, "Invalid Relay Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets(["http://relay.radroots.test"], NostrRelayUrlPolicy::Public); assert!(invalid_relays.is_err()); @@ -365,7 +365,7 @@ fn farm_request_builders_reject_invalid_options_and_timestamp_bounds() { let invalid_key = FarmEnqueuePublishRequest::new( farmer_actor(), farm(FARM_B_D_TAG, "Invalid Key Farm"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key(""); assert!(invalid_key.is_err()); diff --git a/crates/sdk/tests/unit/listings_runtime_tests.rs b/crates/sdk/tests/unit/listings_runtime_tests.rs @@ -104,7 +104,7 @@ fn listing_runtime_request_builders_and_serializers_cover_success_paths() { let enqueue = ListingEnqueuePublishRequest::from_document( actor(), RadrootsListingDraftDocumentV1::new(listing(LISTING_B_D_TAG, "Queued Greens")), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_nostr_targets([RELAY_A, RELAY_B], NostrRelayUrlPolicy::Public) .expect("relay targets") @@ -119,7 +119,7 @@ fn listing_runtime_request_builders_and_serializers_cover_success_paths() { let try_key = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_C_D_TAG, "Try Key Greens"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key("listing-unit-try-key") .expect("try key"); @@ -134,7 +134,7 @@ fn listing_request_builders_reject_invalid_options_and_timestamp_bounds() { let invalid_key = ListingEnqueuePublishRequest::new( actor(), listing(LISTING_A_D_TAG, "Invalid Key Greens"), - TargetPolicy::use_transport_profile(), + TargetPolicy::default_profile(), ) .try_with_idempotency_key(""); assert!(invalid_key.is_err()); diff --git a/crates/sdk/tests/unit/orders_runtime_tests.rs b/crates/sdk/tests/unit/orders_runtime_tests.rs @@ -2075,7 +2075,7 @@ fn order_enqueue_request_mutators_reject_invalid_relays_and_idempotency_keys() { RadrootsOrderRevisionOutcome::Accepted, ); let cancellation_payload = cancellation_payload(); - let policy = TargetPolicy::use_transport_profile(); + let policy = TargetPolicy::default_profile(); assert_error_display( TradeSubmitEnqueueRequest::new( @@ -4029,7 +4029,7 @@ fn order_runtime_request_builders_and_serializers_cover_source_attached_paths() reason: "not workable".to_owned(), }, ); - let policy = TargetPolicy::use_transport_profile(); + let policy = TargetPolicy::default_profile(); let submit_prepare = TradeSubmitPrepareRequest::new(buyer_actor(), root_event.clone(), order_request_payload()) diff --git a/crates/sdk/tests/unit/transport_tests.rs b/crates/sdk/tests/unit/transport_tests.rs @@ -79,10 +79,10 @@ fn relay_authority_host(authority: &str) -> Option<String> { #[test] fn transport_profile_policy_serializes_as_kind_only() { - let transport_profile_policy = TargetPolicy::use_transport_profile(); + let transport_profile_policy = TargetPolicy::default_profile(); assert_eq!( serde_json::to_value(&transport_profile_policy).expect("json"), - serde_json::json!({ "kind": "use_transport_profile" }) + serde_json::json!({ "kind": "default_profile" }) ); assert_struct_serialize_error_paths(&transport_profile_policy, 1); } @@ -148,8 +148,10 @@ fn target_set_accessors_and_configured_relays_cover_empty_paths() { serde_json::json!({ "kind": "explicit", "targets": [{ - "kind": "Nostr", + "kind": "nostr", "uri": "wss://relay-c.example.com", + "scope": null, + "label": null, "fingerprint": "ec4b5005dd1fcf0d949045e3d5524f9a6a95209ecc888f582ae2e9bf69e5b8e6" }], "canonical_targets": ["ec4b5005dd1fcf0d949045e3d5524f9a6a95209ecc888f582ae2e9bf69e5b8e6"] @@ -227,6 +229,8 @@ fn reticulum_preview_profile_uses_canonical_endpoint_and_behavior_names() { serde_json::to_value(profile).expect("profile json"), serde_json::json!({ "endpoint_uri": "reticulum:preview-unavailable", + "scope": "local_preview", + "agent_endpoint": null, "behavior": "reject_delivery_attempts" }) ); @@ -252,11 +256,16 @@ fn explicit_target_sets_reject_noncanonical_reticulum_preview_endpoints() { let uri = RadrootsTransportTargetUri::parse("reticulum:preview-unavailable-alt").expect("target uri"); - let fingerprint = - RadrootsTransportTargetFingerprint::from_target(&RadrootsTransportKind::Reticulum, &uri); + let fingerprint = RadrootsTransportTargetFingerprint::from_target( + &RadrootsTransportKind::Reticulum, + &uri, + None, + ); let err = TargetSet::transport_targets(vec![RadrootsTransportTarget { kind: RadrootsTransportKind::Reticulum, uri, + scope: None, + label: None, fingerprint, }]) .expect_err("noncanonical Reticulum endpoint"); diff --git a/crates/sdk/tests/unit/workflow_runtime_tests.rs b/crates/sdk/tests/unit/workflow_runtime_tests.rs @@ -258,7 +258,7 @@ async fn enqueue_signed_workflow_maps_no_wait_directly_and_allows_local_only_pro operation_kind: "workflow.test.v1", actor: &actor, frozen_draft: &draft, - target_policy: TargetPolicy::use_transport_profile(), + target_policy: TargetPolicy::default_profile(), satisfaction_policy: SatisfactionPolicy::NoWait, idempotency_key: None, }, @@ -333,7 +333,7 @@ async fn enqueue_signed_workflow_stores_signed_event_and_reports_idempotency_con operation_kind: "workflow.test.v1", actor: &actor, frozen_draft: &first_draft, - target_policy: TargetPolicy::use_transport_profile(), + target_policy: TargetPolicy::default_profile(), satisfaction_policy: SatisfactionPolicy::AllTargets, idempotency_key: Some(idempotency_key.clone()), }, @@ -374,7 +374,7 @@ async fn enqueue_signed_workflow_stores_signed_event_and_reports_idempotency_con operation_kind: "workflow.test.v1", actor: &actor, frozen_draft: &second_draft, - target_policy: TargetPolicy::use_transport_profile(), + target_policy: TargetPolicy::default_profile(), satisfaction_policy: SatisfactionPolicy::AllTargets, idempotency_key: Some(idempotency_key), }, @@ -437,7 +437,7 @@ async fn enqueue_configured_signed_workflow_uses_sdk_signer_provider() { operation_kind: "workflow.test.v1", actor: &actor, frozen_draft: &draft, - target_policy: TargetPolicy::use_transport_profile(), + target_policy: TargetPolicy::default_profile(), satisfaction_policy: SatisfactionPolicy::AllTargets, idempotency_key: None, }, @@ -472,7 +472,7 @@ async fn enqueue_signed_workflow_reports_outbox_preflight_failure_without_mutati operation_kind: "workflow.test.v1", actor: &actor, frozen_draft: &draft, - target_policy: TargetPolicy::use_transport_profile(), + target_policy: TargetPolicy::default_profile(), satisfaction_policy: SatisfactionPolicy::AllTargets, idempotency_key: None, }; @@ -508,7 +508,7 @@ async fn enqueue_signed_workflow_reports_store_failures() { operation_kind: "workflow.test.v1", actor: &actor, frozen_draft: &draft, - target_policy: TargetPolicy::use_transport_profile(), + target_policy: TargetPolicy::default_profile(), satisfaction_policy: SatisfactionPolicy::AllTargets, idempotency_key: None, }; @@ -538,7 +538,7 @@ async fn enqueue_signed_workflow_reports_clock_failures() { operation_kind: "workflow.test.v1", actor: &actor, frozen_draft: &draft, - target_policy: TargetPolicy::use_transport_profile(), + target_policy: TargetPolicy::default_profile(), satisfaction_policy: SatisfactionPolicy::AllTargets, idempotency_key: None, }; @@ -558,7 +558,7 @@ async fn enqueue_signed_workflow_rejects_transport_profile_targets_without_proxy operation_kind: "workflow.test.v1", actor: &actor, frozen_draft: &draft, - target_policy: TargetPolicy::UseTransportProfile, + target_policy: TargetPolicy::DefaultProfile, satisfaction_policy: SatisfactionPolicy::AllTargets, idempotency_key: None, };