lib

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

commit 121ab276234198c523e1d01467e8f7cd7cedc9c7
parent 111f143c5007830710b8f895ae62ab778c22c79e
Author: triesap <tyson@radroots.org>
Date:   Wed, 15 Jul 2026 14:57:00 +0000

transport: separate radrootsd execution from transport identity

- replace proxy and hybrid profile surfaces with radrootsd execution and multi-target APIs
- align Reticulum profile, status, and outbox behavior with canonical local Reticulum targets
- update SDK tests and README for transport maturity and availability metadata
- validate source-boundary, feature checks, and runtime publish flows

Diffstat:
Mcrates/sdk/Cargo.toml | 6+++---
Mcrates/sdk/README | 13++++++-------
Mcrates/sdk/src/adapters/mod.rs | 2+-
Mcrates/sdk/src/adapters/radrootsd.rs | 36++++++++++++++++++------------------
Mcrates/sdk/src/error.rs | 27+++++++++++----------------
Mcrates/sdk/src/lib.rs | 16++++++++--------
Mcrates/sdk/src/runtime.rs | 24++++++++++++++++--------
Mcrates/sdk/src/sync_runtime.rs | 441++++++++++++++++++++++++++++++++++++-------------------------------------------
Mcrates/sdk/src/transport.rs | 198+++++++++++++++++++++++++++++--------------------------------------------------
Mcrates/sdk/src/workflow_runtime.rs | 74+++++++++++++++++++++++++++++++++-----------------------------------------
Mcrates/sdk/tests/farms_runtime.rs | 28++++++++++++++--------------
Mcrates/sdk/tests/listings_runtime.rs | 26+++++++++++++-------------
Mcrates/sdk/tests/runtime_foundation.rs | 21++++++++++-----------
Mcrates/sdk/tests/source_boundary.rs | 190++++++++++++++++++++++++++++++++++++-------------------------------------------
Mcrates/sdk/tests/sync_runtime.rs | 225++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mcrates/sdk/tests/trade_product_publish_runtime.rs | 11++++++-----
Mcrates/sdk/tests/unit/adapters_radrootsd_tests.rs | 152+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mcrates/sdk/tests/unit/error_tests.rs | 14+++++++-------
Mcrates/sdk/tests/unit/orders_runtime_tests.rs | 1+
Mcrates/sdk/tests/unit/runtime_tests.rs | 14++++----------
Mcrates/sdk/tests/unit/sync_runtime_tests.rs | 467+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mcrates/sdk/tests/unit/transport_tests.rs | 80++++++++++++++++++++++++++++---------------------------------------------------
Mcrates/sdk/tests/unit/workflow_runtime_tests.rs | 2+-
23 files changed, 956 insertions(+), 1112 deletions(-)

diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml @@ -48,7 +48,7 @@ identity-models = [ identity-storage = ["identity-models", "std", "radroots_identity/std"] signing = ["dep:nostr", "dep:radroots_nostr", "nostr"] transport-nostr-client = ["signing", "std", "serde_json", "radroots_nostr/client"] -radrootsd-proxy = [ +radrootsd-execution = [ "std", "serde_json", "dep:futures", @@ -115,13 +115,13 @@ local-runtime = [ "transport-nostr-runtime", "transport-nostr-client", ] -local-runtime-radrootsd-proxy = [ +local-runtime-radrootsd-execution = [ "std", "serde", "serde_json", "runtime", "signer-adapters", - "radrootsd-proxy", + "radrootsd-execution", ] test-fixture-geonames-asset = [ "runtime", diff --git a/crates/sdk/README b/crates/sdk/README @@ -8,7 +8,7 @@ The SDK v1 product runtime is centered on `RadrootsClient::builder()`, `RadrootsClient::builder()` defaults to memory storage, the system clock, the `LocalOnly` transport profile, and no production network publishing. Directory storage is opt-in and creates `runtime.sqlite`, `private.sqlite`, and `studio.sqlite` in the selected directory. Configured Nostr -relay URLs live inside `TransportProfile::Nostr` or the Nostr side of `TransportProfile::Hybrid`, +relay URLs live inside `TransportProfile::Nostr` or the Nostr side of `TransportProfile::MultiTarget`, and product enqueue requests choose the active profile through `TargetPolicy::default_profile()`. When `signer-adapters` is enabled, `RadrootsClient::builder()` accepts a configured @@ -35,12 +35,11 @@ clock at enqueue time. Listing enqueue receipts report mutation state with the p `sdk.sync().push_outbox(...)` is the product sync entrypoint. It publishes queued signed outbox work when `transport-nostr-runtime` is enabled. Push time uses the delivery targets already stored on each queued outbox event, so already queued work does not require a configured builder profile. Reticulum -preview work reports explicit preview-unavailable or deferred receipt state with zero network +work reports explicit deferred-until-implemented receipt state with zero network attempts; it does not deliver and does not fall back to another transport. Nostr transport publishing -and the `radrootsd-proxy` Publish Proxy transport consume signed outbox events; neither transport -owns signing. `push_outbox_with_transport(...)` remains available for tests and controlled -transport-level substrate checks. `radrootsd-proxy` adds daemon-resolved publishing through -`publish.event`. +and radrootsd execution consume signed outbox events; neither path owns signing. +`push_outbox_with_transport(...)` remains available for tests and controlled transport-level +substrate checks. `radrootsd-execution` adds daemon-resolved publishing through `publish.event`. `sdk.trades()` exposes the local trade runtime surface. With `runtime`, callers can ingest local trade evidence, read `sdk.trades().status(...)`, resync through `sdk.trades().resync()`, inspect @@ -59,7 +58,7 @@ The `local-runtime` feature is the curated feature bundle for local product runt enables `std`, `serde`, `serde_json`, `runtime`, `signer-adapters`, `transport-nostr-runtime`, and `transport-nostr-client`. `signer-adapters` contains the SDK `local_key` and `myc_nip46` signing surface. `transport-nostr-client` supplies the Nostr relay WebSocket publish adapter used when signed outbox work -targets Nostr relay URLs. `local-runtime-radrootsd-proxy` uses daemon-resolved publishing through +targets Nostr relay URLs. `local-runtime-radrootsd-execution` uses daemon-resolved publishing through `publish.event` and the same configured signer provider API. `radroots_sdk_myc_nip46_product_permissions()` and diff --git a/crates/sdk/src/adapters/mod.rs b/crates/sdk/src/adapters/mod.rs @@ -1,6 +1,6 @@ #[cfg(feature = "transport-nostr-client")] pub mod nostr; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] pub mod radrootsd; #[cfg(feature = "signer-adapters")] pub mod signer; diff --git a/crates/sdk/src/adapters/radrootsd.rs b/crates/sdk/src/adapters/radrootsd.rs @@ -10,8 +10,8 @@ use reqwest::header::{AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue}; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_json::{Value, json}; -pub const SDK_RADROOTSD_PROXY_REQUEST_ID: &str = "radroots-sdk-transport-publish-event"; -pub const SDK_RADROOTSD_PROXY_MAX_TARGETS: usize = 20; +pub const SDK_RADROOTSD_PUBLISH_REQUEST_ID: &str = "radroots-sdk-transport-publish-event"; +pub const SDK_RADROOTSD_PUBLISH_MAX_TARGETS: usize = 20; #[derive(Clone, PartialEq, Eq, Default, Serialize, Deserialize)] pub enum RadrootsdAuth { @@ -30,14 +30,14 @@ impl fmt::Debug for RadrootsdAuth { } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsdProxyConfig { +pub struct RadrootsdPublishConfig { pub endpoint: String, pub auth: RadrootsdAuth, pub timeout: Duration, pub request_timeout_ms: Option<u64>, } -impl RadrootsdProxyConfig { +impl RadrootsdPublishConfig { pub fn new(endpoint: impl Into<String>) -> Self { Self { endpoint: endpoint.into(), @@ -64,28 +64,28 @@ impl RadrootsdProxyConfig { } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsdProxyPublishAdapter { - config: RadrootsdProxyConfig, +pub struct RadrootsdPublishAdapter { + config: RadrootsdPublishConfig, } -impl RadrootsdProxyPublishAdapter { - pub fn new(config: RadrootsdProxyConfig) -> Self { +impl RadrootsdPublishAdapter { + pub fn new(config: RadrootsdPublishConfig) -> Self { Self { config } } - pub fn config(&self) -> &RadrootsdProxyConfig { + pub fn config(&self) -> &RadrootsdPublishConfig { &self.config } pub async fn publish_signed_event( &self, - request: RadrootsdProxyPublishRequest, + request: RadrootsdPublishRequest, ) -> Result<TransportPublishEventResponse, RadrootsdError> { let event_identity = - RadrootsdProxyPublishEventIdentity::from_signed_event(&request.signed_event); + RadrootsdPublishEventIdentity::from_signed_event(&request.signed_event); let request = request.into_protocol_request(); request - .validate(SDK_RADROOTSD_PROXY_MAX_TARGETS) + .validate(SDK_RADROOTSD_PUBLISH_MAX_TARGETS) .map_err(RadrootsdError::from_protocol)?; let response = publish_event( self.config.endpoint.as_str(), @@ -100,7 +100,7 @@ impl RadrootsdProxyPublishAdapter { } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsdProxyPublishRequest { +pub struct RadrootsdPublishRequest { pub signed_event: RadrootsSignedEvent, pub target_policy: TransportPublishTargetPolicy, pub delivery_policy: TransportPublishDeliveryPolicy, @@ -108,7 +108,7 @@ pub struct RadrootsdProxyPublishRequest { pub timeout_ms: Option<u64>, } -impl RadrootsdProxyPublishRequest { +impl RadrootsdPublishRequest { fn into_protocol_request(self) -> TransportPublishEventRequest { TransportPublishEventRequest { raw_event_json: self.signed_event.raw_json().to_owned(), @@ -121,13 +121,13 @@ impl RadrootsdProxyPublishRequest { } #[derive(Clone, Debug, PartialEq, Eq)] -struct RadrootsdProxyPublishEventIdentity { +struct RadrootsdPublishEventIdentity { event_id: String, pubkey: String, kind: u32, } -impl RadrootsdProxyPublishEventIdentity { +impl RadrootsdPublishEventIdentity { fn from_signed_event(event: &RadrootsSignedEvent) -> Self { Self { event_id: event.id_str().to_owned(), @@ -193,7 +193,7 @@ pub async fn publish_event( jsonrpc_call( endpoint, auth, - SDK_RADROOTSD_PROXY_REQUEST_ID, + SDK_RADROOTSD_PUBLISH_REQUEST_ID, METHOD_EVENT, request, timeout, @@ -319,7 +319,7 @@ where fn validate_transport_publish_response_for_request( request: &TransportPublishEventRequest, - event_identity: &RadrootsdProxyPublishEventIdentity, + event_identity: &RadrootsdPublishEventIdentity, response: &TransportPublishEventResponse, ) -> Result<(), RadrootsdError> { response.job.validate().map_err(|error| { diff --git a/crates/sdk/src/error.rs b/crates/sdk/src/error.rs @@ -4,7 +4,7 @@ use std::{fmt, path::PathBuf}; #[cfg(feature = "runtime")] use crate::privacy::{PrivacyPreflightStatus, ProductSensitivityField}; #[cfg(feature = "runtime")] -use crate::transport::ReticulumPreviewBehavior; +use crate::transport::ReticulumBehavior; #[cfg(feature = "runtime")] use radroots_trade::identity::RadrootsTradeLocator; #[cfg(feature = "runtime")] @@ -145,10 +145,10 @@ pub enum RadrootsSdkError { operation: &'static str, required_feature: &'static str, }, - ReticulumPreviewTransportUnavailable { + ReticulumTransportUnavailable { operation: String, endpoint_uri: String, - behavior: ReticulumPreviewBehavior, + behavior: ReticulumBehavior, }, ProductSyncTransportSetupFailure { message: String, @@ -218,13 +218,9 @@ impl RadrootsSdkError { Self::TradeAmbiguous { .. } => "trade_ambiguous", Self::PrivacyPreflight { .. } => "privacy_preflight", Self::ProductSyncUnsupported { .. } => "product_sync_unsupported", - Self::ReticulumPreviewTransportUnavailable { behavior, .. } => match behavior { - ReticulumPreviewBehavior::RejectDeliveryAttempts => { - "reticulum_preview_transport_unavailable" - } - ReticulumPreviewBehavior::DeferDeliveryPlans => { - "reticulum_preview_transport_deferred" - } + Self::ReticulumTransportUnavailable { behavior, .. } => match behavior { + ReticulumBehavior::RejectDeliveryAttempts => "reticulum_transport_unavailable", + ReticulumBehavior::DeferDeliveryPlans => "reticulum_transport_deferred", }, Self::ProductSyncTransportSetupFailure { .. } => "product_sync_transport_setup_failure", Self::Authority { .. } => "authority", @@ -288,8 +284,7 @@ impl RadrootsSdkError { | Self::UnsupportedProfileSchema { .. } | Self::ListingEdit { .. } | Self::ListingMutation { .. } => RadrootsSdkErrorClass::Request, - Self::ProductSyncUnsupported { .. } - | Self::ReticulumPreviewTransportUnavailable { .. } => { + Self::ProductSyncUnsupported { .. } | Self::ReticulumTransportUnavailable { .. } => { RadrootsSdkErrorClass::Unsupported } Self::ProductSyncTransportSetupFailure { .. } @@ -364,7 +359,7 @@ impl RadrootsSdkError { Self::ProductSyncUnsupported { .. } => { vec![RadrootsSdkRecoveryAction::EnableRequiredFeature] } - Self::ReticulumPreviewTransportUnavailable { .. } => { + Self::ReticulumTransportUnavailable { .. } => { vec![RadrootsSdkRecoveryAction::ConfigureTransportTargets] } Self::ProductSyncTransportSetupFailure { .. } | Self::Transport { .. } => { @@ -463,7 +458,7 @@ impl RadrootsSdkError { operation, required_feature, } => json!({ "operation": operation, "required_feature": required_feature }), - Self::ReticulumPreviewTransportUnavailable { + Self::ReticulumTransportUnavailable { operation, endpoint_uri, behavior, @@ -640,13 +635,13 @@ impl fmt::Display for RadrootsSdkError { f, "sdk product sync operation {operation} requires feature `{required_feature}`" ), - Self::ReticulumPreviewTransportUnavailable { + Self::ReticulumTransportUnavailable { operation, endpoint_uri, behavior, } => write!( f, - "sdk product sync operation {operation} cannot deliver through Reticulum preview endpoint `{endpoint_uri}` with behavior `{}`", + "sdk product sync operation {operation} cannot deliver through Reticulum endpoint `{endpoint_uri}` with behavior `{}`", behavior.as_str() ), Self::ProductSyncTransportSetupFailure { message } => { diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs @@ -7,7 +7,7 @@ extern crate alloc; #[cfg(feature = "runtime")] mod actor_json; #[cfg(any( - feature = "radrootsd-proxy", + feature = "radrootsd-execution", feature = "signing", feature = "transport-nostr-client", feature = "signer-adapters" @@ -212,7 +212,7 @@ pub use crate::sync_runtime::{ PUSH_OUTBOX_DEFAULT_CLAIM_TTL_MS, PUSH_OUTBOX_DEFAULT_LIMIT, PUSH_OUTBOX_DEFAULT_NEXT_ATTEMPT_DELAY_MS, PUSH_OUTBOX_MAX_LIMIT, PushOutboxEventReceipt, PushOutboxEventState, PushOutboxReceipt, PushOutboxRequest, PushOutboxTargetOutcomeKind, - PushOutboxTargetReceipt, PushOutboxTransportOutcomeKind, ReticulumPreviewTryNowRequest, + PushOutboxTargetReceipt, PushOutboxTransportOutcomeKind, ReticulumTryNowRequest, SYNC_PROJECTION_REFRESH_DEFAULT_LIMIT, SYNC_PROJECTION_REFRESH_MAX_LIMIT, SdkRelayAuthPolicy, SyncEventStoreStatus, SyncOutboxStatus, SyncProjectionRefreshReceipt, SyncProjectionRefreshRequest, SyncStatusReceipt, SyncStatusRequest, SyncStatusSource, @@ -226,12 +226,12 @@ pub use crate::trade_storage::{ }; #[cfg(feature = "runtime")] pub use crate::transport::{ - HybridProfile, MeshScopeId, NostrProfile, NostrRelayUrlPolicy, ProxyAuth, ProxyProfile, - PublishMode, RadrootsTransportDeliveryReceipt, RadrootsTransportDeliveryTargetStatus, - RadrootsTransportKind, RadrootsTransportOutcome, RadrootsTransportSatisfactionClass, - RadrootsTransportTargetReceipt, ReticulumPreviewAgentEndpoint, ReticulumPreviewBehavior, - ReticulumPreviewProfile, SDK_TRANSPORT_TARGET_MAX_COUNT, SatisfactionPolicy, TargetPolicy, - TargetSet, TransportProfile, TransportReceipt, + MeshScopeId, MultiTargetProfile, NostrProfile, NostrRelayUrlPolicy, PublishMode, + RadrootsTransportDeliveryReceipt, RadrootsTransportDeliveryTargetStatus, RadrootsTransportKind, + RadrootsTransportOutcome, RadrootsTransportSatisfactionClass, RadrootsTransportTargetReceipt, + RadrootsdExecutionAuth, RadrootsdExecutionProfile, ReticulumAgentEndpoint, ReticulumBehavior, + ReticulumProfile, SDK_TRANSPORT_TARGET_MAX_COUNT, SatisfactionPolicy, TargetPolicy, TargetSet, + TransportProfile, TransportReceipt, }; #[cfg(feature = "runtime")] pub use radroots_trade::validation_receipt::{ diff --git a/crates/sdk/src/runtime.rs b/crates/sdk/src/runtime.rs @@ -5,7 +5,8 @@ use crate::studio_store::{SDK_STUDIO_STORE_SCHEMA_VERSION, SdkStudioStore}; #[cfg(feature = "runtime")] use crate::{ FarmsClient, GeoNamesClient, ListingsClient, MarketClient, RadrootsGeoNamesConfig, - RadrootsSdkError, SyncClient, TradesClient, transport::TransportProfile, + RadrootsSdkError, SyncClient, TradesClient, + transport::{RadrootsdExecutionProfile, TransportProfile}, }; #[cfg(all(feature = "runtime", feature = "signer-adapters"))] use crate::{ @@ -259,7 +260,6 @@ pub struct SdkOutboxStorageStatus { pub retryable_events: i64, pub terminal_events: i64, pub failed_terminal_events: i64, - pub preview_unavailable_events: i64, pub deferred_until_implemented_events: i64, pub ready_signed_events: i64, pub publishing_events: i64, @@ -506,6 +506,7 @@ pub struct RadrootsClientBuilder { geonames: Option<RadrootsGeoNamesConfig>, clock: RadrootsSdkClock, transport_profile: TransportProfile, + radrootsd_execution_profile: Option<RadrootsdExecutionProfile>, #[cfg(feature = "signer-adapters")] signer_provider: Option<RadrootsSdkSignerProvider>, } @@ -518,6 +519,7 @@ impl Default for RadrootsClientBuilder { geonames: None, clock: RadrootsSdkClock::System, transport_profile: TransportProfile::default(), + radrootsd_execution_profile: None, #[cfg(feature = "signer-adapters")] signer_provider: None, } @@ -561,6 +563,11 @@ impl RadrootsClientBuilder { self } + pub fn radrootsd_execution_profile(mut self, profile: RadrootsdExecutionProfile) -> Self { + self.radrootsd_execution_profile = Some(profile); + self + } + #[cfg(feature = "signer-adapters")] pub fn signer_provider(mut self, signer_provider: RadrootsSdkSignerProvider) -> Self { self.signer_provider = Some(signer_provider); @@ -578,6 +585,7 @@ impl RadrootsClientBuilder { geonames: self.geonames, clock: self.clock, transport_profile: self.transport_profile, + radrootsd_execution_profile: self.radrootsd_execution_profile, #[cfg(feature = "signer-adapters")] signer_provider: self.signer_provider, }) @@ -595,6 +603,7 @@ pub struct RadrootsClient { geonames: Option<RadrootsGeoNamesConfig>, clock: RadrootsSdkClock, transport_profile: TransportProfile, + radrootsd_execution_profile: Option<RadrootsdExecutionProfile>, #[cfg(feature = "signer-adapters")] signer_provider: Option<RadrootsSdkSignerProvider>, } @@ -637,6 +646,10 @@ impl RadrootsClient { &self.transport_profile } + pub fn radrootsd_execution_profile(&self) -> Option<&RadrootsdExecutionProfile> { + self.radrootsd_execution_profile.as_ref() + } + pub fn configured_nostr_relay_urls(&self) -> Vec<String> { self.transport_profile.configured_nostr_relay_urls() } @@ -710,7 +723,6 @@ impl RadrootsClient { retryable_events: outbox_summary.retryable_events, terminal_events: outbox_summary.terminal_events, failed_terminal_events: outbox_summary.failed_terminal_events, - preview_unavailable_events: outbox_summary.preview_unavailable_events, deferred_until_implemented_events: outbox_summary.deferred_until_implemented_events, ready_signed_events: outbox_summary.ready_signed_events, publishing_events: outbox_summary.publishing_events, @@ -1029,7 +1041,6 @@ async fn directory_storage_status_read_only( retryable_events: outbox_summary.retryable_events, terminal_events: outbox_summary.terminal_events, failed_terminal_events: outbox_summary.failed_terminal_events, - preview_unavailable_events: outbox_summary.preview_unavailable_events, deferred_until_implemented_events: outbox_summary.deferred_until_implemented_events, ready_signed_events: outbox_summary.ready_signed_events, publishing_events: outbox_summary.publishing_events, @@ -1123,7 +1134,7 @@ async fn outbox_status_summary_from_pool( now_ms: i64, ) -> Result<radroots_outbox::RadrootsOutboxStatusSummary, RadrootsSdkError> { let row = sqlx::query( - "SELECT COUNT(*) AS total_events, COALESCE(SUM(CASE WHEN state IN ('draft_queued', 'signing', 'signed', 'publishing') THEN 1 ELSE 0 END), 0) AS pending_events, COALESCE(SUM(CASE WHEN state IN ('sign_retryable', 'publish_retryable') THEN 1 ELSE 0 END), 0) AS retryable_events, COALESCE(SUM(CASE WHEN state IN ('published', 'failed_terminal', 'cancelled') THEN 1 ELSE 0 END), 0) AS terminal_events, COALESCE(SUM(CASE WHEN state = 'failed_terminal' THEN 1 ELSE 0 END), 0) AS failed_terminal_events, COALESCE(SUM(CASE WHEN state = 'preview_unavailable' THEN 1 ELSE 0 END), 0) AS preview_unavailable_events, COALESCE(SUM(CASE WHEN state = 'deferred_until_implemented' THEN 1 ELSE 0 END), 0) AS deferred_until_implemented_events, COALESCE(SUM(CASE WHEN state = 'publishing' THEN 1 ELSE 0 END), 0) AS publishing_events FROM outbox_event", + "SELECT COUNT(*) AS total_events, COALESCE(SUM(CASE WHEN state IN ('draft_queued', 'signing', 'signed', 'publishing') THEN 1 ELSE 0 END), 0) AS pending_events, COALESCE(SUM(CASE WHEN state IN ('sign_retryable', 'publish_retryable') THEN 1 ELSE 0 END), 0) AS retryable_events, COALESCE(SUM(CASE WHEN state IN ('published', 'failed_terminal', 'cancelled') THEN 1 ELSE 0 END), 0) AS terminal_events, COALESCE(SUM(CASE WHEN state = 'failed_terminal' THEN 1 ELSE 0 END), 0) AS failed_terminal_events, COALESCE(SUM(CASE WHEN state = 'deferred_until_implemented' THEN 1 ELSE 0 END), 0) AS deferred_until_implemented_events, COALESCE(SUM(CASE WHEN state = 'publishing' THEN 1 ELSE 0 END), 0) AS publishing_events FROM outbox_event", ) .fetch_one(pool) .await @@ -1170,9 +1181,6 @@ async fn outbox_status_summary_from_pool( failed_terminal_events: row .try_get("failed_terminal_events") .map_err(|error| SqliteStoreRole::Outbox.error(error.to_string()))?, - preview_unavailable_events: row - .try_get("preview_unavailable_events") - .map_err(|error| SqliteStoreRole::Outbox.error(error.to_string()))?, deferred_until_implemented_events: row .try_get("deferred_until_implemented_events") .map_err(|error| SqliteStoreRole::Outbox.error(error.to_string()))?, diff --git a/crates/sdk/src/sync_runtime.rs b/crates/sdk/src/sync_runtime.rs @@ -1,29 +1,28 @@ -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] use crate::adapters::radrootsd::{ - RadrootsdAuth, RadrootsdError, RadrootsdProxyConfig, RadrootsdProxyPublishAdapter, - RadrootsdProxyPublishRequest, + RadrootsdAuth, RadrootsdError, RadrootsdPublishAdapter, RadrootsdPublishConfig, + RadrootsdPublishRequest, }; #[cfg(feature = "runtime")] use crate::{ NostrRelayUrlPolicy, RadrootsSdkError, SyncClient, runtime::{RadrootsClient, sdk_now_ms}, - transport::{ReticulumPreviewProfile, TransportProfile}, + transport::{ReticulumProfile, TransportProfile}, }; -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -use crate::{ProxyAuth, ProxyProfile}; +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +use crate::{RadrootsdExecutionAuth, RadrootsdExecutionProfile}; #[cfg(feature = "runtime")] use radroots_event::ids::RadrootsEventId; #[cfg(feature = "runtime")] use radroots_event_store::{RADROOTS_EVENT_STORE_QUERY_LIMIT_MAX, RadrootsEventStoreStatusSummary}; #[cfg(all(feature = "runtime", feature = "transport-nostr-runtime"))] use radroots_nostr::prelude::RadrootsNostrClient; -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] use radroots_outbox::RadrootsOutboxClaimedEvent; #[cfg(feature = "runtime")] use radroots_outbox::{ RadrootsOutboxDeliveryTargetRecord, RadrootsOutboxDeliveryTargetStatus, - RadrootsOutboxEventState, RadrootsOutboxReticulumPreviewEventRecord, - RadrootsOutboxStatusSummary, + RadrootsOutboxEventState, RadrootsOutboxReticulumEventRecord, RadrootsOutboxStatusSummary, }; #[cfg(feature = "runtime")] use radroots_trade::projection::{ @@ -31,15 +30,16 @@ use radroots_trade::projection::{ RadrootsProjectionRefreshReceipt, RadrootsProjectionRefreshRequest, refresh_product_projections, }; -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] use radroots_transport::RadrootsTransportTargetFingerprint; #[cfg(feature = "runtime")] use radroots_transport::{ - RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, RadrootsTransportImplementationState, + RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, RadrootsTransportCapabilityAvailability, + RadrootsTransportCapabilityMaturity, RadrootsTransportImplementationState, RadrootsTransportKind, RadrootsTransportOutcomeKind, RadrootsTransportStatus, RadrootsTransportTarget, }; -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] use radroots_transport::{RadrootsTransportSatisfactionClass, RadrootsTransportSatisfactionPolicy}; #[cfg(all(feature = "runtime", feature = "transport-nostr-runtime"))] use radroots_transport_nostr::{RadrootsNostrClientPublishAdapter, RadrootsNostrTransport}; @@ -48,11 +48,11 @@ use radroots_transport_nostr::{ RadrootsOutboxPublishPolicy, RadrootsOutboxPublishReceipt, RadrootsOutboxPublishTargetReceipt, RadrootsRelayOutcomeKind, publish_claimed_outbox_event_with_transport, }; -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] use radroots_transport_publish_protocol::{ - NostrPublishTargetSourcePolicy, TransportPublishDeliveryPolicy, TransportPublishJobStatus, - TransportPublishJobView, TransportPublishOutcomeKind, TransportPublishTarget, - TransportPublishTargetOutcome, TransportPublishTargetPolicy, + TransportPublishDeliveryPolicy, TransportPublishJobStatus, TransportPublishJobView, + TransportPublishOutcomeKind, TransportPublishTarget, TransportPublishTargetOutcome, + TransportPublishTargetPolicy, }; #[cfg(feature = "runtime")] @@ -86,10 +86,10 @@ impl SyncStatusRequest { #[cfg(feature = "runtime")] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize)] #[non_exhaustive] -pub struct ReticulumPreviewTryNowRequest {} +pub struct ReticulumTryNowRequest {} #[cfg(feature = "runtime")] -impl ReticulumPreviewTryNowRequest { +impl ReticulumTryNowRequest { pub fn new() -> Self { Self::default() } @@ -144,7 +144,6 @@ pub struct SyncOutboxStatus { pub retryable_events: i64, pub terminal_events: i64, pub failed_terminal_events: i64, - pub preview_unavailable_events: i64, pub deferred_until_implemented_events: i64, pub ready_signed_events: i64, pub publishing_events: i64, @@ -161,7 +160,6 @@ impl From<RadrootsOutboxStatusSummary> for SyncOutboxStatus { retryable_events: summary.retryable_events, terminal_events: summary.terminal_events, failed_terminal_events: summary.failed_terminal_events, - preview_unavailable_events: summary.preview_unavailable_events, deferred_until_implemented_events: summary.deferred_until_implemented_events, ready_signed_events: summary.ready_signed_events, publishing_events: summary.publishing_events, @@ -232,6 +230,8 @@ pub struct SyncTransportStatusSummary { pub endpoint_uri: Option<String>, pub configured: bool, pub implementation: String, + pub maturity: String, + pub availability: String, pub usable_for_delivery: bool, pub capabilities: SyncTransportOperationCapabilitiesSummary, pub message: String, @@ -253,6 +253,8 @@ impl SyncTransportStatusSummary { endpoint_uri: status.endpoint_uri, configured: status.configured, implementation: transport_implementation_label(status.implementation).to_owned(), + maturity: transport_maturity_label(status.maturity).to_owned(), + availability: transport_availability_label(status.availability).to_owned(), usable_for_delivery: status.usable_for_delivery, capabilities: SyncTransportOperationCapabilitiesSummary { deliver: status.capabilities.deliver, @@ -268,7 +270,25 @@ fn transport_implementation_label(state: RadrootsTransportImplementationState) - match state { RadrootsTransportImplementationState::Real => "real", RadrootsTransportImplementationState::Mock => "mock", - RadrootsTransportImplementationState::PreviewUnavailable => "preview_unavailable", + } +} + +#[cfg(feature = "runtime")] +fn transport_maturity_label(maturity: RadrootsTransportCapabilityMaturity) -> &'static str { + match maturity { + RadrootsTransportCapabilityMaturity::Preview => "preview", + RadrootsTransportCapabilityMaturity::Stable => "stable", + } +} + +#[cfg(feature = "runtime")] +fn transport_availability_label( + availability: RadrootsTransportCapabilityAvailability, +) -> &'static str { + match availability { + RadrootsTransportCapabilityAvailability::Available => "available", + RadrootsTransportCapabilityAvailability::Degraded => "degraded", + RadrootsTransportCapabilityAvailability::Unavailable => "unavailable", } } @@ -511,7 +531,6 @@ pub enum PushOutboxEventState { SignRetryable, PublishRetryable, DeferredUntilImplemented, - PreviewUnavailable, FailedTerminal, Cancelled, } @@ -528,7 +547,6 @@ impl From<RadrootsOutboxEventState> for PushOutboxEventState { RadrootsOutboxEventState::SignRetryable => Self::SignRetryable, RadrootsOutboxEventState::PublishRetryable => Self::PublishRetryable, RadrootsOutboxEventState::DeferredUntilImplemented => Self::DeferredUntilImplemented, - RadrootsOutboxEventState::PreviewUnavailable => Self::PreviewUnavailable, RadrootsOutboxEventState::FailedTerminal => Self::FailedTerminal, RadrootsOutboxEventState::Cancelled => Self::Cancelled, } @@ -557,7 +575,6 @@ pub enum PushOutboxTargetOutcomeKind { TargetUriRejected, SkippedAlreadyAccepted, DeferredUntilImplemented, - PreviewUnavailable, Unknown, } @@ -582,7 +599,6 @@ impl PushOutboxTargetOutcomeKind { Self::TargetUriRejected => "target_uri_rejected", Self::SkippedAlreadyAccepted => "skipped_already_accepted", Self::DeferredUntilImplemented => "deferred_until_implemented", - Self::PreviewUnavailable => "preview_unavailable", Self::Unknown => "unknown", } } @@ -711,8 +727,25 @@ impl<'sdk> SyncClient<'sdk> { &self, request: PushOutboxRequest, ) -> Result<PushOutboxReceipt, RadrootsSdkError> { + #[cfg(feature = "radrootsd-execution")] + if let Some(profile) = self.sdk.radrootsd_execution_profile() { + let adapter = + RadrootsdPublishAdapter::new(radrootsd_publish_config_from_profile(profile)); + return self + .push_outbox_with_radrootsd_adapter(&adapter, request) + .await; + } + + #[cfg(not(feature = "radrootsd-execution"))] + if self.sdk.radrootsd_execution_profile().is_some() { + return Err(RadrootsSdkError::ProductSyncUnsupported { + operation: "sync.push_outbox", + required_feature: "radrootsd-execution", + }); + } + match self.sdk.transport_profile() { - TransportProfile::Nostr { .. } | TransportProfile::Hybrid { .. } => { + TransportProfile::Nostr { .. } | TransportProfile::MultiTarget { .. } => { #[cfg(feature = "transport-nostr-runtime")] { let adapter = RadrootsNostrClientPublishAdapter::new( @@ -731,17 +764,6 @@ impl<'sdk> SyncClient<'sdk> { }) } } - #[cfg(feature = "radrootsd-proxy")] - TransportProfile::Proxy { profile } => { - let adapter = - RadrootsdProxyPublishAdapter::new(radrootsd_proxy_config_from_profile(profile)); - self.push_outbox_with_proxy_adapter(&adapter, request).await - } - #[cfg(not(feature = "radrootsd-proxy"))] - TransportProfile::Proxy { .. } => Err(RadrootsSdkError::ProductSyncUnsupported { - operation: "sync.push_outbox", - required_feature: "radrootsd-proxy", - }), TransportProfile::LocalOnly => { if self.push_outbox_has_no_ready_signed_work(&request).await? { return Ok(PushOutboxReceipt::default()); @@ -751,26 +773,21 @@ impl<'sdk> SyncClient<'sdk> { required_feature: "delivery-capable transport profile", }) } - TransportProfile::ReticulumPreview { .. } => { - self.reticulum_preview_push_receipt(request).await - } + TransportProfile::Reticulum { .. } => self.reticulum_push_receipt(request).await, } } - pub async fn try_reticulum_preview_now( + pub async fn try_reticulum_now( &self, - _request: ReticulumPreviewTryNowRequest, + _request: ReticulumTryNowRequest, ) -> Result<(), RadrootsSdkError> { - let profile = - active_reticulum_preview_profile(self.sdk.transport_profile()).ok_or_else(|| { - RadrootsSdkError::InvalidRequest { - message: - "sync.try_reticulum_preview_now requires a Reticulum preview transport profile" - .to_owned(), + let profile = active_reticulum_profile(self.sdk.transport_profile()).ok_or_else(|| { + RadrootsSdkError::InvalidRequest { + message: "sync.try_reticulum_now requires a Reticulum transport profile".to_owned(), } - })?; - Err(RadrootsSdkError::ReticulumPreviewTransportUnavailable { - operation: "sync.try_reticulum_preview_now".to_owned(), + })?; + Err(RadrootsSdkError::ReticulumTransportUnavailable { + operation: "sync.try_reticulum_now".to_owned(), endpoint_uri: profile.endpoint_uri().to_owned(), behavior: profile.behavior(), }) @@ -840,10 +857,10 @@ impl<'sdk> SyncClient<'sdk> { Ok(receipt) } - #[cfg(feature = "radrootsd-proxy")] - pub async fn push_outbox_with_proxy_adapter( + #[cfg(feature = "radrootsd-execution")] + pub async fn push_outbox_with_radrootsd_adapter( &self, - adapter: &RadrootsdProxyPublishAdapter, + adapter: &RadrootsdPublishAdapter, request: PushOutboxRequest, ) -> Result<PushOutboxReceipt, RadrootsSdkError> { request.validate()?; @@ -868,7 +885,7 @@ impl<'sdk> SyncClient<'sdk> { break; }; let publish_now_ms = claim_now_ms; - let publish = push_proxy_claimed_outbox_event( + let publish = push_radrootsd_claimed_outbox_event( self, adapter, &claimed, @@ -881,7 +898,7 @@ impl<'sdk> SyncClient<'sdk> { Ok(receipt) } - async fn reticulum_preview_push_receipt( + async fn reticulum_push_receipt( &self, request: PushOutboxRequest, ) -> Result<PushOutboxReceipt, RadrootsSdkError> { @@ -889,22 +906,24 @@ impl<'sdk> SyncClient<'sdk> { let records = self .sdk ._outbox - .reticulum_preview_events(request.outbox_event_id, request.limit) + .reticulum_events(request.outbox_event_id, request.limit) .await?; let mut receipt = PushOutboxReceipt::default(); for record in records { - receipt.push_reported_event(reticulum_preview_event_receipt(record)?); + receipt.push_reported_event(reticulum_event_receipt(record)?); } Ok(receipt) } } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn radrootsd_proxy_config_from_profile(profile: &ProxyProfile) -> RadrootsdProxyConfig { - let config = RadrootsdProxyConfig::new(profile.endpoint_url().to_owned()); +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_publish_config_from_profile( + profile: &RadrootsdExecutionProfile, +) -> RadrootsdPublishConfig { + let config = RadrootsdPublishConfig::new(profile.endpoint_url().to_owned()); match profile.auth() { - ProxyAuth::None => config, - ProxyAuth::BearerToken(token) => { + RadrootsdExecutionAuth::None => config, + RadrootsdExecutionAuth::BearerToken(token) => { config.with_auth(RadrootsdAuth::BearerToken(token.to_owned())) } } @@ -951,14 +970,12 @@ async fn claim_ready_signed_event_for_push( } #[cfg(feature = "runtime")] -fn reticulum_preview_event_receipt( - record: RadrootsOutboxReticulumPreviewEventRecord, +fn reticulum_event_receipt( + record: RadrootsOutboxReticulumEventRecord, ) -> Result<PushOutboxEventReceipt, RadrootsSdkError> { - let event_id = push_receipt_event_id( - record.event.event_id.as_str(), - "Reticulum preview outbox event id", - )?; - let final_state = reticulum_preview_event_final_state(record.event.state, &record.targets); + let event_id = + push_receipt_event_id(record.event.event_id.as_str(), "Reticulum outbox event id")?; + let final_state = reticulum_event_final_state(record.event.state, &record.targets); let quorum = record.targets.len(); Ok(PushOutboxEventReceipt { event_id, @@ -973,36 +990,34 @@ fn reticulum_preview_event_receipt( targets: record .targets .into_iter() - .map(reticulum_preview_target_receipt) + .map(reticulum_target_receipt) .collect(), }) } #[cfg(feature = "runtime")] -fn reticulum_preview_event_final_state( +fn reticulum_event_final_state( event_state: RadrootsOutboxEventState, targets: &[RadrootsOutboxDeliveryTargetRecord], ) -> PushOutboxEventState { - if event_state == RadrootsOutboxEventState::PreviewUnavailable + if event_state == RadrootsOutboxEventState::DeferredUntilImplemented || targets.iter().any(|target| { matches!( target.status, - RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable + RadrootsOutboxDeliveryTargetStatus::DeferredUntilImplemented | RadrootsOutboxDeliveryTargetStatus::Pending | RadrootsOutboxDeliveryTargetStatus::FailedRetryable ) }) { - PushOutboxEventState::PreviewUnavailable + PushOutboxEventState::DeferredUntilImplemented } else { PushOutboxEventState::DeferredUntilImplemented } } #[cfg(feature = "runtime")] -fn reticulum_preview_target_receipt( - target: RadrootsOutboxDeliveryTargetRecord, -) -> PushOutboxTargetReceipt { +fn reticulum_target_receipt(target: RadrootsOutboxDeliveryTargetRecord) -> PushOutboxTargetReceipt { PushOutboxTargetReceipt { transport_kind: target.transport_kind.canonical_label(), endpoint_uri: target.endpoint_uri.as_str().to_owned(), @@ -1014,7 +1029,7 @@ fn reticulum_preview_target_receipt( .target_label .as_ref() .map(|label| label.as_str().to_owned()), - outcome_kind: reticulum_preview_target_outcome_kind(target.status), + outcome_kind: reticulum_target_outcome_kind(target.status), transport_outcome_kind: target.last_outcome_kind.map(Into::into), attempted: false, message: Some( @@ -1026,20 +1041,16 @@ fn reticulum_preview_target_receipt( } #[cfg(feature = "runtime")] -fn active_reticulum_preview_profile( - profile: &TransportProfile, -) -> Option<&ReticulumPreviewProfile> { +fn active_reticulum_profile(profile: &TransportProfile) -> Option<&ReticulumProfile> { match profile { - TransportProfile::ReticulumPreview { profile } => Some(profile), - TransportProfile::Hybrid { profile } => Some(profile.reticulum_preview()), - TransportProfile::LocalOnly - | TransportProfile::Nostr { .. } - | TransportProfile::Proxy { .. } => None, + TransportProfile::Reticulum { profile } => Some(profile), + TransportProfile::MultiTarget { profile } => Some(profile.reticulum()), + TransportProfile::LocalOnly | TransportProfile::Nostr { .. } => None, } } #[cfg(feature = "runtime")] -fn reticulum_preview_target_outcome_kind( +fn reticulum_target_outcome_kind( status: RadrootsOutboxDeliveryTargetStatus, ) -> PushOutboxTargetOutcomeKind { match status { @@ -1047,9 +1058,8 @@ fn reticulum_preview_target_outcome_kind( PushOutboxTargetOutcomeKind::DeferredUntilImplemented } RadrootsOutboxDeliveryTargetStatus::Pending - | RadrootsOutboxDeliveryTargetStatus::FailedRetryable - | RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable => { - PushOutboxTargetOutcomeKind::PreviewUnavailable + | RadrootsOutboxDeliveryTargetStatus::FailedRetryable => { + PushOutboxTargetOutcomeKind::DeferredUntilImplemented } RadrootsOutboxDeliveryTargetStatus::Accepted | RadrootsOutboxDeliveryTargetStatus::Delivered @@ -1081,10 +1091,10 @@ pub(crate) async fn refresh_product_projections_for_sdk( )) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -async fn push_proxy_claimed_outbox_event( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +async fn push_radrootsd_claimed_outbox_event( sync: &SyncClient<'_>, - adapter: &RadrootsdProxyPublishAdapter, + adapter: &RadrootsdPublishAdapter, claimed: &RadrootsOutboxClaimedEvent, next_attempt_delay_ms: i64, now_ms: i64, @@ -1094,16 +1104,16 @@ async fn push_proxy_claimed_outbox_event( claimed.outbox_event_id, ), )?; - let target_policy = match proxy_transport_publish_target_policy(claimed) { + let target_policy = match radrootsd_transport_publish_target_policy(claimed) { Ok(target_policy) => target_policy, Err(error) => { - return fail_proxy_local_validation(sync, claimed, error, now_ms).await; + return fail_radrootsd_local_validation(sync, claimed, error, now_ms).await; } }; - let delivery_policy = match proxy_delivery_policy(sync, claimed).await { + let delivery_policy = match radrootsd_delivery_policy(sync, claimed).await { Ok(delivery_policy) => delivery_policy, Err(error) => { - return fail_proxy_local_validation(sync, claimed, error, now_ms).await; + return fail_radrootsd_local_validation(sync, claimed, error, now_ms).await; } }; sync.sdk @@ -1115,22 +1125,22 @@ async fn push_proxy_claimed_outbox_event( now_ms, ) .await?; - let request = RadrootsdProxyPublishRequest { + let request = RadrootsdPublishRequest { signed_event: signed_event.clone(), delivery_policy: delivery_policy.clone(), target_policy, - idempotency_key: Some(proxy_outbox_idempotency_key( + idempotency_key: Some(radrootsd_outbox_idempotency_key( claimed.outbox_event_id, claimed.attempt_count, signed_event.id_str(), - active_delivery_plan_id(claimed, "radrootsd proxy publish")?, + active_delivery_plan_id(claimed, "radrootsd publish")?, )), timeout_ms: adapter.config().request_timeout_ms, }; let publish = match adapter.publish_signed_event(request).await { Ok(response) => response.job, Err(error) => { - let message = proxy_error_message(&error); + let message = radrootsd_error_message(&error); sync.sdk ._outbox .mark_publish_retryable( @@ -1141,7 +1151,7 @@ async fn push_proxy_claimed_outbox_event( now_ms, ) .await?; - return proxy_transport_error_receipt( + return radrootsd_transport_error_receipt( claimed, &signed_event, &delivery_policy, @@ -1149,12 +1159,13 @@ async fn push_proxy_claimed_outbox_event( ); } }; - complete_proxy_publish_attempt(sync, claimed, &publish, next_attempt_delay_ms, now_ms).await?; - push_proxy_event_receipt(claimed.outbox_event_id, publish) + complete_radrootsd_publish_attempt(sync, claimed, &publish, next_attempt_delay_ms, now_ms) + .await?; + push_radrootsd_event_receipt(claimed.outbox_event_id, publish) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -async fn fail_proxy_local_validation( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +async fn fail_radrootsd_local_validation( sync: &SyncClient<'_>, claimed: &RadrootsOutboxClaimedEvent, error: RadrootsSdkError, @@ -1173,12 +1184,12 @@ async fn fail_proxy_local_validation( Err(error) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -async fn proxy_delivery_policy( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +async fn radrootsd_delivery_policy( sync: &SyncClient<'_>, claimed: &RadrootsOutboxClaimedEvent, ) -> Result<TransportPublishDeliveryPolicy, RadrootsSdkError> { - let active_delivery_plan_id = active_delivery_plan_id(claimed, "radrootsd proxy publish")?; + let active_delivery_plan_id = active_delivery_plan_id(claimed, "radrootsd publish")?; let plans = sync .sdk ._outbox @@ -1189,7 +1200,7 @@ async fn proxy_delivery_policy( .find(|plan| plan.delivery_plan_id == active_delivery_plan_id) .ok_or_else(|| RadrootsSdkError::InvalidRequest { message: format!( - "outbox event {} active delivery plan {} was not found for proxy publish", + "outbox event {} active delivery plan {} was not found for radrootsd publish", claimed.outbox_event_id, active_delivery_plan_id ), })?; @@ -1202,13 +1213,13 @@ async fn proxy_delivery_policy( .iter() .filter(|target| target.delivery_plan_id == active_delivery_plan_id) .collect::<Vec<_>>(); - reject_non_accepted_proxy_satisfaction(&plan.satisfaction_policy)?; + reject_non_accepted_radrootsd_satisfaction(&plan.satisfaction_policy)?; let ready_target_count = active_targets .iter() .filter(|target| target.status.is_ready_for_attempt()) .count(); let required_remaining_targets = - proxy_required_remaining_targets(&plan.satisfaction_policy, &active_targets)?; + radrootsd_required_remaining_targets(&plan.satisfaction_policy, &active_targets)?; let required_remaining = if let Some(targets) = required_remaining_targets.as_ref() { targets.len() } else { @@ -1228,7 +1239,7 @@ async fn proxy_delivery_policy( .unwrap_or(0); (plan.required_success_count as usize).saturating_sub(satisfied_count) }; - proxy_delivery_policy_from_remaining( + radrootsd_delivery_policy_from_remaining( ready_target_count, required_remaining, required_remaining_targets.as_deref(), @@ -1236,14 +1247,14 @@ async fn proxy_delivery_policy( ) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn proxy_delivery_policy_from_remaining( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_delivery_policy_from_remaining( ready_target_count: usize, required_remaining: usize, required_remaining_targets: Option<&[RadrootsTransportTargetFingerprint]>, satisfaction_policy: &RadrootsTransportSatisfactionPolicy, ) -> Result<TransportPublishDeliveryPolicy, RadrootsSdkError> { - reject_non_accepted_proxy_satisfaction(satisfaction_policy)?; + reject_non_accepted_radrootsd_satisfaction(satisfaction_policy)?; if required_remaining == 0 { return Ok(TransportPublishDeliveryPolicy::Any); } @@ -1253,9 +1264,8 @@ fn proxy_delivery_policy_from_remaining( RadrootsTransportSatisfactionPolicy::RequiredTargets { .. } ) { return Err(RadrootsSdkError::InvalidRequest { - message: - "radrootsd proxy publish has unsatisfied required targets but no ready target" - .to_owned(), + message: "radrootsd publish has unsatisfied required targets but no ready target" + .to_owned(), }); } return Ok(TransportPublishDeliveryPolicy::Any); @@ -1268,7 +1278,7 @@ fn proxy_delivery_policy_from_remaining( TransportPublishDeliveryPolicy::required_targets( required_remaining_targets .ok_or_else(|| RadrootsSdkError::InvalidRequest { - message: "radrootsd proxy publish missing required target fingerprints" + message: "radrootsd publish missing required target fingerprints" .to_owned(), })? .to_vec(), @@ -1291,8 +1301,8 @@ fn proxy_delivery_policy_from_remaining( }) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn proxy_required_remaining_targets( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_required_remaining_targets( satisfaction_policy: &RadrootsTransportSatisfactionPolicy, active_targets: &[&RadrootsOutboxDeliveryTargetRecord], ) -> Result<Option<Vec<RadrootsTransportTargetFingerprint>>, RadrootsSdkError> { @@ -1308,7 +1318,7 @@ fn proxy_required_remaining_targets( .find(|target| target.endpoint_fingerprint == *required) .ok_or_else(|| RadrootsSdkError::InvalidRequest { message: format!( - "radrootsd proxy publish required target {required} is not present in active delivery plan" + "radrootsd publish required target {required} is not present in active delivery plan" ), })?; if target.status.counts_as_transport_satisfaction(*class) { @@ -1317,7 +1327,7 @@ fn proxy_required_remaining_targets( if !target.status.is_ready_for_attempt() { return Err(RadrootsSdkError::InvalidRequest { message: format!( - "radrootsd proxy publish required target {required} is not ready for publish" + "radrootsd publish required target {required} is not ready for publish" ), }); } @@ -1326,8 +1336,8 @@ fn proxy_required_remaining_targets( Ok(Some(remaining)) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn reject_non_accepted_proxy_satisfaction( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn reject_non_accepted_radrootsd_satisfaction( satisfaction_policy: &RadrootsTransportSatisfactionPolicy, ) -> Result<(), RadrootsSdkError> { if satisfaction_policy @@ -1335,15 +1345,15 @@ fn reject_non_accepted_proxy_satisfaction( .is_some_and(|class| class != RadrootsTransportSatisfactionClass::Accepted) { return Err(RadrootsSdkError::InvalidRequest { - message: "radrootsd proxy publish only supports accepted-class satisfaction policies" + message: "radrootsd publish only supports accepted-class satisfaction policies" .to_owned(), }); } Ok(()) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn proxy_outbox_idempotency_key( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_outbox_idempotency_key( outbox_event_id: i64, attempt_count: i64, event_id: &str, @@ -1354,7 +1364,7 @@ fn proxy_outbox_idempotency_key( ) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] fn active_delivery_plan_id( claimed: &RadrootsOutboxClaimedEvent, operation: &'static str, @@ -1369,8 +1379,8 @@ fn active_delivery_plan_id( }) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -async fn complete_proxy_publish_attempt( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +async fn complete_radrootsd_publish_attempt( sync: &SyncClient<'_>, claimed: &RadrootsOutboxClaimedEvent, publish: &TransportPublishJobView, @@ -1384,7 +1394,7 @@ async fn complete_proxy_publish_attempt( .delivery_targets .iter() .filter(|target| target.status.is_ready_for_attempt()) - .filter(|target| proxy_target_matches_outcome(target, outcome)) + .filter(|target| radrootsd_target_matches_outcome(target, outcome)) .collect::<Vec<_>>(); if matched_targets.is_empty() { continue; @@ -1392,7 +1402,7 @@ async fn complete_proxy_publish_attempt( if matched_targets.len() > 1 { return Err(RadrootsSdkError::InvalidRequest { message: format!( - "radrootsd proxy publish outcome for {} {} matched multiple ready delivery targets on outbox event {}", + "radrootsd publish outcome for {} {} matched multiple ready delivery targets on outbox event {}", outcome.transport_kind, outcome.endpoint_uri, claimed.outbox_event_id ), }); @@ -1401,7 +1411,7 @@ async fn complete_proxy_publish_attempt( if !completed_target_ids.insert(target.delivery_target_id) { return Err(RadrootsSdkError::InvalidRequest { message: format!( - "radrootsd proxy publish outcome for {} {} matched delivery target {} more than once on outbox event {}", + "radrootsd publish outcome for {} {} matched delivery target {} more than once on outbox event {}", outcome.transport_kind, outcome.endpoint_uri, target.delivery_target_id, @@ -1412,7 +1422,7 @@ async fn complete_proxy_publish_attempt( matched_outcomes.push((target, outcome)); } for (target, outcome) in matched_outcomes { - complete_proxy_delivery_target(sync, claimed, target, outcome, now_ms).await?; + complete_radrootsd_delivery_target(sync, claimed, target, outcome, now_ms).await?; } for target in claimed .delivery_targets @@ -1420,15 +1430,15 @@ async fn complete_proxy_publish_attempt( .filter(|target| target.status.is_ready_for_attempt()) .filter(|target| !completed_target_ids.contains(&target.delivery_target_id)) { - complete_missing_proxy_delivery_target(sync, claimed, target, publish, now_ms).await?; + complete_missing_radrootsd_delivery_target(sync, claimed, target, publish, now_ms).await?; } sync.sdk ._outbox .complete_publish_attempt( claimed.outbox_event_id, claimed.claim_token.as_str(), - "radrootsd proxy publish incomplete", - "radrootsd proxy publish terminal", + "radrootsd publish incomplete", + "radrootsd publish terminal", now_ms.saturating_add(next_attempt_delay_ms), now_ms, ) @@ -1436,8 +1446,8 @@ async fn complete_proxy_publish_attempt( Ok(()) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn proxy_transport_publish_target_policy( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_transport_publish_target_policy( claimed: &RadrootsOutboxClaimedEvent, ) -> Result<TransportPublishTargetPolicy, RadrootsSdkError> { let ready_targets = claimed @@ -1445,40 +1455,22 @@ fn proxy_transport_publish_target_policy( .iter() .filter(|target| target.status.is_ready_for_attempt()) .collect::<Vec<_>>(); - if ready_targets - .iter() - .any(|target| is_proxy_delegate_target(target)) - { - if ready_targets.len() != 1 || !is_proxy_delegate_target(ready_targets[0]) { - return Err(RadrootsSdkError::InvalidRequest { - message: format!( - "radrootsd proxy outbox publish does not accept mixed proxy delegate targets for outbox event {}", - claimed.outbox_event_id - ), - }); - } - Ok(TransportPublishTargetPolicy::nostr( - NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, - Vec::new(), - )) - } else { - Ok(TransportPublishTargetPolicy::explicit_targets( - ready_targets - .into_iter() - .map(transport_publish_target_from_outbox_target) - .collect::<Result<Vec<_>, _>>()?, - )) - } + Ok(TransportPublishTargetPolicy::explicit_targets( + ready_targets + .into_iter() + .map(transport_publish_target_from_outbox_target) + .collect::<Result<Vec<_>, _>>()?, + )) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] fn transport_publish_target_from_outbox_target( target: &RadrootsOutboxDeliveryTargetRecord, ) -> Result<TransportPublishTarget, RadrootsSdkError> { if target.transport_kind != RadrootsTransportKind::Nostr { return Err(RadrootsSdkError::InvalidRequest { message: format!( - "radrootsd proxy outbox publish explicit targets are Nostr-only and cannot publish {} target {}", + "radrootsd execution explicit targets are Nostr-only and cannot publish {} target {}", target.transport_kind.canonical_label(), target.endpoint_uri.as_str() ), @@ -1495,12 +1487,12 @@ fn transport_publish_target_from_outbox_target( .target_label .as_ref() .map(|label| label.as_str().to_owned()), - preview_behavior: None, + reticulum_behavior: None, }) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn proxy_target_matches_outcome( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_target_matches_outcome( target: &RadrootsOutboxDeliveryTargetRecord, outcome: &TransportPublishTargetOutcome, ) -> bool { @@ -1510,13 +1502,8 @@ fn proxy_target_matches_outcome( == outcome.target_scope.as_deref() } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn is_proxy_delegate_target(target: &RadrootsOutboxDeliveryTargetRecord) -> bool { - target.transport_kind == RadrootsTransportKind::Proxy -} - -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -async fn complete_proxy_delivery_target( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +async fn complete_radrootsd_delivery_target( sync: &SyncClient<'_>, claimed: &RadrootsOutboxClaimedEvent, target: &RadrootsOutboxDeliveryTargetRecord, @@ -1543,7 +1530,7 @@ async fn complete_proxy_delivery_target( outcome .message .as_deref() - .unwrap_or("radrootsd proxy publish retryable"), + .unwrap_or("radrootsd publish retryable"), now_ms, ) .await?; @@ -1557,21 +1544,7 @@ async fn complete_proxy_delivery_target( outcome .message .as_deref() - .unwrap_or("radrootsd proxy publish deferred until implemented"), - now_ms, - ) - .await?; - } else if outcome.outcome_kind == TransportPublishOutcomeKind::PreviewUnavailable { - sync.sdk - ._outbox - .mark_delivery_target_preview_unavailable( - claimed.outbox_event_id, - claimed.claim_token.as_str(), - target.delivery_target_id, - outcome - .message - .as_deref() - .unwrap_or("radrootsd proxy publish preview unavailable"), + .unwrap_or("radrootsd publish deferred until implemented"), now_ms, ) .await?; @@ -1585,7 +1558,7 @@ async fn complete_proxy_delivery_target( outcome .message .as_deref() - .unwrap_or("radrootsd proxy publish terminal"), + .unwrap_or("radrootsd publish terminal"), now_ms, ) .await?; @@ -1593,43 +1566,33 @@ async fn complete_proxy_delivery_target( Ok(()) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -async fn complete_missing_proxy_delivery_target( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +async fn complete_missing_radrootsd_delivery_target( sync: &SyncClient<'_>, claimed: &RadrootsOutboxClaimedEvent, target: &RadrootsOutboxDeliveryTargetRecord, publish: &TransportPublishJobView, now_ms: i64, ) -> Result<(), RadrootsSdkError> { - if is_proxy_delegate_target(target) && publish.delivery_satisfied { - sync.sdk - ._outbox - .mark_delivery_target_accepted( - claimed.outbox_event_id, - claimed.claim_token.as_str(), - target.delivery_target_id, - now_ms, - ) - .await?; - } else if publish.status == TransportPublishJobStatus::DeliveryDeferred { + if publish.status == TransportPublishJobStatus::DeliveryDeferred { sync.sdk ._outbox .mark_delivery_target_deferred_until_implemented( claimed.outbox_event_id, claimed.claim_token.as_str(), target.delivery_target_id, - "radrootsd proxy publish deferred until implemented", + "radrootsd publish deferred until implemented", now_ms, ) .await?; - } else if publish.status == TransportPublishJobStatus::DeliveryPreviewUnavailable { + } else if publish.status == TransportPublishJobStatus::DeliveryDeferredUntilImplemented { sync.sdk ._outbox - .mark_delivery_target_preview_unavailable( + .mark_delivery_target_deferred_until_implemented( claimed.outbox_event_id, claimed.claim_token.as_str(), target.delivery_target_id, - "radrootsd proxy publish preview unavailable", + "radrootsd publish deferred until implemented", now_ms, ) .await?; @@ -1643,7 +1606,7 @@ async fn complete_missing_proxy_delivery_target( claimed.outbox_event_id, claimed.claim_token.as_str(), target.delivery_target_id, - "radrootsd proxy publish incomplete", + "radrootsd publish incomplete", now_ms, ) .await?; @@ -1654,7 +1617,7 @@ async fn complete_missing_proxy_delivery_target( claimed.outbox_event_id, claimed.claim_token.as_str(), target.delivery_target_id, - "radrootsd proxy publish terminal", + "radrootsd publish terminal", now_ms, ) .await?; @@ -1662,13 +1625,13 @@ async fn complete_missing_proxy_delivery_target( Ok(()) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn proxy_error_message(error: &RadrootsdError) -> String { - format!("radrootsd proxy publish failed: {error}") +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_error_message(error: &RadrootsdError) -> String { + format!("radrootsd publish failed: {error}") } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn proxy_transport_error_receipt( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_transport_error_receipt( claimed: &RadrootsOutboxClaimedEvent, event: &radroots_event::draft::RadrootsSignedEvent, delivery_policy: &TransportPublishDeliveryPolicy, @@ -1680,7 +1643,7 @@ fn proxy_transport_error_receipt( .filter(|target| target.status.is_ready_for_attempt()) .collect::<Vec<_>>(); let target_count = ready_targets.len(); - let event_id = push_receipt_event_id(event.id_str(), "proxy transport failure event id")?; + let event_id = push_receipt_event_id(event.id_str(), "radrootsd transport failure event id")?; Ok(PushOutboxEventReceipt { event_id, outbox_event_id: claimed.outbox_event_id, @@ -1713,14 +1676,14 @@ fn proxy_transport_error_receipt( }) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn proxy_push_event_final_state(publish: &TransportPublishJobView) -> PushOutboxEventState { +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn radrootsd_push_event_final_state(publish: &TransportPublishJobView) -> PushOutboxEventState { if publish.delivery_satisfied { PushOutboxEventState::Published } else if publish.status == TransportPublishJobStatus::DeliveryDeferred { PushOutboxEventState::DeferredUntilImplemented - } else if publish.status == TransportPublishJobStatus::DeliveryPreviewUnavailable { - PushOutboxEventState::PreviewUnavailable + } else if publish.status == TransportPublishJobStatus::DeliveryDeferredUntilImplemented { + PushOutboxEventState::DeferredUntilImplemented } else if publish.retryable_count > 0 || !publish.terminal { PushOutboxEventState::PublishRetryable } else { @@ -1728,8 +1691,8 @@ fn proxy_push_event_final_state(publish: &TransportPublishJobView) -> PushOutbox } } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn push_proxy_event_receipt( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn push_radrootsd_event_receipt( outbox_event_id: i64, publish: TransportPublishJobView, ) -> Result<PushOutboxEventReceipt, RadrootsSdkError> { @@ -1743,7 +1706,7 @@ fn push_proxy_event_receipt( Ok(PushOutboxEventReceipt { event_id, outbox_event_id, - final_state: proxy_push_event_final_state(&publish), + final_state: radrootsd_push_event_final_state(&publish), attempted_count: publish .targets .iter() @@ -1757,27 +1720,29 @@ fn push_proxy_event_receipt( targets: publish .targets .into_iter() - .map(push_proxy_target_receipt) + .map(push_radrootsd_target_receipt) .collect(), }) } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn push_proxy_target_receipt(outcome: TransportPublishTargetOutcome) -> PushOutboxTargetReceipt { +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn push_radrootsd_target_receipt( + outcome: TransportPublishTargetOutcome, +) -> PushOutboxTargetReceipt { PushOutboxTargetReceipt { transport_kind: outcome.transport_kind, endpoint_uri: outcome.endpoint_uri, target_scope: outcome.target_scope, target_label: outcome.target_label, - outcome_kind: push_proxy_target_outcome_kind(outcome.outcome_kind), - transport_outcome_kind: Some(push_proxy_transport_outcome_kind(outcome.outcome_kind)), + outcome_kind: push_radrootsd_target_outcome_kind(outcome.outcome_kind), + transport_outcome_kind: Some(push_radrootsd_transport_outcome_kind(outcome.outcome_kind)), attempted: outcome.attempted, message: outcome.message, } } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn push_proxy_target_outcome_kind( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn push_radrootsd_target_outcome_kind( outcome_kind: TransportPublishOutcomeKind, ) -> PushOutboxTargetOutcomeKind { match outcome_kind { @@ -1810,15 +1775,12 @@ fn push_proxy_target_outcome_kind( TransportPublishOutcomeKind::DeferredUntilImplemented => { PushOutboxTargetOutcomeKind::DeferredUntilImplemented } - TransportPublishOutcomeKind::PreviewUnavailable => { - PushOutboxTargetOutcomeKind::PreviewUnavailable - } TransportPublishOutcomeKind::Unknown => PushOutboxTargetOutcomeKind::Unknown, } } -#[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn push_proxy_transport_outcome_kind( +#[cfg(all(feature = "runtime", feature = "radrootsd-execution"))] +fn push_radrootsd_transport_outcome_kind( outcome_kind: TransportPublishOutcomeKind, ) -> PushOutboxTransportOutcomeKind { match outcome_kind { @@ -1845,7 +1807,6 @@ fn push_proxy_transport_outcome_kind( } TransportPublishOutcomeKind::RateLimited | TransportPublishOutcomeKind::Error - | TransportPublishOutcomeKind::PreviewUnavailable | TransportPublishOutcomeKind::Unknown => { PushOutboxTransportOutcomeKind::TransportUnavailable } diff --git a/crates/sdk/src/transport.rs b/crates/sdk/src/transport.rs @@ -1,6 +1,7 @@ use crate::RadrootsSdkError; use radroots_transport::{ - RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, + RADROOTS_RETICULUM_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, + RadrootsTransportCapabilityAvailability, RadrootsTransportCapabilityMaturity, RadrootsTransportImplementationState, RadrootsTransportMeshScopeId, RadrootsTransportSatisfactionPolicy, RadrootsTransportStatus, RadrootsTransportTarget, RadrootsTransportTargetFingerprint, RadrootsTransportTargetSet, @@ -244,8 +245,8 @@ impl MeshScopeId { Ok(Self(RadrootsTransportMeshScopeId::parse(raw)?)) } - pub fn local_preview() -> Self { - Self(RadrootsTransportMeshScopeId::local_preview()) + pub fn local_reticulum() -> Self { + Self(RadrootsTransportMeshScopeId::local_reticulum()) } pub fn as_str(&self) -> &str { @@ -332,23 +333,13 @@ impl TargetSet { targets.len(), )); } - if targets - .iter() - .any(|target| target.kind == RadrootsTransportKind::Proxy) - && (targets.len() != 1 || targets[0].kind != RadrootsTransportKind::Proxy) - { - return Err(RadrootsSdkError::InvalidRequest { - message: "proxy transport targets must be the only target in a target set" - .to_owned(), - }); - } for target in &targets { if target.kind == RadrootsTransportKind::Reticulum - && target.uri.as_str() != RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI + && target.uri.as_str() != RADROOTS_RETICULUM_ENDPOINT_URI { return Err(RadrootsSdkError::InvalidRequest { message: format!( - "Reticulum preview endpoint must be {RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI}" + "Reticulum endpoint must be {RADROOTS_RETICULUM_ENDPOINT_URI}" ), }); } @@ -405,24 +396,24 @@ impl NostrProfile { } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] -pub struct ReticulumPreviewProfile { +pub struct ReticulumProfile { endpoint_uri: String, scope: MeshScopeId, - agent_endpoint: Option<ReticulumPreviewAgentEndpoint>, - behavior: ReticulumPreviewBehavior, + agent_endpoint: Option<ReticulumAgentEndpoint>, + behavior: ReticulumBehavior, } -impl ReticulumPreviewProfile { - pub fn preview_unavailable() -> Self { +impl ReticulumProfile { + pub fn deferred_until_implemented() -> Self { Self { - endpoint_uri: RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), - scope: MeshScopeId::local_preview(), + endpoint_uri: RADROOTS_RETICULUM_ENDPOINT_URI.to_owned(), + scope: MeshScopeId::local_reticulum(), agent_endpoint: None, - behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts, + behavior: ReticulumBehavior::RejectDeliveryAttempts, } } - pub fn with_behavior(mut self, behavior: ReticulumPreviewBehavior) -> Self { + pub fn with_behavior(mut self, behavior: ReticulumBehavior) -> Self { self.behavior = behavior; self } @@ -435,11 +426,11 @@ impl ReticulumPreviewProfile { &self.scope } - pub fn agent_endpoint(&self) -> Option<&ReticulumPreviewAgentEndpoint> { + pub fn agent_endpoint(&self) -> Option<&ReticulumAgentEndpoint> { self.agent_endpoint.as_ref() } - pub fn with_agent_endpoint(mut self, agent_endpoint: ReticulumPreviewAgentEndpoint) -> Self { + pub fn with_agent_endpoint(mut self, agent_endpoint: ReticulumAgentEndpoint) -> Self { self.agent_endpoint = Some(agent_endpoint); self } @@ -449,34 +440,32 @@ impl ReticulumPreviewProfile { self } - pub fn behavior(&self) -> ReticulumPreviewBehavior { + pub fn behavior(&self) -> ReticulumBehavior { self.behavior } pub fn target_set(&self) -> Result<TargetSet, RadrootsSdkError> { - if self.endpoint_uri.as_str() != radroots_transport::RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI - { + if self.endpoint_uri.as_str() != radroots_transport::RADROOTS_RETICULUM_ENDPOINT_URI { return Err(radroots_transport::RadrootsTransportError::InvalidTargetUri.into()); } - TargetSet::transport_targets(vec![ - RadrootsTransportTarget::reticulum_preview_with_metadata( - Some(self.scope.transport_scope()), - None, - )?, - ]) + TargetSet::transport_targets(vec![RadrootsTransportTarget::reticulum_with_metadata( + self.endpoint_uri.as_str(), + Some(self.scope.transport_scope()), + None, + )?]) } } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] #[serde(transparent)] -pub struct ReticulumPreviewAgentEndpoint(String); +pub struct ReticulumAgentEndpoint(String); -impl ReticulumPreviewAgentEndpoint { +impl ReticulumAgentEndpoint { pub fn parse(raw: impl AsRef<str>) -> Result<Self, RadrootsSdkError> { let uri = raw.as_ref(); let Some(suffix) = uri.strip_prefix(RETICULUM_AGENT_ENDPOINT_PREFIX) else { return Err(RadrootsSdkError::InvalidRequest { - message: "Reticulum preview agent endpoint is invalid".to_owned(), + message: "Reticulum agent endpoint is invalid".to_owned(), }); }; if uri.is_empty() @@ -487,7 +476,7 @@ impl ReticulumPreviewAgentEndpoint { .any(|ch| ch.is_ascii_control() || ch.is_ascii_whitespace()) { return Err(RadrootsSdkError::InvalidRequest { - message: "Reticulum preview agent endpoint is invalid".to_owned(), + message: "Reticulum agent endpoint is invalid".to_owned(), }); } Ok(Self(uri.to_owned())) @@ -498,21 +487,21 @@ impl ReticulumPreviewAgentEndpoint { } } -impl Default for ReticulumPreviewProfile { +impl Default for ReticulumProfile { fn default() -> Self { - Self::preview_unavailable() + Self::deferred_until_implemented() } } #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)] #[serde(rename_all = "snake_case")] #[non_exhaustive] -pub enum ReticulumPreviewBehavior { +pub enum ReticulumBehavior { RejectDeliveryAttempts, DeferDeliveryPlans, } -impl ReticulumPreviewBehavior { +impl ReticulumBehavior { pub fn as_str(self) -> &'static str { match self { Self::RejectDeliveryAttempts => "reject_delivery_attempts", @@ -522,36 +511,33 @@ impl ReticulumPreviewBehavior { } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] -pub struct HybridProfile { +pub struct MultiTargetProfile { nostr: NostrProfile, - reticulum_preview: ReticulumPreviewProfile, + reticulum: ReticulumProfile, } -impl HybridProfile { - pub fn new(nostr: NostrProfile, reticulum_preview: ReticulumPreviewProfile) -> Self { - Self { - nostr, - reticulum_preview, - } +impl MultiTargetProfile { + pub fn new(nostr: NostrProfile, reticulum: ReticulumProfile) -> Self { + Self { nostr, reticulum } } pub fn nostr(&self) -> &NostrProfile { &self.nostr } - pub fn reticulum_preview(&self) -> &ReticulumPreviewProfile { - &self.reticulum_preview + pub fn reticulum(&self) -> &ReticulumProfile { + &self.reticulum } } #[derive(Clone, Default, PartialEq, Eq)] -pub enum ProxyAuth { +pub enum RadrootsdExecutionAuth { #[default] None, BearerToken(String), } -impl core::fmt::Debug for ProxyAuth { +impl core::fmt::Debug for RadrootsdExecutionAuth { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::None => f.write_str("None"), @@ -560,12 +546,12 @@ impl core::fmt::Debug for ProxyAuth { } } -impl serde::Serialize for ProxyAuth { +impl serde::Serialize for RadrootsdExecutionAuth { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { - let mut state = serializer.serialize_struct("ProxyAuth", 1)?; + let mut state = serializer.serialize_struct("RadrootsdExecutionAuth", 1)?; match self { Self::None => state.serialize_field("kind", "none")?, Self::BearerToken(_) => state.serialize_field("kind", "bearer_token")?, @@ -575,21 +561,21 @@ impl serde::Serialize for ProxyAuth { } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] -pub struct ProxyProfile { +pub struct RadrootsdExecutionProfile { endpoint_url: String, - auth: ProxyAuth, + auth: RadrootsdExecutionAuth, } -impl ProxyProfile { +impl RadrootsdExecutionProfile { pub fn new(endpoint_url: impl Into<String>) -> Self { Self { endpoint_url: endpoint_url.into(), - auth: ProxyAuth::None, + auth: RadrootsdExecutionAuth::None, } } pub fn with_bearer_token(mut self, token: impl Into<String>) -> Self { - self.auth = ProxyAuth::BearerToken(token.into()); + self.auth = RadrootsdExecutionAuth::BearerToken(token.into()); self } @@ -597,15 +583,9 @@ impl ProxyProfile { self.endpoint_url.as_str() } - pub fn auth(&self) -> &ProxyAuth { + pub fn auth(&self) -> &RadrootsdExecutionAuth { &self.auth } - - pub(crate) fn target_set(&self) -> Result<TargetSet, RadrootsSdkError> { - TargetSet::transport_targets(vec![RadrootsTransportTarget::proxy( - self.endpoint_url.as_str(), - )?]) - } } #[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize)] @@ -617,14 +597,11 @@ pub enum TransportProfile { Nostr { profile: NostrProfile, }, - ReticulumPreview { - profile: ReticulumPreviewProfile, + Reticulum { + profile: ReticulumProfile, }, - Hybrid { - profile: HybridProfile, - }, - Proxy { - profile: ProxyProfile, + MultiTarget { + profile: MultiTargetProfile, }, } @@ -637,29 +614,20 @@ impl TransportProfile { Self::Nostr { profile } } - pub fn reticulum_preview(profile: ReticulumPreviewProfile) -> Self { - Self::ReticulumPreview { profile } - } - - pub fn hybrid(profile: HybridProfile) -> Self { - Self::Hybrid { profile } - } - - pub fn proxy(profile: ProxyProfile) -> Self { - Self::Proxy { profile } + pub fn reticulum(profile: ReticulumProfile) -> Self { + Self::Reticulum { profile } } - pub fn supports_delegated_target_resolution(&self) -> bool { - matches!(self, Self::Proxy { .. }) + pub fn multi_target(profile: MultiTargetProfile) -> Self { + Self::MultiTarget { profile } } pub(crate) fn transport_profile_id(&self) -> &'static str { match self { Self::LocalOnly => "local_only", Self::Nostr { .. } => "nostr", - Self::ReticulumPreview { .. } => "reticulum_preview", - Self::Hybrid { .. } => "hybrid", - Self::Proxy { .. } => "proxy", + Self::Reticulum { .. } => "reticulum", + Self::MultiTarget { .. } => "multi_target", } } @@ -667,13 +635,12 @@ impl TransportProfile { match self { Self::LocalOnly => Ok(None), Self::Nostr { profile } => Ok(Some(profile.target_set().clone())), - Self::ReticulumPreview { profile } => Ok(Some(profile.target_set()?)), - Self::Hybrid { profile } => { + Self::Reticulum { profile } => Ok(Some(profile.target_set()?)), + Self::MultiTarget { profile } => { let mut targets = profile.nostr().target_set().targets().to_vec(); - targets.extend(profile.reticulum_preview().target_set()?.into_targets()); + targets.extend(profile.reticulum().target_set()?.into_targets()); Ok(Some(TargetSet::transport_targets(targets)?)) } - Self::Proxy { profile } => Ok(Some(profile.target_set()?)), } } @@ -699,45 +666,27 @@ impl TransportProfile { .with_profile_id(self.transport_profile_id()), ], Self::Nostr { .. } => vec![nostr_transport_status(self.transport_profile_id())], - Self::ReticulumPreview { profile } => { - vec![reticulum_preview_transport_status( + Self::Reticulum { profile } => { + vec![reticulum_transport_status( self.transport_profile_id(), profile.endpoint_uri(), )] } - Self::Hybrid { profile } => vec![ + Self::MultiTarget { profile } => vec![ nostr_transport_status(self.transport_profile_id()), - reticulum_preview_transport_status( + reticulum_transport_status( self.transport_profile_id(), - profile.reticulum_preview().endpoint_uri(), + profile.reticulum().endpoint_uri(), ), ], - Self::Proxy { profile } => { - let auth_configured = matches!(profile.auth(), ProxyAuth::BearerToken(_)); - vec![ - RadrootsTransportStatus::new( - RadrootsTransportKind::Proxy, - auth_configured, - RadrootsTransportImplementationState::Real, - auth_configured, - if auth_configured { - "ready" - } else { - "proxy transport requires bearer token" - }, - ) - .with_profile_id(self.transport_profile_id()) - .with_endpoint_uri(profile.endpoint_url()), - ] - } } } pub(crate) fn configured_nostr_relay_urls(&self) -> Vec<String> { match self { Self::Nostr { profile } => profile.relay_urls(), - Self::Hybrid { profile } => profile.nostr().relay_urls(), - Self::LocalOnly | Self::ReticulumPreview { .. } | Self::Proxy { .. } => Vec::new(), + Self::MultiTarget { profile } => profile.nostr().relay_urls(), + Self::LocalOnly | Self::Reticulum { .. } => Vec::new(), } } } @@ -753,19 +702,18 @@ fn nostr_transport_status(profile_id: &str) -> RadrootsTransportStatus { .with_profile_id(profile_id) } -fn reticulum_preview_transport_status( - profile_id: &str, - endpoint_uri: &str, -) -> RadrootsTransportStatus { +fn reticulum_transport_status(profile_id: &str, endpoint_uri: &str) -> RadrootsTransportStatus { RadrootsTransportStatus::new( RadrootsTransportKind::Reticulum, true, - RadrootsTransportImplementationState::PreviewUnavailable, + RadrootsTransportImplementationState::Real, false, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, ) .with_profile_id(profile_id) .with_endpoint_uri(endpoint_uri) + .with_maturity(RadrootsTransportCapabilityMaturity::Preview) + .with_availability(RadrootsTransportCapabilityAvailability::Unavailable) } impl From<RadrootsTransportDeliveryReceipt> for TransportReceipt { diff --git a/crates/sdk/src/workflow_runtime.rs b/crates/sdk/src/workflow_runtime.rs @@ -1,8 +1,8 @@ #[cfg(feature = "signer-adapters")] use crate::RadrootsSdkSignRequest; use crate::{ - RadrootsClient, RadrootsSdkError, ReticulumPreviewBehavior, SatisfactionPolicy, - SdkIdempotencyKey, TargetPolicy, TargetSet, TransportProfile, runtime::sdk_now_ms, + RadrootsClient, RadrootsSdkError, ReticulumBehavior, SatisfactionPolicy, SdkIdempotencyKey, + TargetPolicy, TargetSet, TransportProfile, runtime::sdk_now_ms, }; use radroots_authority::{RadrootsActorContext, RadrootsEventSigner, sign_authorized_draft}; use radroots_event::{ @@ -13,10 +13,12 @@ use radroots_event_store::{ RadrootsEventIngest, RadrootsTransportObservation, RadrootsTransportObservationType, }; use radroots_outbox::{ - RadrootsOutboxDeliveryPlanInput, RadrootsOutboxEnqueueStatus, - RadrootsOutboxReticulumPreviewBehavior, RadrootsOutboxSignedOperationInput, + RadrootsOutboxDeliveryPlanInput, RadrootsOutboxEnqueueStatus, RadrootsOutboxReticulumBehavior, + RadrootsOutboxSignedOperationInput, +}; +use radroots_transport::{ + RADROOTS_RETICULUM_ENDPOINT_URI, RadrootsTransportKind, RadrootsTransportTarget, }; -use radroots_transport::{RadrootsTransportKind, RadrootsTransportTarget}; use sha2::{Digest, Sha256}; use sqlx::Row; @@ -168,14 +170,9 @@ fn resolved_delivery_plan( match target_policy { TargetPolicy::Explicit(target_policy) => { let targets = target_policy.clone().into_targets(); - let reticulum_preview_behavior = - reticulum_preview_behavior_for_targets(sdk.transport_profile(), &targets); - delivery_plan_from_targets( - "explicit", - targets, - satisfaction_policy, - reticulum_preview_behavior, - ) + let reticulum_behavior = + reticulum_behavior_for_targets(sdk.transport_profile(), &targets); + delivery_plan_from_targets("explicit", targets, satisfaction_policy, reticulum_behavior) } TargetPolicy::DefaultProfile => { let transport_profile = sdk.transport_profile(); @@ -192,7 +189,7 @@ fn resolved_delivery_plan( transport_profile.transport_profile_id(), targets, satisfaction_policy, - outbox_reticulum_preview_behavior(transport_profile), + outbox_reticulum_behavior(transport_profile), ) } TargetPolicy::LocalOnly => { @@ -206,12 +203,13 @@ fn resolved_delivery_plan( "local_only", Vec::new(), satisfaction_policy, - RadrootsOutboxReticulumPreviewBehavior::RejectDeliveryAttempts, + RadrootsOutboxReticulumBehavior::RejectDeliveryAttempts, ) } TargetPolicy::MeshScope(scope) => { let target_set = TargetSet::transport_targets(vec![ - RadrootsTransportTarget::reticulum_preview_with_metadata( + RadrootsTransportTarget::reticulum_with_metadata( + RADROOTS_RETICULUM_ENDPOINT_URI, Some(scope.transport_scope()), None, )?, @@ -220,7 +218,7 @@ fn resolved_delivery_plan( "mesh_scope", target_set.into_targets(), satisfaction_policy, - outbox_reticulum_preview_behavior(sdk.transport_profile()), + outbox_reticulum_behavior(sdk.transport_profile()), ) } } @@ -230,7 +228,7 @@ fn delivery_plan_from_targets( transport_profile_id: impl Into<String>, targets: Vec<RadrootsTransportTarget>, satisfaction_policy: &SatisfactionPolicy, - reticulum_preview_behavior: RadrootsOutboxReticulumPreviewBehavior, + reticulum_behavior: RadrootsOutboxReticulumBehavior, ) -> Result<SdkResolvedDeliveryPlan, RadrootsSdkError> { let delivery_plan = RadrootsOutboxDeliveryPlanInput::new( transport_profile_id, @@ -238,51 +236,45 @@ fn delivery_plan_from_targets( satisfaction_policy.transport_satisfaction_policy()?, targets, ) - .with_reticulum_preview_behavior(reticulum_preview_behavior); + .with_reticulum_behavior(reticulum_behavior); Ok(SdkResolvedDeliveryPlan { delivery_plan }) } -fn reticulum_preview_behavior_for_targets( +fn reticulum_behavior_for_targets( transport_profile: &TransportProfile, targets: &[RadrootsTransportTarget], -) -> RadrootsOutboxReticulumPreviewBehavior { +) -> RadrootsOutboxReticulumBehavior { if targets .iter() .any(|target| target.kind == RadrootsTransportKind::Reticulum) { - outbox_reticulum_preview_behavior(transport_profile) + outbox_reticulum_behavior(transport_profile) } else { - RadrootsOutboxReticulumPreviewBehavior::RejectDeliveryAttempts + RadrootsOutboxReticulumBehavior::RejectDeliveryAttempts } } -fn outbox_reticulum_preview_behavior( +fn outbox_reticulum_behavior( transport_profile: &TransportProfile, -) -> RadrootsOutboxReticulumPreviewBehavior { +) -> RadrootsOutboxReticulumBehavior { match transport_profile { - TransportProfile::ReticulumPreview { profile } => { - reticulum_preview_behavior(profile.behavior()) - } - TransportProfile::Hybrid { profile } => { - reticulum_preview_behavior(profile.reticulum_preview().behavior()) + TransportProfile::Reticulum { profile } => reticulum_behavior(profile.behavior()), + TransportProfile::MultiTarget { profile } => { + reticulum_behavior(profile.reticulum().behavior()) } - TransportProfile::LocalOnly - | TransportProfile::Nostr { .. } - | TransportProfile::Proxy { .. } => { - RadrootsOutboxReticulumPreviewBehavior::RejectDeliveryAttempts + TransportProfile::LocalOnly | TransportProfile::Nostr { .. } => { + RadrootsOutboxReticulumBehavior::RejectDeliveryAttempts } } } -fn reticulum_preview_behavior( - behavior: ReticulumPreviewBehavior, -) -> RadrootsOutboxReticulumPreviewBehavior { +fn reticulum_behavior(behavior: ReticulumBehavior) -> RadrootsOutboxReticulumBehavior { match behavior { - ReticulumPreviewBehavior::RejectDeliveryAttempts => { - RadrootsOutboxReticulumPreviewBehavior::RejectDeliveryAttempts + ReticulumBehavior::RejectDeliveryAttempts => { + RadrootsOutboxReticulumBehavior::RejectDeliveryAttempts } - ReticulumPreviewBehavior::DeferDeliveryPlans => { - RadrootsOutboxReticulumPreviewBehavior::DeferDeliveryPlans + ReticulumBehavior::DeferDeliveryPlans => { + RadrootsOutboxReticulumBehavior::DeferDeliveryPlans } } } diff --git a/crates/sdk/tests/farms_runtime.rs b/crates/sdk/tests/farms_runtime.rs @@ -20,12 +20,12 @@ use radroots_sdk::{ FarmPrivateLocationClearRequest, FarmPrivateLocationInput, FarmPrivateLocationLookupCandidate, FarmPrivateLocationLookupReceipt, FarmPrivateLocationReceipt, FarmPrivateLocationSetRequest, FarmPrivateLocationSetResult, FarmPrivateLocationUpsertRequest, Geocoder, - GeocoderLocalityQuery, HybridProfile, NostrProfile, NostrRelayUrlPolicy, PushOutboxEventState, - PushOutboxRequest, PushOutboxTargetOutcomeKind, RadrootsClient, RadrootsSdkError, - RadrootsSdkErrorClass, RadrootsSdkGeoNamesErrorKind, RadrootsSdkRecoveryAction, - RadrootsSdkTimestamp, ReticulumPreviewProfile, SdkExactLocation, SdkIdempotencyKey, - SdkMutationState, SdkPublicLocality, StorageStatusRequest, TargetPolicy, TargetSet, - TransportProfile, + GeocoderLocalityQuery, MultiTargetProfile, NostrProfile, NostrRelayUrlPolicy, + PushOutboxEventState, PushOutboxRequest, PushOutboxTargetOutcomeKind, RadrootsClient, + RadrootsSdkError, RadrootsSdkErrorClass, RadrootsSdkGeoNamesErrorKind, + RadrootsSdkRecoveryAction, RadrootsSdkTimestamp, ReticulumProfile, SdkExactLocation, + SdkIdempotencyKey, SdkMutationState, SdkPublicLocality, StorageStatusRequest, TargetPolicy, + TargetSet, TransportProfile, }; use radroots_transport_nostr::{RadrootsMockRelayPublishAdapter, RadrootsNostrTransport}; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; @@ -152,14 +152,14 @@ async fn directory_sdk_with_relays(relays: &[&str]) -> (tempfile::TempDir, Radro (tempdir, sdk) } -async fn hybrid_directory_sdk() -> (tempfile::TempDir, RadrootsClient) { +async fn multi_target_directory_sdk() -> (tempfile::TempDir, RadrootsClient) { let tempdir = tempfile::tempdir().expect("tempdir"); let sdk = RadrootsClient::builder() .directory_storage(tempdir.path().join("sdk")) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(TransportProfile::hybrid(HybridProfile::new( + .transport_profile(TransportProfile::multi_target(MultiTargetProfile::new( NostrProfile::new([RELAY], NostrRelayUrlPolicy::Public).expect("Nostr profile"), - ReticulumPreviewProfile::preview_unavailable(), + ReticulumProfile::deferred_until_implemented(), ))) .build() .await @@ -855,14 +855,14 @@ async fn farm_enqueue_publish_pushes_queued_event_with_mock_relay_sync() { } #[tokio::test] -async fn farm_hybrid_profile_publishes_after_nostr_success_and_retains_reticulum_preview() { - let (_tempdir, sdk) = hybrid_directory_sdk().await; +async fn farm_multi_target_profile_publishes_after_nostr_success_and_retains_reticulum() { + let (_tempdir, sdk) = multi_target_directory_sdk().await; let enqueue_receipt = sdk .farms() .enqueue_publish_with_explicit_signer( FarmEnqueuePublishRequest::new( farmer_actor(), - farm(FARM_E_D_TAG, "Hybrid Farm"), + farm(FARM_E_D_TAG, "MultiTarget Farm"), TargetPolicy::default_profile(), ) .try_with_idempotency_key("01890f0e-6c00-7000-8000-00000000022f") @@ -925,8 +925,8 @@ async fn farm_hybrid_profile_publishes_after_nostr_success_and_retains_reticulum && target.status == RadrootsOutboxDeliveryTargetStatus::Accepted })); assert!(targets.iter().any(|target| { - target.endpoint_uri.to_string() == "reticulum:preview-unavailable" - && target.status == RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable + target.endpoint_uri.to_string() == "reticulum:local" + && target.status == RadrootsOutboxDeliveryTargetStatus::DeferredUntilImplemented && target.attempt_count == 0 })); } diff --git a/crates/sdk/tests/listings_runtime.rs b/crates/sdk/tests/listings_runtime.rs @@ -21,11 +21,11 @@ use radroots_outbox::{ RadrootsOutboxEventState, }; use radroots_sdk::{ - HybridProfile, LISTING_PUBLISH_OPERATION_KIND, ListingEnqueuePublishRequest, - ListingPreparePublishRequest, NostrProfile, NostrRelayUrlPolicy, PushOutboxEventState, - PushOutboxRequest, PushOutboxTargetOutcomeKind, RadrootsClient, RadrootsSdkError, - RadrootsSdkRecoveryAction, RadrootsSdkTimestamp, ReticulumPreviewProfile, SdkIdempotencyKey, - SdkMutationState, TargetPolicy, TargetSet, TransportProfile, + LISTING_PUBLISH_OPERATION_KIND, ListingEnqueuePublishRequest, ListingPreparePublishRequest, + MultiTargetProfile, NostrProfile, NostrRelayUrlPolicy, PushOutboxEventState, PushOutboxRequest, + PushOutboxTargetOutcomeKind, RadrootsClient, RadrootsSdkError, RadrootsSdkRecoveryAction, + RadrootsSdkTimestamp, ReticulumProfile, SdkIdempotencyKey, SdkMutationState, TargetPolicy, + TargetSet, TransportProfile, }; use radroots_trade::listing::RadrootsListingEditDocumentV1; use radroots_transport_nostr::{RadrootsMockRelayPublishAdapter, RadrootsNostrTransport}; @@ -187,14 +187,14 @@ async fn directory_sdk_with_relays(relays: &[&str]) -> (tempfile::TempDir, Radro (tempdir, sdk) } -async fn hybrid_directory_sdk() -> (tempfile::TempDir, RadrootsClient) { +async fn multi_target_directory_sdk() -> (tempfile::TempDir, RadrootsClient) { let tempdir = tempfile::tempdir().expect("tempdir"); let sdk = RadrootsClient::builder() .directory_storage(tempdir.path().join("sdk")) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(TransportProfile::hybrid(HybridProfile::new( + .transport_profile(TransportProfile::multi_target(MultiTargetProfile::new( NostrProfile::new([RELAY], NostrRelayUrlPolicy::Public).expect("Nostr profile"), - ReticulumPreviewProfile::preview_unavailable(), + ReticulumProfile::deferred_until_implemented(), ))) .build() .await @@ -926,14 +926,14 @@ async fn enqueue_publish_uses_explicit_idempotency_key_across_equivalent_target_ } #[tokio::test] -async fn listing_hybrid_profile_publishes_after_nostr_success_and_retains_reticulum_preview() { - let (_tempdir, sdk) = hybrid_directory_sdk().await; +async fn listing_multi_target_profile_publishes_after_nostr_success_and_retains_reticulum() { + let (_tempdir, sdk) = multi_target_directory_sdk().await; let enqueue_receipt = sdk .listings() .enqueue_publish_with_explicit_signer( ListingEnqueuePublishRequest::new( actor(), - listing(LISTING_G_D_TAG, "Hybrid Coffee"), + listing(LISTING_G_D_TAG, "MultiTarget Coffee"), TargetPolicy::default_profile(), ) .try_with_idempotency_key("01890f0e-6c00-7000-8000-00000000023c") @@ -996,8 +996,8 @@ async fn listing_hybrid_profile_publishes_after_nostr_success_and_retains_reticu && target.status == RadrootsOutboxDeliveryTargetStatus::Accepted })); assert!(targets.iter().any(|target| { - target.endpoint_uri.to_string() == "reticulum:preview-unavailable" - && target.status == RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable + target.endpoint_uri.to_string() == "reticulum:local" + && target.status == RadrootsOutboxDeliveryTargetStatus::DeferredUntilImplemented && target.attempt_count == 0 })); } diff --git a/crates/sdk/tests/runtime_foundation.rs b/crates/sdk/tests/runtime_foundation.rs @@ -5,7 +5,7 @@ use radroots_sdk::{ BackupRequest, IntegrityRequest, LISTING_PUBLISH_OPERATION_KIND, NostrProfile, NostrRelayUrlPolicy, RadrootsClient, RadrootsSdkClock, RadrootsSdkError, RadrootsSdkErrorClass, RadrootsSdkGeoNamesErrorKind, RadrootsSdkRecoveryAction, RadrootsSdkStorageConfig, - RadrootsSdkTimestamp, RestoreRequest, ReticulumPreviewBehavior, SDK_IDEMPOTENCY_KEY_MAX_LEN, + RadrootsSdkTimestamp, RestoreRequest, ReticulumBehavior, SDK_IDEMPOTENCY_KEY_MAX_LEN, SDK_TRANSPORT_TARGET_MAX_COUNT, SdkBackupState, SdkBackupVerification, SdkEventStoreStorageStatus, SdkIdempotencyKey, SdkOutboxStorageStatus, SdkPrivateStoreStorageStatus, SdkRestoreState, SdkSqliteStoreStatus, @@ -473,23 +473,23 @@ fn sdk_error_contract_methods_cover_all_variants() { vec![RadrootsSdkRecoveryAction::EnableRequiredFeature], ), ( - RadrootsSdkError::ReticulumPreviewTransportUnavailable { + RadrootsSdkError::ReticulumTransportUnavailable { operation: "sync.push_outbox".to_owned(), - endpoint_uri: "reticulum:preview-unavailable".to_owned(), - behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts, + endpoint_uri: "reticulum:local".to_owned(), + behavior: ReticulumBehavior::RejectDeliveryAttempts, }, - "reticulum_preview_transport_unavailable", + "reticulum_transport_unavailable", RadrootsSdkErrorClass::Unsupported, false, vec![RadrootsSdkRecoveryAction::ConfigureTransportTargets], ), ( - RadrootsSdkError::ReticulumPreviewTransportUnavailable { + RadrootsSdkError::ReticulumTransportUnavailable { operation: "sync.push_outbox".to_owned(), - endpoint_uri: "reticulum:preview-unavailable".to_owned(), - behavior: ReticulumPreviewBehavior::DeferDeliveryPlans, + endpoint_uri: "reticulum:local".to_owned(), + behavior: ReticulumBehavior::DeferDeliveryPlans, }, - "reticulum_preview_transport_deferred", + "reticulum_transport_deferred", RadrootsSdkErrorClass::Unsupported, false, vec![RadrootsSdkRecoveryAction::ConfigureTransportTargets], @@ -766,7 +766,6 @@ fn storage_backup_and_integrity_contract_dtos_serialize() { retryable_events: 1, terminal_events: 1, failed_terminal_events: 0, - preview_unavailable_events: 0, deferred_until_implemented_events: 0, ready_signed_events: 1, publishing_events: 0, @@ -821,7 +820,7 @@ fn storage_backup_and_integrity_contract_dtos_serialize() { "retryable_events": 1, "terminal_events": 1, "failed_terminal_events": 0, - "preview_unavailable_events": 0, + "deferred_until_implemented_events": 0, "deferred_until_implemented_events": 0, "ready_signed_events": 1, "publishing_events": 0, diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs @@ -229,19 +229,11 @@ const FORBIDDEN_SDK_ROOT_TRADE_ALIAS_NAMES: &[&str] = &[ ]; const FORBIDDEN_DAEMON_PUBLISH_PROXY_IDENTIFIERS: &[&str] = &[ - "\"radrootsd_proxy\"", - "radrootsd.publish_proxy.v1", - "radroots_publish_proxy_protocol", - "publish_proxy_protocol", + concat!("\"radrootsd", "_", "pro", "xy\""), + concat!("radrootsd.publish", "_", "pro", "xy.v1"), + concat!("radroots_publish", "_", "pro", "xy_protocol"), + concat!("publish", "_", "pro", "xy_protocol"), "publish.relays.resolve", - "\"publish.event\"", - "PublishRelayPolicy", - "PublishDeliveryPolicy", - "PublishEventRequest", - "PublishEventResponse", - "PublishJobView", - "PublishRelayOutcome", - "PublishRelaySource", ]; const REQUIRED_SDK_README_CONCEPTS: &[&str] = &[ @@ -773,14 +765,14 @@ fn contains_sdk_target_set_new(source: &str) -> bool { } #[test] -fn sdk_proxy_required_targets_stay_fingerprint_exact() { +fn sdk_radrootsd_execution_required_targets_stay_fingerprint_exact() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let sync_runtime = read_source(manifest_dir.join("src/sync_runtime.rs").as_path()); for required in [ - "let required_remaining_targets =\n proxy_required_remaining_targets(&plan.satisfaction_policy, &active_targets)?;", + "let required_remaining_targets =\n radrootsd_required_remaining_targets(&plan.satisfaction_policy, &active_targets)?;", "required_remaining_targets.as_deref()", - "fn proxy_required_remaining_targets", + "fn radrootsd_required_remaining_targets", "RadrootsTransportSatisfactionPolicy::RequiredTargets { class, targets }", "target.endpoint_fingerprint == *required", "target.status.counts_as_transport_satisfaction(*class)", @@ -788,7 +780,7 @@ fn sdk_proxy_required_targets_stay_fingerprint_exact() { ] { assert!( sync_runtime.contains(required), - "SDK proxy publish must retain exact required-target witness `{required}`" + "SDK radrootsd publish must retain exact required-target witness `{required}`" ); } @@ -804,7 +796,7 @@ fn sdk_proxy_required_targets_stay_fingerprint_exact() { ] { assert!( !required_targets_arm.contains(forbidden), - "SDK RequiredTargets proxy arm must not lower to count policy `{forbidden}`" + "SDK RequiredTargets radrootsd arm must not lower to count policy `{forbidden}`" ); } } @@ -1662,7 +1654,7 @@ fn sdk_public_api_does_not_export_protocol_workflow_bypass() { } #[test] -fn sdk_proxy_surfaces_reject_removed_daemon_publish_proxy_identifiers() { +fn sdk_radrootsd_execution_surfaces_reject_removed_daemon_publish_identifiers() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); for relative_path in [ @@ -1676,34 +1668,29 @@ fn sdk_proxy_surfaces_reject_removed_daemon_publish_proxy_identifiers() { for forbidden in FORBIDDEN_DAEMON_PUBLISH_PROXY_IDENTIFIERS { assert!( !contains_forbidden_concept(source.as_str(), forbidden), - "{relative_path} must not reintroduce removed daemon publish proxy identifier `{forbidden}`" + "{relative_path} must not reintroduce removed daemon publish identifier `{forbidden}`" ); } } let transport_source = read_source(manifest_dir.join("src/transport.rs").as_path()); assert!( - transport_source.contains("RadrootsTransportKind::Proxy"), - "src/transport.rs must model SDK proxy targets with RadrootsTransportKind::Proxy" + !transport_source.contains(concat!("RadrootsTransportKind::", "Pro", "xy")), + "src/transport.rs must not model radrootsd execution as a transport kind" ); let sync_runtime_source = read_source(manifest_dir.join("src/sync_runtime.rs").as_path()); assert!( - sync_runtime_source.contains("target.transport_kind == RadrootsTransportKind::Proxy"), - "src/sync_runtime.rs must identify proxy delegate targets with RadrootsTransportKind::Proxy" + !sync_runtime_source.contains(concat!("RadrootsTransportKind::", "Pro", "xy")), + "src/sync_runtime.rs must not identify radrootsd execution through a transport kind" ); assert!( - sync_runtime_source - .contains("radrootsd proxy outbox publish explicit targets are Nostr-only"), - "src/sync_runtime.rs must reject non-Nostr proxy outbox explicit targets before behavior is lost" + sync_runtime_source.contains("radrootsd execution explicit targets are Nostr-only"), + "src/sync_runtime.rs must reject non-Nostr radrootsd explicit targets before behavior is lost" ); assert!( sync_runtime_source.contains("active_delivery_plan_id(claimed"), - "src/sync_runtime.rs must derive proxy publish behavior from the claimed active delivery plan" - ); - assert!( - sync_runtime_source.contains("mixed proxy delegate targets"), - "src/sync_runtime.rs must fail closed if proxy delegate targets are mixed in a claimed publish set" + "src/sync_runtime.rs must derive radrootsd publish behavior from the claimed active delivery plan" ); for required in [ "let mut completed_target_ids = std::collections::BTreeSet::new();", @@ -1714,12 +1701,12 @@ fn sdk_proxy_surfaces_reject_removed_daemon_publish_proxy_identifiers() { ] { assert!( sync_runtime_source.contains(required), - "src/sync_runtime.rs must retain proxy completion uniqueness witness `{required}`" + "src/sync_runtime.rs must retain radrootsd completion uniqueness witness `{required}`" ); } assert!( - !sync_runtime_source.contains("TransportPublishPreviewBehavior::RejectDeliveryAttempts"), - "src/sync_runtime.rs must not rewrite Reticulum proxy outbox targets to reject attempts" + !sync_runtime_source.contains("TransportPublishReticulumBehavior::RejectDeliveryAttempts"), + "src/sync_runtime.rs must not rewrite Reticulum radrootsd outbox targets to reject attempts" ); let sync_runtime_unit_source = read_source( manifest_dir @@ -1727,48 +1714,47 @@ fn sdk_proxy_surfaces_reject_removed_daemon_publish_proxy_identifiers() { .as_path(), ); for required in [ - "claimed_uningested_proxy_event", + "claimed_uningested_radrootsd_event", "assert_no_transport_publish_request", "assert!(!stored_before.event_store_ingested)", "assert!(!stored.event_store_ingested)", "with_timeout(Duration::from_millis(50))", - "proxy_delivery_policy_rejects_non_accepted_satisfaction_before_daemon_publish", - "proxy_outbox_target_conversion_preserves_nostr_scope_and_label", - "proxy_completion_matches_duplicate_endpoint_targets_by_scope", - "push_proxy_event_receipt_preserves_daemon_target_metadata", - "proxy_completion_rejects_duplicate_daemon_outcome_before_local_mutation", + "radrootsd_delivery_policy_rejects_non_accepted_satisfaction_before_daemon_publish", + "radrootsd_outbox_target_conversion_preserves_nostr_scope_and_label", + "radrootsd_completion_matches_duplicate_endpoint_targets_by_scope", + "push_radrootsd_event_receipt_preserves_daemon_target_metadata", + "radrootsd_completion_rejects_duplicate_daemon_outcome_before_local_mutation", ] { assert!( sync_runtime_unit_source.contains(required), - "tests/unit/sync_runtime_tests.rs must retain proxy local-validation ordering proof `{required}`" + "tests/unit/sync_runtime_tests.rs must retain radrootsd local-validation ordering proof `{required}`" ); } let adapter_source = read_source(manifest_dir.join("src/adapters/radrootsd.rs").as_path()); assert!( - !adapter_source - .contains("impl RadrootsRelayPublishAdapter for RadrootsdProxyPublishAdapter"), - "src/adapters/radrootsd.rs must not implement relay publish traits for RadrootsdProxyPublishAdapter" + !adapter_source.contains("impl RadrootsRelayPublishAdapter for RadrootsdPublishAdapter"), + "src/adapters/radrootsd.rs must not implement relay publish traits for RadrootsdPublishAdapter" ); assert!( - !adapter_source.contains("proxy_relay_receipt_from_response"), + !adapter_source.contains("radrootsd_relay_receipt_from_response"), "src/adapters/radrootsd.rs must not convert typed transport publish jobs into relay receipts" ); assert!( - !adapter_source.contains("TransportPublishPreviewBehavior::RejectDeliveryAttempts"), + !adapter_source.contains("TransportPublishReticulumBehavior::RejectDeliveryAttempts"), "src/adapters/radrootsd.rs must not rewrite Reticulum relay targets to reject attempts" ); for required in [ "TransportPublishOutcomeKind::DeferredUntilImplemented", "mark_delivery_target_deferred_until_implemented", - "TransportPublishOutcomeKind::PreviewUnavailable", - "mark_delivery_target_preview_unavailable", + "TransportPublishOutcomeKind::DeferredUntilImplemented", + "mark_delivery_target_deferred_until_implemented", "PushOutboxEventState::DeferredUntilImplemented", - "PushOutboxEventState::PreviewUnavailable", + "PushOutboxEventState::DeferredUntilImplemented", + "PushOutboxTargetOutcomeKind::DeferredUntilImplemented", "PushOutboxTargetOutcomeKind::DeferredUntilImplemented", - "PushOutboxTargetOutcomeKind::PreviewUnavailable", - "reject_non_accepted_proxy_satisfaction", + "reject_non_accepted_radrootsd_satisfaction", "class != RadrootsTransportSatisfactionClass::Accepted", "target.target_scope.as_ref()", "outcome.target_scope.as_deref()", @@ -1777,35 +1763,35 @@ fn sdk_proxy_surfaces_reject_removed_daemon_publish_proxy_identifiers() { ] { assert!( sync_runtime_source.contains(required), - "src/sync_runtime.rs must preserve proxy preview/deferred outcome witness `{required}`" + "src/sync_runtime.rs must preserve radrootsd deferred outcome witness `{required}`" ); } let receipt_source = source_between( sync_runtime_source.as_str(), - "fn push_proxy_event_receipt", - "fn push_proxy_target_receipt", + "fn push_radrootsd_event_receipt", + "fn push_radrootsd_target_receipt", ); assert!( receipt_source.contains("push_receipt_event_id("), - "push_proxy_event_receipt must convert daemon event ids through the typed receipt helper" + "push_radrootsd_event_receipt must convert daemon event ids through the typed receipt helper" ); for forbidden in [".expect(", ".unwrap(", "panic!("] { assert!( !receipt_source.contains(forbidden), - "push_proxy_event_receipt must not use production panic path `{forbidden}`" + "push_radrootsd_event_receipt must not use production panic path `{forbidden}`" ); } - let proxy_target_receipt_source = source_between( + let radrootsd_target_receipt_source = source_between( sync_runtime_source.as_str(), - "fn push_proxy_target_receipt", - "fn push_proxy_target_outcome_kind", + "fn push_radrootsd_target_receipt", + "fn push_radrootsd_target_outcome_kind", ); for forbidden in ["target_scope: None", "target_label: None"] { assert!( - !proxy_target_receipt_source.contains(forbidden), - "push_proxy_target_receipt must not hard-code daemon metadata field `{forbidden}`" + !radrootsd_target_receipt_source.contains(forbidden), + "push_radrootsd_target_receipt must not hard-code daemon metadata field `{forbidden}`" ); } } @@ -1898,7 +1884,6 @@ fn sdk_public_outcome_label_contracts_are_explicit() { r#"Self::TargetUriRejected => "target_uri_rejected""#, r#"Self::SkippedAlreadyAccepted => "skipped_already_accepted""#, r#"Self::DeferredUntilImplemented => "deferred_until_implemented""#, - r#"Self::PreviewUnavailable => "preview_unavailable""#, r#"Self::Unknown => "unknown""#, ][..], ), @@ -2009,7 +1994,7 @@ fn sdk_public_outcome_label_contracts_are_explicit() { } #[test] -fn sdk_transport_policy_sources_reject_configured_profile_and_proxy_relay_bridge() { +fn sdk_transport_policy_sources_reject_configured_profile_and_radrootsd_relay_bridge() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); for relative_path in [ "src", @@ -2035,12 +2020,12 @@ fn sdk_transport_policy_sources_reject_configured_profile_and_proxy_relay_bridge "configured_profile()", "UseTransportProfile", "use_transport_profile", - "impl RadrootsRelayPublishAdapter for RadrootsdProxyPublishAdapter", - "proxy_relay_receipt_from_response", + "impl RadrootsRelayPublishAdapter for RadrootsdPublishAdapter", + "radrootsd_relay_receipt_from_response", ] { assert!( !source.contains(forbidden), - "{} must not reintroduce removed transport policy or proxy relay bridge surface `{forbidden}`", + "{} must not reintroduce removed transport policy or relay bridge surface `{forbidden}`", path.display() ); } @@ -2249,7 +2234,7 @@ fn sdk_workflow_runtime_records_local_import_observations() { } #[test] -fn sdk_transport_sources_keep_reticulum_preview_push_boundary() { +fn sdk_transport_sources_keep_reticulum_push_boundary() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); for relative_path in [ @@ -2259,41 +2244,39 @@ fn sdk_transport_sources_keep_reticulum_preview_push_boundary() { "src/adapters/radrootsd.rs", ] { let source = read_source(manifest_dir.join(relative_path).as_path()); - if let Some(line) = removed_reticulum_preview_endpoint_lines(source.as_str()) + if let Some(line) = removed_reticulum_endpoint_lines(source.as_str()) .into_iter() .next() { - panic!( - "{relative_path}:{line} contains removed Reticulum preview endpoint `reticulum:preview`" - ); + panic!("{relative_path}:{line} contains removed Reticulum endpoint"); } } let transport_source = read_source(manifest_dir.join("src/transport.rs").as_path()); assert!( - transport_source.contains("RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI"), - "src/transport.rs must consume the shared Reticulum preview endpoint constant" + transport_source.contains("RADROOTS_RETICULUM_ENDPOINT_URI"), + "src/transport.rs must consume the shared Reticulum endpoint constant" ); assert!( !transport_source.contains("const RETICULUM_PREVIEW_ENDPOINT_URI"), - "src/transport.rs must not duplicate the Reticulum preview endpoint constant" + "src/transport.rs must not duplicate the Reticulum endpoint constant" ); assert!( - !transport_source.contains("reticulum:preview-unavailable"), - "src/transport.rs must not duplicate the Reticulum preview endpoint literal" + !transport_source.contains("reticulum:local"), + "src/transport.rs must not duplicate the Reticulum endpoint literal" ); let sync_runtime = read_source(manifest_dir.join("src/sync_runtime.rs").as_path()); for required in [ - "TransportProfile::ReticulumPreview { .. }", - "reticulum_preview_push_receipt", - "reticulum_preview_event_receipt", + "TransportProfile::Reticulum { .. }", + "reticulum_push_receipt", + "reticulum_event_receipt", "push_reported_event", "RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE", - "ReticulumPreviewTryNowRequest", - "try_reticulum_preview_now", - "\"sync.try_reticulum_preview_now\"", - "RadrootsSdkError::ReticulumPreviewTransportUnavailable", + "ReticulumTryNowRequest", + "try_reticulum_now", + "\"sync.try_reticulum_now\"", + "RadrootsSdkError::ReticulumTransportUnavailable", "pub target_scope: Option<String>,", "pub target_label: Option<String>,", "pub transport_outcome_kind: Option<PushOutboxTransportOutcomeKind>,", @@ -2301,33 +2284,33 @@ fn sdk_transport_sources_keep_reticulum_preview_push_boundary() { ] { assert!( sync_runtime.contains(required), - "src/sync_runtime.rs must retain Reticulum preview push boundary `{required}`" + "src/sync_runtime.rs must retain Reticulum push boundary `{required}`" ); } assert!( - !sync_runtime.contains("push_outbox_has_no_reticulum_preview_work"), - "src/sync_runtime.rs must not revive the Reticulum preview ready-work error probe" + !sync_runtime.contains("push_outbox_has_no_reticulum_work"), + "src/sync_runtime.rs must not revive the Reticulum ready-work error probe" ); assert!( - !sync_runtime.contains("RadrootsSdkError::reticulum_preview_transport_unavailable(\n"), - "src/sync_runtime.rs must not return Reticulum preview unavailable errors from push_outbox" + !sync_runtime.contains("RadrootsSdkError::reticulum_transport_unavailable(\n"), + "src/sync_runtime.rs must not return Reticulum deferred until implemented errors from push_outbox" ); let error_source = read_source(manifest_dir.join("src/error.rs").as_path()); for required in [ - "reticulum_preview_transport_unavailable", - "reticulum_preview_transport_deferred", - "Reticulum preview endpoint", + "reticulum_transport_unavailable", + "reticulum_transport_deferred", + "Reticulum endpoint", ] { assert!( error_source.contains(required), - "src/error.rs must retain Reticulum preview error witness `{required}`" + "src/error.rs must retain Reticulum error witness `{required}`" ); } } #[test] -fn sdk_feature_matrix_keeps_reticulum_preview_runtime_owned_without_alias() { +fn sdk_feature_matrix_keeps_reticulum_runtime_owned_without_alias() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let manifest_source = read_source(manifest_dir.join("Cargo.toml").as_path()); let features_source = source_between(manifest_source.as_str(), "[features]", "[dependencies]"); @@ -2345,7 +2328,7 @@ fn sdk_feature_matrix_keeps_reticulum_preview_runtime_owned_without_alias() { ] { assert!( runtime_source.contains(required), - "SDK runtime feature must retain Reticulum preview matrix witness `{required}`" + "SDK runtime feature must retain Reticulum matrix witness `{required}`" ); } for required in [ @@ -2361,7 +2344,7 @@ fn sdk_feature_matrix_keeps_reticulum_preview_runtime_owned_without_alias() { } for forbidden in [ - "transport-reticulum-preview", + concat!("transport-reticulum-", "pre", "view"), "radroots_transport_reticulum/client", "reticulum-runtime", "dep:rns", @@ -2373,7 +2356,7 @@ fn sdk_feature_matrix_keeps_reticulum_preview_runtime_owned_without_alias() { ] { assert!( !manifest_source.contains(forbidden), - "SDK feature matrix must not introduce Reticulum preview alias or real runtime dependency `{forbidden}`" + "SDK feature matrix must not introduce Reticulum alias or real runtime dependency `{forbidden}`" ); } @@ -2399,13 +2382,13 @@ fn sdk_feature_matrix_keeps_reticulum_preview_runtime_owned_without_alias() { let sync_runtime_source = read_source(manifest_dir.join("src/sync_runtime.rs").as_path()); for required in [ - "#[cfg(not(feature = \"radrootsd-proxy\"))]", - "TransportProfile::Proxy { .. } => Err(RadrootsSdkError::ProductSyncUnsupported", - "required_feature: \"radrootsd-proxy\"", + "#[cfg(not(feature = \"radrootsd-execution\"))]", + "self.sdk.radrootsd_execution_profile().is_some()", + "required_feature: \"radrootsd-execution\"", ] { assert!( sync_runtime_source.contains(required), - "SDK runtime-only push_outbox must retain proxy feature gate witness `{required}`" + "SDK runtime-only push_outbox must retain radrootsd execution feature gate witness `{required}`" ); } } @@ -2555,11 +2538,12 @@ fn is_doc_surface(path: &Path) -> bool { ) } -fn removed_reticulum_preview_endpoint_lines(source: &str) -> Vec<usize> { +fn removed_reticulum_endpoint_lines(source: &str) -> Vec<usize> { + let removed_endpoint = ["reticulum:", "pre", "view"].concat(); source - .match_indices("reticulum:preview") + .match_indices(removed_endpoint.as_str()) .filter_map(|(index, _)| { - let after = source[index + "reticulum:preview".len()..].chars().next(); + let after = source[index + removed_endpoint.len()..].chars().next(); (after != Some('-')).then(|| line_number(source, index)) }) .collect() diff --git a/crates/sdk/tests/sync_runtime.rs b/crates/sdk/tests/sync_runtime.rs @@ -20,17 +20,17 @@ use radroots_outbox::{ RadrootsOutbox, RadrootsOutboxDeliveryTargetStatus, RadrootsOutboxEventState, RadrootsOutboxOperationInput, RadrootsOutboxSignedOperationInput, }; -#[cfg(feature = "radrootsd-proxy")] -use radroots_sdk::ProxyProfile; +#[cfg(feature = "radrootsd-execution")] +use radroots_sdk::RadrootsdExecutionProfile; use radroots_sdk::{ - BackupRequest, HybridProfile, IntegrityRequest, LISTING_PUBLISH_OPERATION_KIND, - ListingEnqueuePublishRequest, ListingPreparePublishRequest, NostrProfile, NostrRelayUrlPolicy, + BackupRequest, IntegrityRequest, LISTING_PUBLISH_OPERATION_KIND, ListingEnqueuePublishRequest, + ListingPreparePublishRequest, MultiTargetProfile, NostrProfile, NostrRelayUrlPolicy, PUSH_OUTBOX_DEFAULT_CLAIM_TTL_MS, PUSH_OUTBOX_DEFAULT_LIMIT, PUSH_OUTBOX_DEFAULT_NEXT_ATTEMPT_DELAY_MS, PUSH_OUTBOX_MAX_LIMIT, PushOutboxEventReceipt, PushOutboxEventState, PushOutboxReceipt, PushOutboxRequest, PushOutboxTargetOutcomeKind, PushOutboxTargetReceipt, PushOutboxTransportOutcomeKind, RadrootsClient, RadrootsSdkError, - RadrootsSdkTimestamp, RestoreRequest, ReticulumPreviewBehavior, ReticulumPreviewProfile, - ReticulumPreviewTryNowRequest, SdkBackupManifestKind, SdkRelayAuthPolicy, SdkRestoreState, + RadrootsSdkTimestamp, RestoreRequest, ReticulumBehavior, ReticulumProfile, + ReticulumTryNowRequest, SdkBackupManifestKind, SdkRelayAuthPolicy, SdkRestoreState, StorageStatusRequest, SyncStatusRequest, SyncStatusSource, TargetPolicy, TransportProfile, }; use radroots_transport::{ @@ -42,13 +42,13 @@ use radroots_transport_nostr::{ RadrootsRelayPublishAdapter, RadrootsRelayPublishRelayReceipt, RadrootsRelayPublishRequest, RadrootsRelayTransportError, }; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use std::io::{Read, Write}; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use std::net::{TcpListener, TcpStream}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use std::thread::JoinHandle; use std::time::Duration; @@ -65,6 +65,8 @@ const LOCAL_RELAY_B: &str = "ws://127.0.0.1:8081"; const LOCAL_RELAY_C: &str = "ws://[::1]:8082"; const NONLOCAL_WS_RELAY: &str = "ws://relay.example.com"; const PRIVATE_LAN_WS_RELAY: &str = "ws://192.168.1.10:8080"; +#[cfg(feature = "radrootsd-execution")] +const RADROOTSD_EXECUTION_TEST_RELAY: &str = "wss://daemon-resolved.example.com"; #[derive(Clone)] struct FixtureSigner { @@ -73,12 +75,23 @@ struct FixtureSigner { struct TransportFailurePublishAdapter; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] struct RecordedTransportPublishRequest { body: String, } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] +fn radrootsd_execution_transport_profile() -> TransportProfile { + TransportProfile::nostr( + NostrProfile::new( + [RADROOTSD_EXECUTION_TEST_RELAY], + NostrRelayUrlPolicy::Public, + ) + .expect("radrootsd execution Nostr profile"), + ) +} + +#[cfg(feature = "radrootsd-execution")] #[derive(Clone, Copy)] enum TransportPublishResponseMode { Accepted, @@ -95,7 +108,7 @@ struct RecordingPublishAdapter { relay_batches: Arc<Mutex<Vec<Vec<String>>>>, } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] fn spawn_transport_publish_server() -> (String, JoinHandle<RecordedTransportPublishRequest>) { let listener = TcpListener::bind("127.0.0.1:0").expect("bind transport publish server"); let endpoint = format!("http://{}/rpc", listener.local_addr().expect("addr")); @@ -113,7 +126,7 @@ fn spawn_transport_publish_server() -> (String, JoinHandle<RecordedTransportPubl (endpoint, handle) } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] fn spawn_transport_publish_sequence_server( responses: Vec<TransportPublishResponseMode>, ) -> (String, JoinHandle<Vec<RecordedTransportPublishRequest>>) { @@ -134,7 +147,7 @@ fn spawn_transport_publish_sequence_server( (endpoint, handle) } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] fn read_transport_publish_request_body(stream: &mut TcpStream) -> String { let mut request = Vec::new(); let mut buffer = [0u8; 1024]; @@ -174,7 +187,7 @@ fn read_transport_publish_request_body(stream: &mut TcpStream) -> String { body.to_owned() } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] fn write_transport_publish_response( stream: &mut TcpStream, body: &str, @@ -490,30 +503,30 @@ async fn directory_sdk(relays: &[&str]) -> (tempfile::TempDir, RadrootsClient) { (tempdir, sdk) } -async fn reticulum_preview_directory_sdk( - behavior: ReticulumPreviewBehavior, +async fn reticulum_directory_sdk( + behavior: ReticulumBehavior, ) -> (tempfile::TempDir, RadrootsClient) { let tempdir = tempfile::tempdir().expect("tempdir"); - let profile = ReticulumPreviewProfile::preview_unavailable().with_behavior(behavior); + let profile = ReticulumProfile::deferred_until_implemented().with_behavior(behavior); let sdk = RadrootsClient::builder() .directory_storage(tempdir.path().join("sdk")) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(TransportProfile::reticulum_preview(profile)) + .transport_profile(TransportProfile::reticulum(profile)) .build() .await .expect("sdk"); (tempdir, sdk) } -async fn hybrid_directory_sdk(relays: &[&str]) -> (tempfile::TempDir, RadrootsClient) { +async fn multi_target_directory_sdk(relays: &[&str]) -> (tempfile::TempDir, RadrootsClient) { let tempdir = tempfile::tempdir().expect("tempdir"); let sdk = RadrootsClient::builder() .directory_storage(tempdir.path().join("sdk")) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(TransportProfile::hybrid(HybridProfile::new( + .transport_profile(TransportProfile::multi_target(MultiTargetProfile::new( NostrProfile::new(relays.iter().copied(), NostrRelayUrlPolicy::Public) .expect("Nostr profile"), - ReticulumPreviewProfile::preview_unavailable(), + ReticulumProfile::deferred_until_implemented(), ))) .build() .await @@ -763,7 +776,7 @@ async fn sync_status_empty_store_reports_canonical_sources_and_transport_targets "retryable_events": 0, "terminal_events": 0, "failed_terminal_events": 0, - "preview_unavailable_events": 0, + "deferred_until_implemented_events": 0, "deferred_until_implemented_events": 0, "ready_signed_events": 0, "publishing_events": 0, @@ -795,6 +808,8 @@ async fn sync_status_empty_store_reports_canonical_sources_and_transport_targets "endpoint_uri": null, "configured": true, "implementation": "real", + "maturity": "stable", + "availability": "available", "usable_for_delivery": true, "capabilities": { "deliver": true, @@ -808,8 +823,8 @@ async fn sync_status_empty_store_reports_canonical_sources_and_transport_targets } #[tokio::test] -async fn sync_status_reports_hybrid_transport_targets_and_statuses() { - let (_tempdir, sdk) = hybrid_directory_sdk(&[RELAY_A, RELAY_B]).await; +async fn sync_status_reports_multi_target_transport_targets_and_statuses() { + let (_tempdir, sdk) = multi_target_directory_sdk(&[RELAY_A, RELAY_B]).await; let receipt = sdk .sync() @@ -817,7 +832,10 @@ async fn sync_status_reports_hybrid_transport_targets_and_statuses() { .await .expect("status"); - assert_eq!(receipt.transport_profile.transport_profile_id, "hybrid"); + assert_eq!( + receipt.transport_profile.transport_profile_id, + "multi_target" + ); assert_eq!( receipt.transport_profile.configured_transport_target_count, 3 @@ -839,12 +857,7 @@ async fn sync_status_reports_hybrid_transport_targets_and_statuses() { vec![ ("nostr", RELAY_A, None, None), ("nostr", RELAY_B, None, None), - ( - "reticulum", - "reticulum:preview-unavailable", - Some("local_preview"), - None - ) + ("reticulum", "reticulum:local", Some("local"), None) ] ); assert_eq!( @@ -865,14 +878,7 @@ async fn sync_status_reports_hybrid_transport_targets_and_statuses() { .collect::<Vec<_>>(), vec![ ("nostr", "real", true, true, true, false), - ( - "reticulum", - "preview_unavailable", - true, - false, - false, - false - ) + ("reticulum", "real", true, false, false, false) ] ); } @@ -1735,15 +1741,16 @@ async fn push_outbox_empty_queue_returns_zero_counts() { assert!(adapter.captured_raw_events().is_empty()); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved_relays() { +async fn product_push_outbox_uses_radrootsd_execution_transport_with_daemon_resolved_relays() { let (endpoint, handle) = spawn_transport_publish_server(); let tempdir = tempfile::tempdir().expect("tempdir"); let sdk = RadrootsClient::builder() .directory_storage(tempdir.path().join("sdk")) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(TransportProfile::proxy(ProxyProfile::new(endpoint))) + .transport_profile(radrootsd_execution_transport_profile()) + .radrootsd_execution_profile(RadrootsdExecutionProfile::new(endpoint)) .build() .await .expect("sdk"); @@ -1753,7 +1760,7 @@ async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved .enqueue_publish_with_explicit_signer( ListingEnqueuePublishRequest::new( actor(), - listing(LISTING_A_D_TAG, "Proxy Coffee"), + listing(LISTING_A_D_TAG, "Radrootsd Coffee"), TargetPolicy::default_profile(), ) .try_with_idempotency_key("01890f0e-6c00-7000-8000-000000000250") @@ -1779,7 +1786,7 @@ async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved assert_eq!(pre_push_targets.len(), 1); assert_eq!( pre_push_targets[0].transport_kind, - radroots_sdk::RadrootsTransportKind::Proxy + radroots_sdk::RadrootsTransportKind::Nostr ); assert_eq!( pre_push_targets[0].status, @@ -1818,14 +1825,10 @@ async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved let recorded = handle.join().expect("transport publish request"); let body: serde_json::Value = serde_json::from_str(recorded.body.as_str()).expect("body"); assert_eq!(body["method"], "transport.publish.event"); - assert_eq!(body["params"]["target_policy"]["kind"], "nostr"); + assert_eq!(body["params"]["target_policy"]["kind"], "explicit_targets"); assert_eq!( - body["params"]["target_policy"]["relay_urls"], - serde_json::json!([]) - ); - assert_eq!( - body["params"]["target_policy"]["source_policy"], - "request_then_author_write_then_daemon_default" + body["params"]["target_policy"]["targets"][0]["endpoint_uri"], + RADROOTSD_EXECUTION_TEST_RELAY ); assert_eq!(body["params"]["delivery_policy"]["mode"], "all"); let raw_event_json = body["params"]["raw_event_json"] @@ -1845,9 +1848,9 @@ async fn product_push_outbox_uses_radrootsd_proxy_transport_with_daemon_resolved assert_eq!(status.outbox.ready_signed_events, 0); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn product_push_outbox_radrootsd_proxy_recovers_expired_publishing_claim_before_selecting_work() +async fn product_push_outbox_radrootsd_execution_recovers_expired_publishing_claim_before_selecting_work() { let (endpoint, handle) = spawn_transport_publish_server(); let tempdir = tempfile::tempdir().expect("tempdir"); @@ -1855,7 +1858,8 @@ async fn product_push_outbox_radrootsd_proxy_recovers_expired_publishing_claim_b let sdk = RadrootsClient::builder() .directory_storage(storage.clone()) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(TransportProfile::proxy(ProxyProfile::new(endpoint.clone()))) + .transport_profile(radrootsd_execution_transport_profile()) + .radrootsd_execution_profile(RadrootsdExecutionProfile::new(endpoint.clone())) .build() .await .expect("sdk"); @@ -1864,7 +1868,7 @@ async fn product_push_outbox_radrootsd_proxy_recovers_expired_publishing_claim_b .enqueue_publish_with_explicit_signer( ListingEnqueuePublishRequest::new( actor(), - listing(LISTING_A_D_TAG, "Recovered Proxy Coffee"), + listing(LISTING_A_D_TAG, "Recovered Radrootsd Coffee"), TargetPolicy::default_profile(), ) .try_with_idempotency_key("01890f0e-6c00-7000-8000-000000000251") @@ -1879,14 +1883,14 @@ async fn product_push_outbox_radrootsd_proxy_recovers_expired_publishing_claim_b let stale_claim = outbox .claim_ready_signed_event( enqueue.outbox_event_id, - "stalled-proxy-publisher", - "expired-proxy-publish", + "stalled-radrootsd-publisher", + "expired-radrootsd-publish", 1_700_000_000_500, 1_700_000_000_000, ) .await - .expect("stale proxy claim") - .expect("stale proxy claim"); + .expect("stale radrootsd claim") + .expect("stale radrootsd claim"); let active_delivery_plan_id = stale_claim .active_delivery_plan_id .expect("active delivery plan id"); @@ -1898,13 +1902,14 @@ async fn product_push_outbox_radrootsd_proxy_recovers_expired_publishing_claim_b assert_eq!(stored_before.state, RadrootsOutboxEventState::Publishing); assert_eq!( stored_before.claim_token.as_deref(), - Some("expired-proxy-publish") + Some("expired-radrootsd-publish") ); drop(sdk); let sdk = RadrootsClient::builder() .directory_storage(storage) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_001)) - .transport_profile(TransportProfile::proxy(ProxyProfile::new(endpoint))) + .transport_profile(radrootsd_execution_transport_profile()) + .radrootsd_execution_profile(RadrootsdExecutionProfile::new(endpoint)) .build() .await .expect("reopened sdk"); @@ -1940,24 +1945,25 @@ async fn product_push_outbox_radrootsd_proxy_recovers_expired_publishing_claim_b .starts_with(format!("radroots-sdk-outbox-{}-2-", enqueue.outbox_event_id).as_str()) ); assert!(idempotency_key.ends_with(format!("-{active_delivery_plan_id}").as_str())); - assert_eq!(body["params"]["target_policy"]["kind"], "nostr"); + assert_eq!(body["params"]["target_policy"]["kind"], "explicit_targets"); assert_eq!(body["params"]["delivery_policy"]["mode"], "all"); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn product_push_outbox_radrootsd_proxy_idempotency_is_attempt_scoped() { +async fn product_push_outbox_radrootsd_execution_idempotency_is_attempt_scoped() { let (endpoint, handle) = spawn_transport_publish_sequence_server(vec![ TransportPublishResponseMode::Retryable, TransportPublishResponseMode::Accepted, ]); let tempdir = tempfile::tempdir().expect("tempdir"); let storage = tempdir.path().join("sdk"); - let transport = TransportProfile::proxy(ProxyProfile::new(endpoint)); + let radrootsd_execution_profile = RadrootsdExecutionProfile::new(endpoint); let sdk = RadrootsClient::builder() .directory_storage(storage.clone()) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(transport.clone()) + .transport_profile(radrootsd_execution_transport_profile()) + .radrootsd_execution_profile(radrootsd_execution_profile.clone()) .build() .await .expect("sdk"); @@ -2008,7 +2014,8 @@ async fn product_push_outbox_radrootsd_proxy_idempotency_is_attempt_scoped() { let sdk = RadrootsClient::builder() .directory_storage(storage) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_001)) - .transport_profile(transport) + .transport_profile(radrootsd_execution_transport_profile()) + .radrootsd_execution_profile(radrootsd_execution_profile) .build() .await .expect("reopened sdk"); @@ -2055,17 +2062,18 @@ async fn product_push_outbox_radrootsd_proxy_idempotency_is_attempt_scoped() { assert!(second_key.ends_with(format!("-{active_plan_id}").as_str())); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_outbox() { - let closed_listener = TcpListener::bind("127.0.0.1:0").expect("bind closed proxy"); +async fn product_push_outbox_radrootsd_execution_error_and_terminal_paths_update_outbox() { + let closed_listener = TcpListener::bind("127.0.0.1:0").expect("bind closed radrootsd"); let closed_endpoint = format!("http://{}/rpc", closed_listener.local_addr().expect("addr")); drop(closed_listener); let tempdir = tempfile::tempdir().expect("tempdir"); let retryable_sdk = RadrootsClient::builder() .directory_storage(tempdir.path().join("retryable-sdk")) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(TransportProfile::proxy(ProxyProfile::new(closed_endpoint))) + .transport_profile(radrootsd_execution_transport_profile()) + .radrootsd_execution_profile(RadrootsdExecutionProfile::new(closed_endpoint)) .build() .await .expect("retryable sdk"); @@ -2074,7 +2082,7 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out .enqueue_publish_with_explicit_signer( ListingEnqueuePublishRequest::new( actor(), - listing(LISTING_A_D_TAG, "Proxy Error Coffee"), + listing(LISTING_A_D_TAG, "Radrootsd Error Coffee"), TargetPolicy::default_profile(), ) .try_with_idempotency_key("01890f0e-6c00-7000-8000-000000000253") @@ -2108,7 +2116,7 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out retryable.events[0].targets[0] .message .as_deref() - .is_some_and(|error| error.contains("radrootsd proxy publish failed")) + .is_some_and(|error| error.contains("radrootsd publish failed")) ); let retryable_status = retryable_sdk .sync() @@ -2121,7 +2129,7 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out .outbox .last_error .as_deref() - .is_some_and(|error| error.contains("radrootsd proxy publish failed")) + .is_some_and(|error| error.contains("radrootsd publish failed")) ); let (terminal_endpoint, terminal_handle) = @@ -2129,9 +2137,8 @@ async fn product_push_outbox_radrootsd_proxy_error_and_terminal_paths_update_out let terminal_sdk = RadrootsClient::builder() .directory_storage(tempdir.path().join("terminal-sdk")) .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) - .transport_profile(TransportProfile::proxy(ProxyProfile::new( - terminal_endpoint, - ))) + .transport_profile(radrootsd_execution_transport_profile()) + .radrootsd_execution_profile(RadrootsdExecutionProfile::new(terminal_endpoint)) .build() .await .expect("terminal sdk"); @@ -2292,17 +2299,16 @@ async fn product_push_outbox_empty_queue_does_not_require_builder_relays() { } #[tokio::test] -async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempts_with_preview_work() - { +async fn sync_runtime_product_push_outbox_reticulum_reports_zero_attempts_with_reticulum_work() { let cases = [ ( - ReticulumPreviewBehavior::RejectDeliveryAttempts, - PushOutboxEventState::PreviewUnavailable, - PushOutboxTargetOutcomeKind::PreviewUnavailable, - PushOutboxTransportOutcomeKind::TransportUnavailable, + ReticulumBehavior::RejectDeliveryAttempts, + PushOutboxEventState::DeferredUntilImplemented, + PushOutboxTargetOutcomeKind::DeferredUntilImplemented, + PushOutboxTransportOutcomeKind::DeferredUntilImplemented, ), ( - ReticulumPreviewBehavior::DeferDeliveryPlans, + ReticulumBehavior::DeferDeliveryPlans, PushOutboxEventState::DeferredUntilImplemented, PushOutboxTargetOutcomeKind::DeferredUntilImplemented, PushOutboxTransportOutcomeKind::DeferredUntilImplemented, @@ -2310,12 +2316,12 @@ async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempt ]; for (behavior, expected_state, expected_outcome, expected_transport_outcome) in cases { - let (_tempdir, sdk) = reticulum_preview_directory_sdk(behavior).await; + let (_tempdir, sdk) = reticulum_directory_sdk(behavior).await; let empty = sdk .sync() .push_outbox(PushOutboxRequest::new()) .await - .expect("empty Reticulum preview push"); + .expect("empty Reticulum push"); assert_eq!(empty.attempted_events, 0); assert!(empty.events.is_empty()); @@ -2324,7 +2330,7 @@ async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempt .enqueue_publish_with_explicit_signer( ListingEnqueuePublishRequest::new( actor(), - listing(LISTING_A_D_TAG, "Reticulum Preview Coffee"), + listing(LISTING_A_D_TAG, "Reticulum Coffee"), TargetPolicy::default_profile(), ) .try_with_idempotency_key("01890f0e-6c00-7000-8000-000000000255") @@ -2338,7 +2344,7 @@ async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempt .sync() .push_outbox(PushOutboxRequest::new().with_limit(1)) .await - .expect("Reticulum preview push receipt"); + .expect("Reticulum push receipt"); assert_eq!(receipt.attempted_events, 0); assert_eq!(receipt.published_events, 0); assert_eq!(receipt.retryable_events, 0); @@ -2356,8 +2362,8 @@ async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempt assert_eq!(event.targets.len(), 1); let target = &event.targets[0]; assert_eq!(target.transport_kind, "reticulum"); - assert_eq!(target.endpoint_uri, "reticulum:preview-unavailable"); - assert_eq!(target.target_scope.as_deref(), Some("local_preview")); + assert_eq!(target.endpoint_uri, "reticulum:local"); + assert_eq!(target.target_scope.as_deref(), Some("local")); assert_eq!(target.target_label.as_deref(), None); assert_eq!(target.outcome_kind, expected_outcome); assert_eq!( @@ -2377,26 +2383,12 @@ async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempt .expect("status"); assert_eq!(status.outbox.ready_signed_events, 0); assert_eq!(status.outbox.pending_events, 0); - let expected_preview = if behavior == ReticulumPreviewBehavior::RejectDeliveryAttempts { - 1 - } else { - 0 - }; - let expected_deferred = if behavior == ReticulumPreviewBehavior::DeferDeliveryPlans { - 1 - } else { - 0 - }; - assert_eq!(status.outbox.preview_unavailable_events, expected_preview); - assert_eq!( - status.outbox.deferred_until_implemented_events, - expected_deferred - ); + assert_eq!(status.outbox.deferred_until_implemented_events, 1); assert_eq!( status.transport_profile.configured_transport_targets[0] .target_scope .as_deref(), - Some("local_preview") + Some("local") ); assert_eq!(status.outbox.total_events, 1); assert_eq!(enqueue.outbox_event_id, 1); @@ -2405,7 +2397,7 @@ async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempt .sync() .push_outbox(PushOutboxRequest::new().with_outbox_event_id(enqueue.outbox_event_id)) .await - .expect("specific Reticulum preview push receipt"); + .expect("specific Reticulum push receipt"); assert_eq!(specific.attempted_events, 0); assert_eq!(specific.events.len(), 1); assert_eq!(specific.events[0].outbox_event_id, enqueue.outbox_event_id); @@ -2413,24 +2405,23 @@ async fn sync_runtime_product_push_outbox_reticulum_preview_reports_zero_attempt } #[tokio::test] -async fn sync_runtime_try_reticulum_preview_now_returns_explicit_unavailable_error() { - let (_tempdir, sdk) = - reticulum_preview_directory_sdk(ReticulumPreviewBehavior::DeferDeliveryPlans).await; +async fn sync_runtime_try_reticulum_now_returns_explicit_unavailable_error() { + let (_tempdir, sdk) = reticulum_directory_sdk(ReticulumBehavior::DeferDeliveryPlans).await; let error = sdk .sync() - .try_reticulum_preview_now(ReticulumPreviewTryNowRequest::new()) + .try_reticulum_now(ReticulumTryNowRequest::new()) .await - .expect_err("Reticulum preview unavailable"); + .expect_err("Reticulum deferred until implemented"); assert!(matches!( error, - RadrootsSdkError::ReticulumPreviewTransportUnavailable { + RadrootsSdkError::ReticulumTransportUnavailable { ref operation, ref endpoint_uri, - behavior: ReticulumPreviewBehavior::DeferDeliveryPlans, - } if operation == "sync.try_reticulum_preview_now" - && endpoint_uri == "reticulum:preview-unavailable" + behavior: ReticulumBehavior::DeferDeliveryPlans, + } if operation == "sync.try_reticulum_now" + && endpoint_uri == "reticulum:local" )); } diff --git a/crates/sdk/tests/trade_product_publish_runtime.rs b/crates/sdk/tests/trade_product_publish_runtime.rs @@ -2,7 +2,7 @@ feature = "runtime", feature = "signer-adapters", feature = "local-signer", - feature = "radrootsd-proxy" + feature = "radrootsd-execution" ))] use radroots_authority::RadrootsActorContext; @@ -21,9 +21,10 @@ use radroots_event::{ }; use radroots_nostr::prelude::{RadrootsNostrKeys, RadrootsNostrSecretKey}; use radroots_sdk::{ - NostrRelayUrlPolicy, ProxyProfile, PublishMode, PushOutboxTargetOutcomeKind, RadrootsClient, - RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, SatisfactionPolicy, - TargetPolicy, TargetSet, TradeMutationOutcome, TradeProposeRequest, TransportProfile, + NostrRelayUrlPolicy, PublishMode, PushOutboxTargetOutcomeKind, RadrootsClient, + RadrootsSdkLocalKeySigner, RadrootsSdkSignerProvider, RadrootsSdkTimestamp, + RadrootsdExecutionProfile, SatisfactionPolicy, TargetPolicy, TargetSet, TradeMutationOutcome, + TradeProposeRequest, }; use std::{ io::{Read, Write}, @@ -241,7 +242,7 @@ async fn trade_product_propose_enqueue_and_publish_uses_ack_policy() { .signer_provider(RadrootsSdkSignerProvider::LocalKey( RadrootsSdkLocalKeySigner::new(signer_keys).expect("local signer"), )) - .transport_profile(TransportProfile::proxy(ProxyProfile::new(endpoint))) + .radrootsd_execution_profile(RadrootsdExecutionProfile::new(endpoint)) .build() .await .expect("sdk"); diff --git a/crates/sdk/tests/unit/adapters_radrootsd_tests.rs b/crates/sdk/tests/unit/adapters_radrootsd_tests.rs @@ -1,10 +1,10 @@ use super::*; use radroots_event::wire::RadrootsNip01EventWire; -use radroots_transport::RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI; +use radroots_transport::RADROOTS_RETICULUM_ENDPOINT_URI; use radroots_transport_publish_protocol::{ NostrPublishTargetSourcePolicy, TransportPublishDeliveryPolicy, TransportPublishEventRequest, TransportPublishEventResponse, TransportPublishJobStatus, TransportPublishJobView, - TransportPublishOutcomeKind, TransportPublishPreviewBehavior, TransportPublishTarget, + TransportPublishOutcomeKind, TransportPublishReticulumBehavior, TransportPublishTarget, TransportPublishTargetOutcome, TransportPublishTargetPolicy, TransportPublishTargetSource, }; use std::io::{Read, Write}; @@ -115,6 +115,14 @@ fn publish_request() -> TransportPublishEventRequest { } } +fn signed_event_id() -> String { + signed_event().id_str().to_owned() +} + +fn signed_event_pubkey() -> String { + signed_event().pubkey_str().to_owned() +} + fn job_status_for_outcome(outcome_kind: TransportPublishOutcomeKind) -> TransportPublishJobStatus { if outcome_kind.counts_toward_accepted_delivery() { TransportPublishJobStatus::DeliverySatisfied @@ -124,8 +132,6 @@ fn job_status_for_outcome(outcome_kind: TransportPublishOutcomeKind) -> Transpor TransportPublishJobStatus::DeliveryUnsatisfiedTerminal } else if outcome_kind == TransportPublishOutcomeKind::DeferredUntilImplemented { TransportPublishJobStatus::DeliveryDeferred - } else if outcome_kind == TransportPublishOutcomeKind::PreviewUnavailable { - TransportPublishJobStatus::DeliveryPreviewUnavailable } else { TransportPublishJobStatus::DeliveryUnsatisfiedRetryable } @@ -141,12 +147,12 @@ fn job(outcome_kind: TransportPublishOutcomeKind) -> TransportPublishJobView { TransportPublishJobStatus::DeliverySatisfied | TransportPublishJobStatus::DeliveryUnsatisfiedTerminal | TransportPublishJobStatus::DeliveryDeferred - | TransportPublishJobStatus::DeliveryPreviewUnavailable + | TransportPublishJobStatus::DeliveryDeferredUntilImplemented | TransportPublishJobStatus::Rejected ), delivery_satisfied: status == TransportPublishJobStatus::DeliverySatisfied, - event_id: "a".repeat(64), - pubkey: "b".repeat(64), + event_id: signed_event_id(), + pubkey: signed_event_pubkey(), event_kind: 30_402, target_policy: TransportPublishTargetPolicy::nostr( NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, @@ -197,8 +203,8 @@ fn explicit_nostr_job( status: TransportPublishJobStatus::DeliverySatisfied, terminal: true, delivery_satisfied: true, - event_id: "a".repeat(64), - pubkey: "b".repeat(64), + event_id: signed_event_id(), + pubkey: signed_event_pubkey(), event_kind: 30_402, target_policy: TransportPublishTargetPolicy::explicit_targets( endpoints @@ -224,12 +230,12 @@ fn reticulum_deferred_job() -> TransportPublishJobView { status: TransportPublishJobStatus::DeliveryDeferred, terminal: true, delivery_satisfied: false, - event_id: "a".repeat(64), - pubkey: "b".repeat(64), + event_id: signed_event_id(), + pubkey: signed_event_pubkey(), event_kind: 30_402, target_policy: TransportPublishTargetPolicy::explicit_targets(vec![ - TransportPublishTarget::reticulum_preview( - TransportPublishPreviewBehavior::DeferDeliveryPlans, + TransportPublishTarget::reticulum( + TransportPublishReticulumBehavior::DeferDeliveryPlans, ), ]), delivery_policy: TransportPublishDeliveryPolicy::Any, @@ -242,13 +248,13 @@ fn reticulum_deferred_job() -> TransportPublishJobView { last_error: Some("delivery_deferred_until_implemented".to_owned()), targets: vec![TransportPublishTargetOutcome { transport_kind: "reticulum".to_owned(), - endpoint_uri: RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI.to_owned(), + endpoint_uri: RADROOTS_RETICULUM_ENDPOINT_URI.to_owned(), target_scope: None, target_label: None, - source: TransportPublishTargetSource::ReticulumPreview, + source: TransportPublishTargetSource::Reticulum, attempted: false, outcome_kind: TransportPublishOutcomeKind::DeferredUntilImplemented, - message: Some("reticulum preview unavailable".to_owned()), + message: Some("reticulum deferred until implemented".to_owned()), latency_ms: None, }], } @@ -257,7 +263,7 @@ fn reticulum_deferred_job() -> TransportPublishJobView { fn publish_response_json_for_job(job: TransportPublishJobView) -> String { serde_json::json!({ "jsonrpc": "2.0", - "id": SDK_RADROOTSD_PROXY_REQUEST_ID, + "id": SDK_RADROOTSD_PUBLISH_REQUEST_ID, "result": { "deduplicated": false, "job": job @@ -273,7 +279,7 @@ fn publish_response_json() -> String { fn reticulum_deferred_response_json() -> String { serde_json::json!({ "jsonrpc": "2.0", - "id": SDK_RADROOTSD_PROXY_REQUEST_ID, + "id": SDK_RADROOTSD_PUBLISH_REQUEST_ID, "result": { "deduplicated": false, "job": reticulum_deferred_job() @@ -332,12 +338,12 @@ fn auth_headers_omit_or_redact_bearer_authorization() { } #[test] -fn proxy_config_builders_preserve_typed_runtime_options() { - let config = RadrootsdProxyConfig::new("http://127.0.0.1:8080/rpc") +fn radrootsd_publish_config_builders_preserve_typed_runtime_options() { + let config = RadrootsdPublishConfig::new("http://127.0.0.1:8080/rpc") .with_auth(RadrootsdAuth::BearerToken("sdk-token".to_owned())) .with_timeout(Duration::from_millis(250)) .with_request_timeout_ms(1_500); - let adapter = RadrootsdProxyPublishAdapter::new(config.clone()); + let adapter = RadrootsdPublishAdapter::new(config.clone()); assert_eq!(adapter.config(), &config); assert_eq!(adapter.config().endpoint, "http://127.0.0.1:8080/rpc"); @@ -355,7 +361,7 @@ fn publish_event_request_json_uses_signed_event_contract() { let raw_event_json = value["raw_event_json"].as_str().expect("raw event json"); let raw_event: serde_json::Value = serde_json::from_str(raw_event_json).expect("raw event"); - assert_eq!(raw_event["id"], "a".repeat(64)); + assert_eq!(raw_event["id"], signed_event_id()); assert_eq!(raw_event["pubkey"], "b".repeat(64)); assert_eq!(raw_event["kind"], 30_402); assert_eq!(value["target_policy"]["kind"], "nostr"); @@ -378,15 +384,15 @@ fn publish_event_request_json_uses_signed_event_contract() { fn decode_jsonrpc_response_validates_envelope_and_errors() { let response: TransportPublishEventResponse = decode_jsonrpc_response( METHOD_EVENT, - SDK_RADROOTSD_PROXY_REQUEST_ID, + SDK_RADROOTSD_PUBLISH_REQUEST_ID, publish_response_json().as_str(), ) .expect("response"); - assert_eq!(response.job.event_id, "a".repeat(64)); + assert_eq!(response.job.event_id, signed_event_id()); let error = decode_jsonrpc_response::<TransportPublishEventResponse>( METHOD_EVENT, - SDK_RADROOTSD_PROXY_REQUEST_ID, + SDK_RADROOTSD_PUBLISH_REQUEST_ID, r#"{"jsonrpc":"2.0","id":"radroots-sdk-transport-publish-event","error":{"code":-32001,"message":"principal unauthorized"}}"#, ) .expect_err("jsonrpc error"); @@ -447,7 +453,7 @@ async fn publish_event_posts_transport_publish_jsonrpc() { .await .expect("publish"); - assert_eq!(receipt.job.event_id, "a".repeat(64)); + assert_eq!(receipt.job.event_id, signed_event_id()); let recorded = handle.join().expect("server thread"); assert_eq!(recorded.request_line, "POST /rpc HTTP/1.1"); assert!( @@ -458,7 +464,7 @@ async fn publish_event_posts_transport_publish_jsonrpc() { ); let body: serde_json::Value = serde_json::from_str(recorded.body.as_str()).expect("body"); assert_eq!(body["method"], METHOD_EVENT); - assert_eq!(body["id"], SDK_RADROOTSD_PROXY_REQUEST_ID); + assert_eq!(body["id"], SDK_RADROOTSD_PUBLISH_REQUEST_ID); let raw_event_json = body["params"]["raw_event_json"] .as_str() .expect("raw event json"); @@ -472,7 +478,7 @@ async fn publish_event_posts_transport_publish_jsonrpc() { } #[tokio::test] -async fn publish_signed_event_posts_typed_proxy_request() { +async fn publish_signed_event_posts_typed_radrootsd_request() { let mut response_job = explicit_nostr_job( vec!["wss://relay.example.com".to_owned()], TransportPublishDeliveryPolicy::All, @@ -486,14 +492,14 @@ async fn publish_signed_event_posts_typed_proxy_request() { response_job.targets[0].target_label = Some("Farm relay".to_owned()); let response_json = publish_response_json_for_job(response_job); let (endpoint, handle) = spawn_http_server("200 OK", response_json.as_str()); - let adapter = RadrootsdProxyPublishAdapter::new( - RadrootsdProxyConfig::new(endpoint) + let adapter = RadrootsdPublishAdapter::new( + RadrootsdPublishConfig::new(endpoint) .with_auth(RadrootsdAuth::BearerToken("sdk-token".into())) .with_request_timeout_ms(7_000), ); let receipt = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::explicit_targets(vec![ TransportPublishTarget::nostr("wss://relay.example.com") @@ -543,17 +549,17 @@ async fn publish_signed_event_posts_typed_proxy_request() { } #[tokio::test] -async fn publish_signed_event_preserves_typed_reticulum_preview_behavior() { +async fn publish_signed_event_preserves_typed_reticulum_behavior() { let response_json = reticulum_deferred_response_json(); let (endpoint, handle) = spawn_http_server("200 OK", response_json.as_str()); - let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(endpoint)); + let adapter = RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new(endpoint)); let response = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::explicit_targets(vec![ - TransportPublishTarget::reticulum_preview( - TransportPublishPreviewBehavior::DeferDeliveryPlans, + TransportPublishTarget::reticulum( + TransportPublishReticulumBehavior::DeferDeliveryPlans, ), ]), delivery_policy: TransportPublishDeliveryPolicy::Any, @@ -577,10 +583,10 @@ async fn publish_signed_event_preserves_typed_reticulum_preview_behavior() { ); assert_eq!( body["params"]["target_policy"]["targets"][0]["endpoint_uri"], - RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI + RADROOTS_RETICULUM_ENDPOINT_URI ); assert_eq!( - body["params"]["target_policy"]["targets"][0]["preview_behavior"], + body["params"]["target_policy"]["targets"][0]["reticulum_behavior"], "defer_delivery_plans" ); } @@ -591,10 +597,10 @@ async fn publish_signed_event_rejects_mismatched_daemon_event_identity() { response_job.event_id = "0".repeat(64); let response_json = publish_response_json_for_job(response_job); let (endpoint, _handle) = spawn_http_server("200 OK", response_json.as_str()); - let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(endpoint)); + let adapter = RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new(endpoint)); let error = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::nostr( NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, @@ -627,10 +633,10 @@ async fn publish_signed_event_rejects_mismatched_daemon_pubkey_and_kind() { ] { let response_json = publish_response_json_for_job(response_job); let (endpoint, _handle) = spawn_http_server("200 OK", response_json.as_str()); - let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(endpoint)); + let adapter = RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new(endpoint)); let error = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::nostr( NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, @@ -654,10 +660,10 @@ async fn publish_signed_event_rejects_mismatched_daemon_delivery_policy() { response_job.delivery_policy = TransportPublishDeliveryPolicy::All; let response_json = publish_response_json_for_job(response_job); let (endpoint, _handle) = spawn_http_server("200 OK", response_json.as_str()); - let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(endpoint)); + let adapter = RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new(endpoint)); let error = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::nostr( NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, @@ -677,14 +683,14 @@ async fn publish_signed_event_rejects_mismatched_daemon_delivery_policy() { #[tokio::test] async fn publish_signed_event_rejects_mismatched_explicit_target_response() { let (endpoint, _handle) = spawn_http_server("200 OK", publish_response_json().as_str()); - let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(endpoint)); + let adapter = RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new(endpoint)); let error = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::explicit_targets(vec![ - TransportPublishTarget::reticulum_preview( - TransportPublishPreviewBehavior::DeferDeliveryPlans, + TransportPublishTarget::reticulum( + TransportPublishReticulumBehavior::DeferDeliveryPlans, ), ]), delivery_policy: TransportPublishDeliveryPolicy::Any, @@ -710,10 +716,10 @@ async fn publish_signed_event_accepts_reordered_explicit_target_outcomes() { response_job.targets.reverse(); let response_json = publish_response_json_for_job(response_job); let (endpoint, _handle) = spawn_http_server("200 OK", response_json.as_str()); - let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(endpoint)); + let adapter = RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new(endpoint)); let response = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::explicit_targets(vec![ TransportPublishTarget::nostr("wss://relay-a.example.com"), @@ -746,10 +752,10 @@ async fn publish_signed_event_rejects_mismatched_explicit_target_outcomes() { response_job.targets[0].endpoint_uri = "wss://relay-other.example.com".to_owned(); let response_json = publish_response_json_for_job(response_job); let (endpoint, _handle) = spawn_http_server("200 OK", response_json.as_str()); - let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(endpoint)); + let adapter = RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new(endpoint)); let error = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::explicit_targets(vec![ TransportPublishTarget::nostr("wss://relay.example.com"), @@ -777,10 +783,10 @@ async fn publish_signed_event_rejects_mismatched_scoped_explicit_target_outcomes response_job.targets[0].target_scope = Some("farm.remote".to_owned()); let response_json = publish_response_json_for_job(response_job); let (endpoint, _handle) = spawn_http_server("200 OK", response_json.as_str()); - let adapter = RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new(endpoint)); + let adapter = RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new(endpoint)); let error = adapter - .publish_signed_event(RadrootsdProxyPublishRequest { + .publish_signed_event(RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::explicit_targets(vec![ TransportPublishTarget::nostr("wss://relay.example.com").with_scope("farm.local"), @@ -836,8 +842,8 @@ async fn publish_event_empty_http_error_reports_empty_body() { #[tokio::test] async fn publish_signed_event_rejects_invalid_target_requests_before_http() { let adapter = - RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new("http://127.0.0.1:9/rpc")); - let base = RadrootsdProxyPublishRequest { + RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new("http://127.0.0.1:9/rpc")); + let base = RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::nostr( NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, @@ -853,7 +859,7 @@ async fn publish_signed_event_rejects_invalid_target_requests_before_http() { let mut too_many_targets = base.clone(); too_many_targets.target_policy = TransportPublishTargetPolicy::nostr( NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, - (0..=SDK_RADROOTSD_PROXY_MAX_TARGETS) + (0..=SDK_RADROOTSD_PUBLISH_MAX_TARGETS) .map(|index| format!("wss://relay-{index}.example.com")) .collect(), ); @@ -862,32 +868,32 @@ async fn publish_signed_event_rejects_invalid_target_requests_before_http() { NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, vec![" ".to_owned()], ); - let mut nostr_preview_behavior = base.clone(); - nostr_preview_behavior.target_policy = + let mut nostr_reticulum_behavior = base.clone(); + nostr_reticulum_behavior.target_policy = TransportPublishTargetPolicy::explicit_targets(vec![TransportPublishTarget { transport_kind: "nostr".to_owned(), endpoint_uri: "wss://relay.example.com".to_owned(), target_scope: None, target_label: None, - preview_behavior: Some(TransportPublishPreviewBehavior::RejectDeliveryAttempts), + reticulum_behavior: Some(TransportPublishReticulumBehavior::RejectDeliveryAttempts), }]); - let mut explicit_proxy_target = base.clone(); - explicit_proxy_target.target_policy = + let mut explicit_radrootsd_target = base.clone(); + explicit_radrootsd_target.target_policy = TransportPublishTargetPolicy::explicit_targets(vec![TransportPublishTarget { - transport_kind: "proxy".to_owned(), - endpoint_uri: "radrootsd-proxy:publish".to_owned(), + transport_kind: "radrootsd".to_owned(), + endpoint_uri: "radrootsd-execution:publish".to_owned(), target_scope: None, target_label: None, - preview_behavior: None, + reticulum_behavior: None, }]); - let proxy_error = adapter - .publish_signed_event(explicit_proxy_target) + let radrootsd_error = adapter + .publish_signed_event(explicit_radrootsd_target) .await - .expect_err("explicit proxy target"); + .expect_err("explicit radrootsd target"); assert!(matches!( - proxy_error, + radrootsd_error, RadrootsdError::InvalidRequest(message) - if message.contains("proxy") && message.contains("daemon explicit target") + if message.contains("transport target 0 kind must be canonical lowercase") )); let mut empty_idempotency = base; empty_idempotency.idempotency_key = Some(" ".to_owned()); @@ -896,7 +902,7 @@ async fn publish_signed_event_rejects_invalid_target_requests_before_http() { invalid_quorum, too_many_targets, empty_endpoint_uri, - nostr_preview_behavior, + nostr_reticulum_behavior, empty_idempotency, ] { assert!(matches!( @@ -909,8 +915,8 @@ async fn publish_signed_event_rejects_invalid_target_requests_before_http() { #[tokio::test] async fn adapter_rejects_invalid_request_before_transport() { let adapter = - RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new("http://127.0.0.1:9/rpc")); - let request = RadrootsdProxyPublishRequest { + RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new("http://127.0.0.1:9/rpc")); + let request = RadrootsdPublishRequest { signed_event: signed_event(), target_policy: TransportPublishTargetPolicy::nostr( NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, diff --git a/crates/sdk/tests/unit/error_tests.rs b/crates/sdk/tests/unit/error_tests.rs @@ -1,6 +1,6 @@ use super::{RadrootsSdkError, RadrootsSdkGeoNamesErrorKind, redacted_relay_url}; use crate::privacy::{PrivacyPreflightStatus, ProductSensitivityField}; -use crate::transport::ReticulumPreviewBehavior; +use crate::transport::ReticulumBehavior; use radroots_authority::RadrootsAuthorityError; use radroots_event::contract::RadrootsActorRole; use radroots_geocoder::{GeoNamesAssetFetcher, GeoNamesBlockingHttpFetcher, GeocoderError}; @@ -306,15 +306,15 @@ fn sdk_error_contract_methods_cover_representative_classes_and_details() { operation: "sync.push_outbox", required_feature: "transport-nostr-runtime", }, - RadrootsSdkError::ReticulumPreviewTransportUnavailable { + RadrootsSdkError::ReticulumTransportUnavailable { operation: "sync.push_outbox".to_owned(), - endpoint_uri: "reticulum:preview-unavailable".to_owned(), - behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts, + endpoint_uri: "reticulum:local".to_owned(), + behavior: ReticulumBehavior::RejectDeliveryAttempts, }, - RadrootsSdkError::ReticulumPreviewTransportUnavailable { + RadrootsSdkError::ReticulumTransportUnavailable { operation: "sync.push_outbox".to_owned(), - endpoint_uri: "reticulum:preview-unavailable".to_owned(), - behavior: ReticulumPreviewBehavior::DeferDeliveryPlans, + endpoint_uri: "reticulum:local".to_owned(), + behavior: ReticulumBehavior::DeferDeliveryPlans, }, RadrootsSdkError::ProductSyncTransportSetupFailure { message: "offline".to_owned(), diff --git a/crates/sdk/tests/unit/orders_runtime_tests.rs b/crates/sdk/tests/unit/orders_runtime_tests.rs @@ -1,6 +1,7 @@ use super::*; #[test] +#[cfg(feature = "transport-nostr-runtime")] fn validation_receipt_limit_rejects_out_of_range_values() { assert!(validate_validation_receipt_limit(1).is_ok()); assert!(validate_validation_receipt_limit(TRADE_STATUS_MAX_LIMIT).is_ok()); diff --git a/crates/sdk/tests/unit/runtime_tests.rs b/crates/sdk/tests/unit/runtime_tests.rs @@ -112,7 +112,6 @@ fn storage_status() -> StorageStatusReceipt { retryable_events: 0, terminal_events: 0, failed_terminal_events: 0, - preview_unavailable_events: 0, deferred_until_implemented_events: 0, ready_signed_events: 0, publishing_events: 0, @@ -287,22 +286,15 @@ async fn private_store_validates_location_rows_and_round_trips_valid_records() { } #[test] -fn transport_profile_defaults_and_delegated_resolution_are_explicit() { +fn transport_profile_defaults_are_explicit() { let local = TransportProfile::default(); assert_eq!(local, TransportProfile::LocalOnly); - assert!(!local.supports_delegated_target_resolution()); let nostr = nostr_profile( ["wss://relay.example.com"], crate::NostrRelayUrlPolicy::Public, ); - assert!(!nostr.supports_delegated_target_resolution()); - - #[cfg(feature = "radrootsd-proxy")] - { - let proxy = TransportProfile::proxy(crate::ProxyProfile::new("http://127.0.0.1:9/rpc")); - assert!(proxy.supports_delegated_target_resolution()); - } + assert_eq!(nostr.transport_profile_id(), "nostr"); } #[tokio::test] @@ -320,6 +312,7 @@ async fn open_storage_and_storage_kind_cover_memory_directory_and_file_failures( geonames: None, clock: RadrootsSdkClock::Fixed(RadrootsSdkTimestamp::from_unix_seconds(1)), transport_profile: TransportProfile::local_only(), + radrootsd_execution_profile: None, #[cfg(feature = "signer-adapters")] signer_provider: None, }; @@ -343,6 +336,7 @@ async fn open_storage_and_storage_kind_cover_memory_directory_and_file_failures( geonames: None, clock: RadrootsSdkClock::Fixed(RadrootsSdkTimestamp::from_unix_seconds(1)), transport_profile: TransportProfile::local_only(), + radrootsd_execution_profile: None, #[cfg(feature = "signer-adapters")] signer_provider: None, }; diff --git a/crates/sdk/tests/unit/sync_runtime_tests.rs b/crates/sdk/tests/unit/sync_runtime_tests.rs @@ -1,8 +1,9 @@ -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use super::{ - CLAIM_OWNER, complete_proxy_publish_attempt, proxy_delivery_policy_from_remaining, - proxy_error_message, proxy_outbox_idempotency_key, proxy_required_remaining_targets, - proxy_transport_error_receipt, push_proxy_claimed_outbox_event, push_proxy_event_receipt, + CLAIM_OWNER, complete_radrootsd_publish_attempt, push_radrootsd_claimed_outbox_event, + push_radrootsd_event_receipt, radrootsd_delivery_policy_from_remaining, + radrootsd_error_message, radrootsd_outbox_idempotency_key, + radrootsd_required_remaining_targets, radrootsd_transport_error_receipt, transport_publish_target_from_outbox_target, }; use super::{ @@ -11,30 +12,28 @@ use super::{ push_event_final_state, push_event_receipt, push_outbox_claim_token, }; use crate::RadrootsSdkError; -#[cfg(feature = "radrootsd-proxy")] -use crate::adapters::radrootsd::{ - RadrootsdError, RadrootsdProxyConfig, RadrootsdProxyPublishAdapter, -}; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] +use crate::adapters::radrootsd::{RadrootsdError, RadrootsdPublishAdapter, RadrootsdPublishConfig}; +#[cfg(feature = "radrootsd-execution")] use crate::workflow_runtime::{SdkWorkflowEnqueueRequest, enqueue_signed_workflow}; use futures::future::BoxFuture; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use radroots_authority::{ RadrootsActorContext, RadrootsEventSigner, RadrootsSignerError, RadrootsSignerIdentity, }; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use radroots_event::contract::RadrootsActorRole; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use radroots_event::draft::{RadrootsEventDraft, RadrootsSignedEvent}; use radroots_event::ids::RadrootsEventId; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use radroots_event::kinds::KIND_FARM; use radroots_event_store::RadrootsEventStoreStatusSummary; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use radroots_nostr::prelude::{ RadrootsNostrKeys, RadrootsNostrSecretKey, radroots_nostr_sign_frozen_draft, }; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use radroots_outbox::{ RadrootsOutboxClaimedEvent, RadrootsOutboxDeliveryPlanInput, RadrootsOutboxDeliveryPlanStatus, RadrootsOutboxDeliveryTargetRecord, RadrootsOutboxDeliveryTargetStatus, @@ -47,57 +46,58 @@ use radroots_transport::{ RadrootsTransportDeliveryTargetStatus, RadrootsTransportMeshScopeId, RadrootsTransportTarget, RadrootsTransportTargetLabel, }; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use radroots_transport::{RadrootsTransportSatisfactionClass, RadrootsTransportSatisfactionPolicy}; use radroots_transport_nostr::{ RadrootsNostrTransport, RadrootsOutboxPublishReceipt, RadrootsOutboxPublishTargetReceipt, RadrootsRelayOutcomeKind, RadrootsRelayPublishAdapter, RadrootsRelayPublishRelayReceipt, RadrootsRelayPublishRequest, RadrootsRelayTransportError, }; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use radroots_transport_publish_protocol::{ NostrPublishTargetSourcePolicy, TransportPublishDeliveryPolicy, TransportPublishJobStatus, TransportPublishJobView, TransportPublishOutcomeKind, TransportPublishTarget, TransportPublishTargetOutcome, TransportPublishTargetPolicy, TransportPublishTargetSource, }; use std::collections::BTreeSet; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use std::io::ErrorKind; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use std::net::TcpListener; -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] use std::time::Duration; -#[cfg(feature = "radrootsd-proxy")] -const PROXY_SIGNER_SECRET_KEY_HEX: &str = +#[cfg(feature = "radrootsd-execution")] +const RADROOTSD_FIXTURE_SIGNER_SECRET_KEY_HEX: &str = "10c5304d6c9ae3a1a16f7860f1cc8f5e3a76225a2663b3a989a0d775919b7df5"; -#[cfg(feature = "radrootsd-proxy")] -const PROXY_SIGNER_PUBLIC_KEY_HEX: &str = +#[cfg(feature = "radrootsd-execution")] +const RADROOTSD_FIXTURE_SIGNER_PUBLIC_KEY_HEX: &str = "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"; struct UnusedPublishAdapter; -#[cfg(feature = "radrootsd-proxy")] -struct ProxyFixtureSigner { +#[cfg(feature = "radrootsd-execution")] +struct RadrootsdFixtureSigner { identity: RadrootsSignerIdentity, keys: RadrootsNostrKeys, } -#[cfg(feature = "radrootsd-proxy")] -impl ProxyFixtureSigner { +#[cfg(feature = "radrootsd-execution")] +impl RadrootsdFixtureSigner { fn new() -> Self { - let secret_key = - RadrootsNostrSecretKey::from_hex(PROXY_SIGNER_SECRET_KEY_HEX).expect("secret key"); + let secret_key = RadrootsNostrSecretKey::from_hex(RADROOTSD_FIXTURE_SIGNER_SECRET_KEY_HEX) + .expect("secret key"); let keys = RadrootsNostrKeys::new(secret_key); Self { - identity: RadrootsSignerIdentity::new(PROXY_SIGNER_PUBLIC_KEY_HEX).expect("identity"), + identity: RadrootsSignerIdentity::new(RADROOTSD_FIXTURE_SIGNER_PUBLIC_KEY_HEX) + .expect("identity"), keys, } } } -#[cfg(feature = "radrootsd-proxy")] -impl RadrootsEventSigner for ProxyFixtureSigner { +#[cfg(feature = "radrootsd-execution")] +impl RadrootsEventSigner for RadrootsdFixtureSigner { fn pubkey(&self) -> &radroots_event::ids::RadrootsPublicKey { self.identity.pubkey() } @@ -124,27 +124,32 @@ impl RadrootsRelayPublishAdapter for UnusedPublishAdapter { } } -#[cfg(feature = "radrootsd-proxy")] -fn proxy_actor() -> RadrootsActorContext { - RadrootsActorContext::test(PROXY_SIGNER_PUBLIC_KEY_HEX, [RadrootsActorRole::Farmer]) - .expect("actor") +#[cfg(feature = "radrootsd-execution")] +fn radrootsd_actor() -> RadrootsActorContext { + RadrootsActorContext::test( + RADROOTSD_FIXTURE_SIGNER_PUBLIC_KEY_HEX, + [RadrootsActorRole::Farmer], + ) + .expect("actor") } -#[cfg(feature = "radrootsd-proxy")] -fn proxy_frozen_draft(d_tag: &str) -> RadrootsEventDraft { +#[cfg(feature = "radrootsd-execution")] +fn radrootsd_frozen_draft(d_tag: &str) -> RadrootsEventDraft { RadrootsEventDraft::new( "radroots.farm.profile.v1", KIND_FARM, 1_700_000_000, vec![vec!["d".to_owned(), d_tag.to_owned()]], "{}", - PROXY_SIGNER_PUBLIC_KEY_HEX, + RADROOTSD_FIXTURE_SIGNER_PUBLIC_KEY_HEX, ) .expect("frozen draft") } -#[cfg(feature = "radrootsd-proxy")] -async fn claimed_proxy_event(d_tag: &str) -> (crate::RadrootsClient, RadrootsOutboxClaimedEvent) { +#[cfg(feature = "radrootsd-execution")] +async fn claimed_radrootsd_event( + d_tag: &str, +) -> (crate::RadrootsClient, RadrootsOutboxClaimedEvent) { let sdk = crate::RadrootsClient::builder() .fixed_clock(crate::RadrootsSdkTimestamp::from_unix_seconds( 1_700_000_000, @@ -152,12 +157,12 @@ async fn claimed_proxy_event(d_tag: &str) -> (crate::RadrootsClient, RadrootsOut .build() .await .expect("sdk"); - let actor = proxy_actor(); - let draft = proxy_frozen_draft(d_tag); + let actor = radrootsd_actor(); + let draft = radrootsd_frozen_draft(d_tag); enqueue_signed_workflow( &sdk, SdkWorkflowEnqueueRequest { - operation_kind: "sync.proxy.unit.v1", + operation_kind: "sync.radrootsd.unit.v1", actor: &actor, frozen_draft: &draft, target_policy: crate::TargetPolicy::try_nostr_relays( @@ -166,9 +171,12 @@ async fn claimed_proxy_event(d_tag: &str) -> (crate::RadrootsClient, RadrootsOut ) .expect("target relays"), satisfaction_policy: crate::SatisfactionPolicy::AllAccepted, - idempotency_key: None, + idempotency_key: Some( + crate::SdkIdempotencyKey::new("01890f0e-6c00-7000-8000-00000000025a") + .expect("idempotency key"), + ), }, - &ProxyFixtureSigner::new(), + &RadrootsdFixtureSigner::new(), ) .await .expect("enqueue signed workflow"); @@ -176,7 +184,7 @@ async fn claimed_proxy_event(d_tag: &str) -> (crate::RadrootsClient, RadrootsOut ._outbox .claim_next_ready_signed_event( CLAIM_OWNER, - "proxy-unit-claim", + "radrootsd-unit-claim", 1_700_000_060_000, 1_700_000_000_000, ) @@ -186,23 +194,23 @@ async fn claimed_proxy_event(d_tag: &str) -> (crate::RadrootsClient, RadrootsOut (sdk, claimed) } -#[cfg(feature = "radrootsd-proxy")] -async fn claimed_uningested_proxy_event( +#[cfg(feature = "radrootsd-execution")] +async fn claimed_uningested_radrootsd_event( d_tag: &str, - proxy_endpoint: &str, + radrootsd_endpoint: &str, ) -> (crate::RadrootsClient, RadrootsOutboxClaimedEvent) { - claimed_uningested_proxy_event_with_satisfaction( + claimed_uningested_radrootsd_event_with_satisfaction( d_tag, - proxy_endpoint, + radrootsd_endpoint, RadrootsTransportSatisfactionPolicy::all_accepted(), ) .await } -#[cfg(feature = "radrootsd-proxy")] -async fn claimed_uningested_proxy_event_with_satisfaction( +#[cfg(feature = "radrootsd-execution")] +async fn claimed_uningested_radrootsd_event_with_satisfaction( d_tag: &str, - proxy_endpoint: &str, + _radrootsd_endpoint: &str, satisfaction_policy: RadrootsTransportSatisfactionPolicy, ) -> (crate::RadrootsClient, RadrootsOutboxClaimedEvent) { let sdk = crate::RadrootsClient::builder() @@ -212,23 +220,24 @@ async fn claimed_uningested_proxy_event_with_satisfaction( .build() .await .expect("sdk"); - let draft = proxy_frozen_draft(d_tag); - let proxy_target = RadrootsTransportTarget::proxy(proxy_endpoint).expect("proxy target"); + let draft = radrootsd_frozen_draft(d_tag); + let radrootsd_target = + RadrootsTransportTarget::nostr_relay("wss://relay.example.com").expect("Nostr target"); let enqueue = sdk ._outbox .enqueue_operation( RadrootsOutboxOperationInput::new( - "sync.proxy.unit.v1", + "sync.radrootsd.unit.v1", draft, RadrootsOutboxDeliveryPlanInput::new( - "proxy", + "radrootsd", 1, satisfaction_policy, - vec![proxy_target], + vec![radrootsd_target], ), 1_700_000_000_000, ) - .with_idempotency_key(format!("proxy-uningested-{d_tag}")), + .with_idempotency_key(format!("radrootsd-uningested-{d_tag}")), ) .await .expect("enqueue"); @@ -236,20 +245,20 @@ async fn claimed_uningested_proxy_event_with_satisfaction( ._outbox .claim_next_ready_event( CLAIM_OWNER, - "proxy-unit-sign", + "radrootsd-unit-sign", 1_700_000_000_500, 1_700_000_000_000, ) .await .expect("signing claim") .expect("signing claim"); - let signed_event = ProxyFixtureSigner::new() + let signed_event = RadrootsdFixtureSigner::new() .sign_frozen_draft(&signing_claim.draft) .expect("signed event"); sdk._outbox .complete_signing( enqueue.outbox_event_id, - "proxy-unit-sign", + "radrootsd-unit-sign", signed_event, 1_700_000_000_100, ) @@ -269,7 +278,7 @@ async fn claimed_uningested_proxy_event_with_satisfaction( ._outbox .claim_next_ready_signed_event( CLAIM_OWNER, - "proxy-unit-publish", + "radrootsd-unit-publish", 1_700_000_060_000, 1_700_000_000_100, ) @@ -279,7 +288,7 @@ async fn claimed_uningested_proxy_event_with_satisfaction( (sdk, claimed) } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] fn assert_no_transport_publish_request(listener: &TcpListener) { listener.set_nonblocking(true).expect("nonblocking"); match listener.accept() { @@ -289,7 +298,7 @@ fn assert_no_transport_publish_request(listener: &TcpListener) { } } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] fn delivery_target_record( delivery_target_id: i64, delivery_plan_id: i64, @@ -312,8 +321,11 @@ fn delivery_target_record( } } -#[cfg(feature = "radrootsd-proxy")] -fn proxy_job(event_id: &str, outcome_kind: TransportPublishOutcomeKind) -> TransportPublishJobView { +#[cfg(feature = "radrootsd-execution")] +fn radrootsd_job( + event_id: &str, + outcome_kind: TransportPublishOutcomeKind, +) -> TransportPublishJobView { let delivery_satisfied = outcome_kind.counts_toward_accepted_delivery(); let retryable = outcome_kind.is_retryable(); let terminal_failure = outcome_kind.is_terminal_failure(); @@ -323,18 +335,16 @@ fn proxy_job(event_id: &str, outcome_kind: TransportPublishOutcomeKind) -> Trans TransportPublishJobStatus::DeliveryUnsatisfiedRetryable } else if outcome_kind == TransportPublishOutcomeKind::DeferredUntilImplemented { TransportPublishJobStatus::DeliveryDeferred - } else if outcome_kind == TransportPublishOutcomeKind::PreviewUnavailable { - TransportPublishJobStatus::DeliveryPreviewUnavailable } else { TransportPublishJobStatus::DeliveryUnsatisfiedTerminal }; TransportPublishJobView { - job_id: "proxy-unit-job".to_owned(), + job_id: "radrootsd-unit-job".to_owned(), status, terminal: !retryable, delivery_satisfied, event_id: event_id.to_owned(), - pubkey: PROXY_SIGNER_PUBLIC_KEY_HEX.to_owned(), + pubkey: RADROOTSD_FIXTURE_SIGNER_PUBLIC_KEY_HEX.to_owned(), event_kind: KIND_FARM, target_policy: TransportPublishTargetPolicy::nostr( NostrPublishTargetSourcePolicy::RequestThenAuthorWriteThenDaemonDefault, @@ -439,7 +449,7 @@ fn push_event_final_state_follows_publish_quorum_and_retryability() { } #[test] -fn push_relay_outcome_mapping_covers_daemon_proxy_results() { +fn push_relay_outcome_mapping_covers_daemon_radrootsd_results() { assert_eq!( PushOutboxTargetOutcomeKind::from(RadrootsRelayOutcomeKind::Muted), PushOutboxTargetOutcomeKind::Muted @@ -542,7 +552,6 @@ fn sync_status_summary_conversions_preserve_all_fields() { retryable_events: 4, terminal_events: 2, failed_terminal_events: 1, - preview_unavailable_events: 9, deferred_until_implemented_events: 10, ready_signed_events: 6, publishing_events: 8, @@ -555,7 +564,6 @@ fn sync_status_summary_conversions_preserve_all_fields() { assert_eq!(outbox_status.retryable_events, 4); assert_eq!(outbox_status.terminal_events, 2); assert_eq!(outbox_status.failed_terminal_events, 1); - assert_eq!(outbox_status.preview_unavailable_events, 9); assert_eq!(outbox_status.deferred_until_implemented_events, 10); assert_eq!(outbox_status.ready_signed_events, 6); assert_eq!(outbox_status.publishing_events, 8); @@ -699,8 +707,8 @@ fn push_outbox_outcome_kind_labels_cover_all_public_variants() { "deferred_until_implemented", ), ( - PushOutboxTargetOutcomeKind::PreviewUnavailable, - "preview_unavailable", + PushOutboxTargetOutcomeKind::DeferredUntilImplemented, + "deferred_until_implemented", ), (PushOutboxTargetOutcomeKind::Unknown, "unknown"), ] { @@ -796,58 +804,58 @@ async fn sync_runtime_reports_clock_errors_before_store_or_relay_work() { )); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_push_empty_queue_and_private_helpers_are_deterministic() { +async fn radrootsd_push_empty_queue_and_private_helpers_are_deterministic() { let sdk = crate::RadrootsClient::builder().build().await.expect("sdk"); let adapter = - RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new("http://127.0.0.1:9/rpc")); + RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new("http://127.0.0.1:9/rpc")); let receipt = sdk .sync() - .push_outbox_with_proxy_adapter(&adapter, super::PushOutboxRequest::new()) + .push_outbox_with_radrootsd_adapter(&adapter, super::PushOutboxRequest::new()) .await .expect("empty transport publish push"); assert_eq!(receipt.attempted_events, 0); assert_eq!( - proxy_delivery_policy_from_remaining( + radrootsd_delivery_policy_from_remaining( 0, 0, None, &RadrootsTransportSatisfactionPolicy::no_wait() ) - .expect("no-wait proxy policy"), + .expect("no-wait radrootsd policy"), TransportPublishDeliveryPolicy::Any ); assert_eq!( - proxy_delivery_policy_from_remaining( + radrootsd_delivery_policy_from_remaining( 0, 0, None, &RadrootsTransportSatisfactionPolicy::all_accepted() ) - .expect("zero-target proxy policy"), + .expect("zero-target radrootsd policy"), TransportPublishDeliveryPolicy::Any ); assert_eq!( - proxy_delivery_policy_from_remaining( + radrootsd_delivery_policy_from_remaining( 2, 2, None, &RadrootsTransportSatisfactionPolicy::all_accepted() ) - .expect("all-target proxy policy"), + .expect("all-target radrootsd policy"), TransportPublishDeliveryPolicy::All ); assert_eq!( - proxy_delivery_policy_from_remaining( + radrootsd_delivery_policy_from_remaining( 2, 1, None, &RadrootsTransportSatisfactionPolicy::any_accepted() ) - .expect("any-target proxy policy"), + .expect("any-target radrootsd policy"), TransportPublishDeliveryPolicy::Any ); let first_required = RadrootsTransportTarget::nostr_relay("wss://required-a.example.com") @@ -870,35 +878,35 @@ async fn proxy_push_empty_queue_and_private_helpers_are_deterministic() { let mut optional_record = delivery_target_record(3, 7, &optional); optional_record.status = RadrootsOutboxDeliveryTargetStatus::Accepted; let active_targets = vec![&first_record, &second_record, &optional_record]; - let remaining = proxy_required_remaining_targets(&policy, &active_targets) + let remaining = radrootsd_required_remaining_targets(&policy, &active_targets) .expect("required remaining targets") .expect("required target policy"); assert_eq!(remaining, vec![second_required.fingerprint]); assert_eq!( - proxy_delivery_policy_from_remaining(2, remaining.len(), Some(&remaining), &policy) - .expect("required target proxy policy"), + radrootsd_delivery_policy_from_remaining(2, remaining.len(), Some(&remaining), &policy) + .expect("required target radrootsd policy"), TransportPublishDeliveryPolicy::RequiredTargets { targets: remaining } ); assert!(matches!( - proxy_delivery_policy_from_remaining(0, 1, Some(&[]), &policy), + radrootsd_delivery_policy_from_remaining(0, 1, Some(&[]), &policy), Err(RadrootsSdkError::InvalidRequest { message }) if message.contains("unsatisfied required targets") )); assert_eq!( - proxy_outbox_idempotency_key(7, 3, "event-id", 5), + radrootsd_outbox_idempotency_key(7, 3, "event-id", 5), "radroots-sdk-outbox-7-3-event-id-5" ); - let (_sdk, claimed) = claimed_proxy_event("proxy-transport-error-receipt").await; + let (_sdk, claimed) = claimed_radrootsd_event("radrootsd-transport-error-receipt").await; let signed_event = claimed.signed_event.as_ref().expect("signed event"); - let message = proxy_error_message(&RadrootsdError::Http("connection refused".to_owned())); - let receipt = proxy_transport_error_receipt( + let message = radrootsd_error_message(&RadrootsdError::Http("connection refused".to_owned())); + let receipt = radrootsd_transport_error_receipt( &claimed, signed_event, &TransportPublishDeliveryPolicy::All, message.clone(), ) - .expect("proxy transport error receipt"); + .expect("radrootsd transport error receipt"); assert_eq!(receipt.event_id, signed_event.id_str()); assert_eq!(receipt.final_state, PushOutboxEventState::PublishRetryable); assert_eq!(receipt.retryable_count, 1); @@ -911,15 +919,15 @@ async fn proxy_push_empty_queue_and_private_helpers_are_deterministic() { assert!(!receipt.targets[0].attempted); assert_eq!(receipt.targets[0].message.as_ref(), Some(&message)); assert_eq!( - proxy_error_message(&RadrootsdError::Http("connection refused".to_owned())), - "radrootsd proxy publish failed: connection refused" + radrootsd_error_message(&RadrootsdError::Http("connection refused".to_owned())), + "radrootsd publish failed: connection refused" ); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_delivery_policy_rejects_non_accepted_satisfaction_before_daemon_publish() { - let listener = TcpListener::bind("127.0.0.1:0").expect("bind proxy listener"); +async fn radrootsd_delivery_policy_rejects_non_accepted_satisfaction_before_daemon_publish() { + let listener = TcpListener::bind("127.0.0.1:0").expect("bind radrootsd listener"); let endpoint = format!("http://{}/rpc", listener.local_addr().expect("addr")); for (index, satisfaction_policy) in [ RadrootsTransportSatisfactionPolicy::all_forwarded(), @@ -931,28 +939,33 @@ async fn proxy_delivery_policy_rejects_non_accepted_satisfaction_before_daemon_p .into_iter() .enumerate() { - let d_tag = format!("proxy-non-accepted-rejected-{index}"); - let (sdk, claimed) = claimed_uningested_proxy_event_with_satisfaction( + let d_tag = format!("radrootsd-non-accepted-rejected-{index}"); + let (sdk, claimed) = claimed_uningested_radrootsd_event_with_satisfaction( d_tag.as_str(), endpoint.as_str(), satisfaction_policy, ) .await; let sync = sdk.sync(); - let adapter = RadrootsdProxyPublishAdapter::new( - RadrootsdProxyConfig::new(endpoint.clone()).with_timeout(Duration::from_millis(50)), + let adapter = RadrootsdPublishAdapter::new( + RadrootsdPublishConfig::new(endpoint.clone()).with_timeout(Duration::from_millis(50)), ); - let error = - push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) - .await - .expect_err("non-accepted-class proxy satisfaction rejected"); + let error = push_radrootsd_claimed_outbox_event( + &sync, + &adapter, + &claimed, + 60_000, + 1_700_000_000_000, + ) + .await + .expect_err("non-accepted-class radrootsd satisfaction rejected"); assert_no_transport_publish_request(&listener); assert!(matches!( error, RadrootsSdkError::InvalidRequest { message } - if message.contains("radrootsd proxy publish") + if message.contains("radrootsd publish") && message.contains("accepted-class satisfaction") )); let stored = sdk @@ -975,10 +988,10 @@ async fn proxy_delivery_policy_rejects_non_accepted_satisfaction_before_daemon_p } } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[test] -fn proxy_outbox_target_conversion_rejects_reticulum_targets_before_behavior_loss() { - let target = RadrootsTransportTarget::reticulum_preview().expect("Reticulum target"); +fn radrootsd_outbox_target_conversion_rejects_reticulum_targets_before_behavior_loss() { + let target = RadrootsTransportTarget::reticulum().expect("Reticulum target"); let record = RadrootsOutboxDeliveryTargetRecord { delivery_target_id: 1, delivery_plan_id: 1, @@ -1001,15 +1014,15 @@ fn proxy_outbox_target_conversion_rejects_reticulum_targets_before_behavior_loss assert!(matches!( error, RadrootsSdkError::InvalidRequest { message } - if message.contains("radrootsd proxy outbox publish") + if message.contains("radrootsd execution") && message.contains("Nostr-only") && message.contains("reticulum target") )); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[test] -fn proxy_outbox_target_conversion_preserves_nostr_scope_and_label() { +fn radrootsd_outbox_target_conversion_preserves_nostr_scope_and_label() { let target = RadrootsTransportTarget::nostr_relay_with_metadata( "wss://relay.example.com", Some(RadrootsTransportMeshScopeId::parse("farm.local").expect("scope")), @@ -1024,47 +1037,21 @@ fn proxy_outbox_target_conversion_preserves_nostr_scope_and_label() { assert_eq!(converted.endpoint_uri, "wss://relay.example.com"); assert_eq!(converted.target_scope.as_deref(), Some("farm.local")); assert_eq!(converted.target_label.as_deref(), Some("Farm relay")); - assert_eq!(converted.preview_behavior, None); -} - -#[cfg(feature = "radrootsd-proxy")] -#[test] -fn proxy_outbox_target_conversion_rejects_proxy_targets_before_daemon_explicit_target() { - let target = RadrootsTransportTarget::proxy("http://127.0.0.1:8080/rpc").expect("proxy target"); - let record = RadrootsOutboxDeliveryTargetRecord { - delivery_target_id: 1, - delivery_plan_id: 1, - transport_kind: target.kind.clone(), - endpoint_uri: target.uri.clone(), - target_scope: target.scope.clone(), - target_label: target.label.clone(), - endpoint_fingerprint: target.fingerprint.clone(), - status: RadrootsOutboxDeliveryTargetStatus::Pending, - last_outcome_kind: None, - attempt_count: 0, - last_attempt_at_ms: None, - completed_at_ms: None, - last_error: None, - }; - - let error = transport_publish_target_from_outbox_target(&record).expect_err("proxy rejected"); - - assert!(matches!( - error, - RadrootsSdkError::InvalidRequest { message } - if message.contains("Nostr-only") && message.contains("proxy target") - )); + assert_eq!(converted.reticulum_behavior, None); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_push_entrypoints_report_request_clock_and_claim_errors() { +async fn radrootsd_push_entrypoints_report_request_clock_and_claim_errors() { let adapter = - RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new("http://127.0.0.1:9/rpc")); + RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new("http://127.0.0.1:9/rpc")); let sdk = crate::RadrootsClient::builder().build().await.expect("sdk"); assert!(matches!( sdk.sync() - .push_outbox_with_proxy_adapter(&adapter, super::PushOutboxRequest::new().with_limit(0)) + .push_outbox_with_radrootsd_adapter( + &adapter, + super::PushOutboxRequest::new().with_limit(0) + ) .await, Err(RadrootsSdkError::InvalidRequest { .. }) )); @@ -1077,7 +1064,7 @@ async fn proxy_push_entrypoints_report_request_clock_and_claim_errors() { assert!(matches!( clock_sdk .sync() - .push_outbox_with_proxy_adapter(&adapter, super::PushOutboxRequest::new()) + .push_outbox_with_radrootsd_adapter(&adapter, super::PushOutboxRequest::new()) .await, Err(RadrootsSdkError::ClockBeforeUnixEpoch) )); @@ -1090,19 +1077,19 @@ async fn proxy_push_entrypoints_report_request_clock_and_claim_errors() { assert!(matches!( closed_outbox_sdk .sync() - .push_outbox_with_proxy_adapter(&adapter, super::PushOutboxRequest::new()) + .push_outbox_with_radrootsd_adapter(&adapter, super::PushOutboxRequest::new()) .await, Err(RadrootsSdkError::Outbox { .. }) )); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_push_reports_missing_signed_claim_before_daemon_publish() { +async fn radrootsd_push_reports_missing_signed_claim_before_daemon_publish() { let sdk = crate::RadrootsClient::builder().build().await.expect("sdk"); let sync = sdk.sync(); let adapter = - RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new("http://127.0.0.1:9/rpc")); + RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new("http://127.0.0.1:9/rpc")); let claimed = RadrootsOutboxClaimedEvent { outbox_event_id: 41, operation_id: 42, @@ -1117,7 +1104,7 @@ async fn proxy_push_reports_missing_signed_claim_before_daemon_publish() { 1_700_000_000, vec![vec!["d".to_owned(), "missing-signed-event".to_owned()]], "{}", - PROXY_SIGNER_PUBLIC_KEY_HEX, + RADROOTSD_FIXTURE_SIGNER_PUBLIC_KEY_HEX, ) .expect("draft"), signed_event: None, @@ -1125,22 +1112,22 @@ async fn proxy_push_reports_missing_signed_claim_before_daemon_publish() { }; assert!(matches!( - push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) + push_radrootsd_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) .await, Err(RadrootsSdkError::Transport { message }) if message.contains("Outbox claim 41 does not contain a signed event") )); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_claim_publish_marks_retryable_transport_errors() { - let (sdk, claimed) = claimed_proxy_event("proxy-transport-error").await; +async fn radrootsd_claim_publish_marks_retryable_transport_errors() { + let (sdk, claimed) = claimed_radrootsd_event("radrootsd-transport-error").await; let sync = sdk.sync(); let adapter = - RadrootsdProxyPublishAdapter::new(RadrootsdProxyConfig::new("http://127.0.0.1:9/rpc")); + RadrootsdPublishAdapter::new(RadrootsdPublishConfig::new("http://127.0.0.1:9/rpc")); let receipt = - push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) + push_radrootsd_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) .await .expect("transport error job"); @@ -1156,7 +1143,7 @@ async fn proxy_claim_publish_marks_retryable_transport_errors() { receipt.targets[0] .message .as_deref() - .is_some_and(|message| message.contains("radrootsd proxy publish failed")) + .is_some_and(|message| message.contains("radrootsd publish failed")) ); let stored = sdk ._outbox @@ -1168,13 +1155,14 @@ async fn proxy_claim_publish_marks_retryable_transport_errors() { assert!(stored.claim_token.is_none()); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_local_validation_errors_release_claim_before_daemon_publish() { - let listener = TcpListener::bind("127.0.0.1:0").expect("bind proxy listener"); +async fn radrootsd_local_validation_errors_release_claim_before_daemon_publish() { + let listener = TcpListener::bind("127.0.0.1:0").expect("bind radrootsd listener"); let endpoint = format!("http://{}/rpc", listener.local_addr().expect("addr")); let (sdk, mut claimed) = - claimed_uningested_proxy_event("proxy-local-validation-error", endpoint.as_str()).await; + claimed_uningested_radrootsd_event("radrootsd-local-validation-error", endpoint.as_str()) + .await; let stored_before = sdk ._outbox .get_event(claimed.outbox_event_id) @@ -1183,24 +1171,24 @@ async fn proxy_local_validation_errors_release_claim_before_daemon_publish() { .expect("stored before"); assert!(!stored_before.event_store_ingested); assert_eq!(stored_before.event_store_ingested_at_ms, None); - let reticulum_target = RadrootsTransportTarget::reticulum_preview().expect("Reticulum target"); + let reticulum_target = RadrootsTransportTarget::reticulum().expect("Reticulum target"); claimed.delivery_targets[0].transport_kind = reticulum_target.kind; claimed.delivery_targets[0].endpoint_uri = reticulum_target.uri; claimed.delivery_targets[0].endpoint_fingerprint = reticulum_target.fingerprint; let sync = sdk.sync(); - let adapter = RadrootsdProxyPublishAdapter::new( - RadrootsdProxyConfig::new(endpoint).with_timeout(Duration::from_millis(50)), + let adapter = RadrootsdPublishAdapter::new( + RadrootsdPublishConfig::new(endpoint).with_timeout(Duration::from_millis(50)), ); let error = - push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) + push_radrootsd_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) .await - .expect_err("local proxy validation error"); + .expect_err("local radrootsd validation error"); assert_no_transport_publish_request(&listener); assert!(matches!( error, RadrootsSdkError::InvalidRequest { message } - if message.contains("radrootsd proxy outbox publish") + if message.contains("radrootsd execution") && message.contains("Nostr-only") && message.contains("reticulum target") )); @@ -1223,10 +1211,10 @@ async fn proxy_local_validation_errors_release_claim_before_daemon_publish() { ); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() { - let listener = TcpListener::bind("127.0.0.1:0").expect("bind proxy listener"); +async fn radrootsd_local_validation_failure_keeps_sibling_plan_ready_and_claimable() { + let listener = TcpListener::bind("127.0.0.1:0").expect("bind radrootsd listener"); let endpoint = format!("http://{}/rpc", listener.local_addr().expect("addr")); let sdk = crate::RadrootsClient::builder() .fixed_clock(crate::RadrootsSdkTimestamp::from_unix_seconds( @@ -1235,19 +1223,19 @@ async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() .build() .await .expect("sdk"); - let draft = proxy_frozen_draft("proxy-local-validation-sibling"); - let signed_event = ProxyFixtureSigner::new() + let draft = radrootsd_frozen_draft("radrootsd-local-validation-sibling"); + let signed_event = RadrootsdFixtureSigner::new() .sign_frozen_draft(&draft) .expect("signed event"); let first = sdk ._outbox .enqueue_signed_operation( RadrootsOutboxSignedOperationInput::new( - "sync.proxy.unit.v1", + "sync.radrootsd.unit.v1", draft.clone(), signed_event.clone(), RadrootsOutboxDeliveryPlanInput::new( - "proxy.validation.active", + "radrootsd.validation.active", 1, RadrootsTransportSatisfactionPolicy::all_accepted(), vec![ @@ -1259,7 +1247,7 @@ async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() 1_700_000_000_000, 1_700_000_000_000, ) - .with_idempotency_key("proxy-local-validation-sibling"), + .with_idempotency_key("radrootsd-local-validation-sibling"), ) .await .expect("first plan"); @@ -1267,11 +1255,11 @@ async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() ._outbox .enqueue_signed_operation( RadrootsOutboxSignedOperationInput::new( - "sync.proxy.unit.v1", + "sync.radrootsd.unit.v1", draft, signed_event, RadrootsOutboxDeliveryPlanInput::new( - "proxy.validation.sibling", + "radrootsd.validation.sibling", 1, RadrootsTransportSatisfactionPolicy::all_accepted(), vec![ @@ -1283,7 +1271,7 @@ async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() 1_700_000_000_000, 1_700_000_000_000, ) - .with_idempotency_key("proxy-local-validation-sibling"), + .with_idempotency_key("radrootsd-local-validation-sibling"), ) .await .expect("second plan"); @@ -1292,7 +1280,7 @@ async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() ._outbox .claim_next_ready_signed_event( CLAIM_OWNER, - "proxy-sibling-claim-a", + "radrootsd-sibling-claim-a", 1_700_000_060_000, 1_700_000_000_000, ) @@ -1313,25 +1301,25 @@ async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() .expect("stored before"); let ingested_before = stored_before.event_store_ingested; let ingested_at_before = stored_before.event_store_ingested_at_ms; - let reticulum_target = RadrootsTransportTarget::reticulum_preview().expect("Reticulum target"); + let reticulum_target = RadrootsTransportTarget::reticulum().expect("Reticulum target"); claimed.delivery_targets[0].transport_kind = reticulum_target.kind; claimed.delivery_targets[0].endpoint_uri = reticulum_target.uri; claimed.delivery_targets[0].endpoint_fingerprint = reticulum_target.fingerprint; let sync = sdk.sync(); - let adapter = RadrootsdProxyPublishAdapter::new( - RadrootsdProxyConfig::new(endpoint).with_timeout(Duration::from_millis(50)), + let adapter = RadrootsdPublishAdapter::new( + RadrootsdPublishConfig::new(endpoint).with_timeout(Duration::from_millis(50)), ); let error = - push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) + push_radrootsd_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) .await - .expect_err("local proxy validation error"); + .expect_err("local radrootsd validation error"); assert_no_transport_publish_request(&listener); assert!(matches!( error, RadrootsSdkError::InvalidRequest { message } - if message.contains("radrootsd proxy outbox publish") + if message.contains("radrootsd execution") && message.contains("Nostr-only") && message.contains("reticulum target") )); @@ -1387,7 +1375,7 @@ async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() ._outbox .claim_next_ready_signed_event( CLAIM_OWNER, - "proxy-sibling-claim-b", + "radrootsd-sibling-claim-b", 1_700_000_060_000, 1_700_000_000_000, ) @@ -1397,44 +1385,44 @@ async fn proxy_local_validation_failure_keeps_sibling_plan_ready_and_claimable() assert_eq!(sibling_claim.active_delivery_plan_id, Some(sibling_plan_id)); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_completion_updates_outbox_for_success_retryable_and_terminal_receipts() { +async fn radrootsd_completion_updates_outbox_for_success_retryable_and_terminal_receipts() { let cases = [ ( - "proxy-complete-success", + "radrootsd-complete-success", PushOutboxEventState::Published, PushOutboxEventState::Published, RadrootsOutboxDeliveryTargetStatus::Accepted, TransportPublishOutcomeKind::Accepted, ), ( - "proxy-complete-retryable", + "radrootsd-complete-retryable", PushOutboxEventState::PublishRetryable, PushOutboxEventState::PublishRetryable, RadrootsOutboxDeliveryTargetStatus::FailedRetryable, TransportPublishOutcomeKind::Timeout, ), ( - "proxy-complete-terminal", + "radrootsd-complete-terminal", PushOutboxEventState::FailedTerminal, PushOutboxEventState::FailedTerminal, RadrootsOutboxDeliveryTargetStatus::FailedTerminal, TransportPublishOutcomeKind::Blocked, ), ( - "proxy-complete-deferred", + "radrootsd-complete-deferred", PushOutboxEventState::DeferredUntilImplemented, PushOutboxEventState::DeferredUntilImplemented, RadrootsOutboxDeliveryTargetStatus::DeferredUntilImplemented, TransportPublishOutcomeKind::DeferredUntilImplemented, ), ( - "proxy-complete-preview-unavailable", - PushOutboxEventState::PreviewUnavailable, - PushOutboxEventState::PreviewUnavailable, - RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable, - TransportPublishOutcomeKind::PreviewUnavailable, + "radrootsd-complete-deferred-until-implemented", + PushOutboxEventState::DeferredUntilImplemented, + PushOutboxEventState::DeferredUntilImplemented, + RadrootsOutboxDeliveryTargetStatus::DeferredUntilImplemented, + TransportPublishOutcomeKind::DeferredUntilImplemented, ), ]; @@ -1446,8 +1434,8 @@ async fn proxy_completion_updates_outbox_for_success_retryable_and_terminal_rece outcome_kind, ) in cases { - let (sdk, claimed) = claimed_proxy_event(d_tag).await; - let publish = proxy_job( + let (sdk, claimed) = claimed_radrootsd_event(d_tag).await; + let publish = radrootsd_job( claimed .signed_event .as_ref() @@ -1475,13 +1463,14 @@ async fn proxy_completion_updates_outbox_for_success_retryable_and_terminal_rece publish.event_kind, claimed.signed_event.as_ref().expect("signed event").kind() ); - let proxy_receipt = - push_proxy_event_receipt(claimed.outbox_event_id, publish.clone()).expect("receipt"); - assert_eq!(proxy_receipt.final_state, expected_receipt_state); + let radrootsd_receipt = + push_radrootsd_event_receipt(claimed.outbox_event_id, publish.clone()) + .expect("receipt"); + assert_eq!(radrootsd_receipt.final_state, expected_receipt_state); let sync = sdk.sync(); - complete_proxy_publish_attempt(&sync, &claimed, &publish, 60_000, 1_700_000_000_000) + complete_radrootsd_publish_attempt(&sync, &claimed, &publish, 60_000, 1_700_000_000_000) .await - .expect("complete proxy attempt"); + .expect("complete radrootsd attempt"); let stored = sdk ._outbox .get_event(claimed.outbox_event_id) @@ -1502,9 +1491,9 @@ async fn proxy_completion_updates_outbox_for_success_retryable_and_terminal_rece } } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_completion_matches_duplicate_endpoint_targets_by_scope() { +async fn radrootsd_completion_matches_duplicate_endpoint_targets_by_scope() { let sdk = crate::RadrootsClient::builder() .fixed_clock(crate::RadrootsSdkTimestamp::from_unix_seconds( 1_700_000_000, @@ -1512,8 +1501,8 @@ async fn proxy_completion_matches_duplicate_endpoint_targets_by_scope() { .build() .await .expect("sdk"); - let draft = proxy_frozen_draft("proxy-complete-scoped-targets"); - let signed_event = ProxyFixtureSigner::new() + let draft = radrootsd_frozen_draft("radrootsd-complete-scoped-targets"); + let signed_event = RadrootsdFixtureSigner::new() .sign_frozen_draft(&draft) .expect("signed event"); let farm_a = RadrootsTransportTarget::nostr_relay_with_metadata( @@ -1532,11 +1521,11 @@ async fn proxy_completion_matches_duplicate_endpoint_targets_by_scope() { ._outbox .enqueue_signed_operation( RadrootsOutboxSignedOperationInput::new( - "sync.proxy.unit.v1", + "sync.radrootsd.unit.v1", draft, signed_event.clone(), RadrootsOutboxDeliveryPlanInput::new( - "proxy.scoped", + "radrootsd.scoped", 2, RadrootsTransportSatisfactionPolicy::all_accepted(), vec![farm_a, farm_b], @@ -1545,15 +1534,15 @@ async fn proxy_completion_matches_duplicate_endpoint_targets_by_scope() { 1_700_000_000_000, 1_700_000_000_000, ) - .with_idempotency_key("proxy-complete-scoped-targets"), + .with_idempotency_key("radrootsd-complete-scoped-targets"), ) .await - .expect("scoped proxy event"); + .expect("scoped radrootsd event"); let claimed = sdk ._outbox .claim_next_ready_signed_event( CLAIM_OWNER, - "proxy-scoped-target-claim", + "radrootsd-scoped-target-claim", 1_700_000_060_000, 1_700_000_000_000, ) @@ -1561,7 +1550,7 @@ async fn proxy_completion_matches_duplicate_endpoint_targets_by_scope() { .expect("claim") .expect("claim"); assert_eq!(claimed.outbox_event_id, enqueue.outbox_event_id); - let mut publish = proxy_job(signed_event.id_str(), TransportPublishOutcomeKind::Accepted); + let mut publish = radrootsd_job(signed_event.id_str(), TransportPublishOutcomeKind::Accepted); publish.target_policy = TransportPublishTargetPolicy::explicit_targets(vec![ TransportPublishTarget::nostr("wss://relay.example.com") .with_scope("farm.a") @@ -1589,9 +1578,9 @@ async fn proxy_completion_matches_duplicate_endpoint_targets_by_scope() { publish.targets.push(farm_b_outcome); let sync = sdk.sync(); - complete_proxy_publish_attempt(&sync, &claimed, &publish, 60_000, 1_700_000_000_000) + complete_radrootsd_publish_attempt(&sync, &claimed, &publish, 60_000, 1_700_000_000_000) .await - .expect("complete scoped proxy attempt"); + .expect("complete scoped radrootsd attempt"); let targets = sdk ._outbox .delivery_targets(claimed.outbox_event_id) @@ -1616,11 +1605,11 @@ async fn proxy_completion_matches_duplicate_endpoint_targets_by_scope() { ); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[tokio::test] -async fn proxy_completion_rejects_duplicate_daemon_outcome_before_local_mutation() { - let (sdk, claimed) = claimed_proxy_event("proxy-complete-duplicate-outcome").await; - let mut publish = proxy_job( +async fn radrootsd_completion_rejects_duplicate_daemon_outcome_before_local_mutation() { + let (sdk, claimed) = claimed_radrootsd_event("radrootsd-complete-duplicate-outcome").await; + let mut publish = radrootsd_job( claimed .signed_event .as_ref() @@ -1632,7 +1621,7 @@ async fn proxy_completion_rejects_duplicate_daemon_outcome_before_local_mutation let sync = sdk.sync(); let error = - complete_proxy_publish_attempt(&sync, &claimed, &publish, 60_000, 1_700_000_000_000) + complete_radrootsd_publish_attempt(&sync, &claimed, &publish, 60_000, 1_700_000_000_000) .await .expect_err("duplicate daemon outcome must fail closed"); @@ -1653,17 +1642,17 @@ async fn proxy_completion_rejects_duplicate_daemon_outcome_before_local_mutation ); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[test] -fn push_proxy_event_receipt_preserves_daemon_target_metadata() { - let mut publish = proxy_job( +fn push_radrootsd_event_receipt_preserves_daemon_target_metadata() { + let mut publish = radrootsd_job( "a".repeat(64).as_str(), TransportPublishOutcomeKind::Accepted, ); publish.targets[0].target_scope = Some("farm.local".to_owned()); publish.targets[0].target_label = Some("Farm relay".to_owned()); - let receipt = push_proxy_event_receipt(1, publish).expect("receipt"); + let receipt = push_radrootsd_event_receipt(1, publish).expect("receipt"); assert_eq!(receipt.targets.len(), 1); assert_eq!( @@ -1676,12 +1665,12 @@ fn push_proxy_event_receipt_preserves_daemon_target_metadata() { ); } -#[cfg(feature = "radrootsd-proxy")] +#[cfg(feature = "radrootsd-execution")] #[test] -fn push_proxy_event_receipt_returns_typed_error_for_invalid_daemon_event_id() { - let error = push_proxy_event_receipt( +fn push_radrootsd_event_receipt_returns_typed_error_for_invalid_daemon_event_id() { + let error = push_radrootsd_event_receipt( 1, - proxy_job( + radrootsd_job( "not-a-valid-event-id", TransportPublishOutcomeKind::Accepted, ), diff --git a/crates/sdk/tests/unit/transport_tests.rs b/crates/sdk/tests/unit/transport_tests.rs @@ -1,11 +1,11 @@ use super::{ - MeshScopeId, NostrProfile, NostrRelayUrlPolicy, PublishMode, ReticulumPreviewAgentEndpoint, - ReticulumPreviewBehavior, ReticulumPreviewProfile, SatisfactionPolicy, TargetPolicy, TargetSet, + MeshScopeId, NostrProfile, NostrRelayUrlPolicy, PublishMode, ReticulumAgentEndpoint, + ReticulumBehavior, ReticulumProfile, SatisfactionPolicy, TargetPolicy, TargetSet, TransportProfile, }; use crate::{RadrootsSdkError, SDK_TRANSPORT_TARGET_MAX_COUNT}; use radroots_transport::{ - RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RadrootsTransportError, RadrootsTransportKind, + RADROOTS_RETICULUM_ENDPOINT_URI, RadrootsTransportError, RadrootsTransportKind, RadrootsTransportTarget, RadrootsTransportTargetFingerprint, RadrootsTransportTargetUri, }; @@ -223,30 +223,27 @@ fn target_sets_reject_duplicate_transport_fingerprints() { } #[test] -fn reticulum_preview_profile_uses_canonical_endpoint_and_behavior_names() { - let profile = ReticulumPreviewProfile::preview_unavailable(); +fn reticulum_profile_uses_canonical_endpoint_and_behavior_names() { + let profile = ReticulumProfile::deferred_until_implemented(); - assert_eq!( - profile.endpoint_uri(), - RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI - ); + assert_eq!(profile.endpoint_uri(), RADROOTS_RETICULUM_ENDPOINT_URI); assert_eq!( profile.behavior(), - ReticulumPreviewBehavior::RejectDeliveryAttempts + ReticulumBehavior::RejectDeliveryAttempts ); assert_eq!( - ReticulumPreviewBehavior::RejectDeliveryAttempts.as_str(), + ReticulumBehavior::RejectDeliveryAttempts.as_str(), "reject_delivery_attempts" ); assert_eq!( - ReticulumPreviewBehavior::DeferDeliveryPlans.as_str(), + ReticulumBehavior::DeferDeliveryPlans.as_str(), "defer_delivery_plans" ); assert_eq!( serde_json::to_value(profile).expect("profile json"), serde_json::json!({ - "endpoint_uri": "reticulum:preview-unavailable", - "scope": "local_preview", + "endpoint_uri": "reticulum:local", + "scope": "local", "agent_endpoint": null, "behavior": "reject_delivery_attempts" }) @@ -254,11 +251,11 @@ fn reticulum_preview_profile_uses_canonical_endpoint_and_behavior_names() { } #[test] -fn reticulum_preview_profile_preserves_explicit_scope_and_agent_endpoint() { - let profile = ReticulumPreviewProfile::preview_unavailable() +fn reticulum_profile_preserves_explicit_scope_and_agent_endpoint() { + let profile = ReticulumProfile::deferred_until_implemented() .with_scope(MeshScopeId::parse("farmers_market").expect("scope")) .with_agent_endpoint( - ReticulumPreviewAgentEndpoint::parse("reticulum-agent:local").expect("agent endpoint"), + ReticulumAgentEndpoint::parse("reticulum-agent:local").expect("agent endpoint"), ); assert_eq!(profile.scope().as_str(), "farmers_market"); @@ -269,7 +266,7 @@ fn reticulum_preview_profile_preserves_explicit_scope_and_agent_endpoint() { assert_eq!( serde_json::to_value(profile).expect("profile json"), serde_json::json!({ - "endpoint_uri": "reticulum:preview-unavailable", + "endpoint_uri": "reticulum:local", "scope": "farmers_market", "agent_endpoint": "reticulum-agent:local", "behavior": "reject_delivery_attempts" @@ -278,7 +275,7 @@ fn reticulum_preview_profile_preserves_explicit_scope_and_agent_endpoint() { } #[test] -fn reticulum_preview_agent_endpoint_rejects_non_agent_endpoint_families() { +fn reticulum_agent_endpoint_rejects_non_agent_endpoint_families() { for invalid in [ "", "reticulum-agent:", @@ -290,33 +287,32 @@ fn reticulum_preview_agent_endpoint_rejects_non_agent_endpoint_families() { "ws://127.0.0.1:9735", ] { assert!(matches!( - ReticulumPreviewAgentEndpoint::parse(invalid), + ReticulumAgentEndpoint::parse(invalid), Err(RadrootsSdkError::InvalidRequest { ref message }) - if message == "Reticulum preview agent endpoint is invalid" + if message == "Reticulum agent endpoint is invalid" )); } } #[test] -fn explicit_target_sets_reject_noncanonical_reticulum_preview_endpoints() { +fn explicit_target_sets_reject_noncanonical_reticulum_endpoints() { for invalid in [ - " reticulum:preview-unavailable", - "reticulum:preview-unavailable ", - "RETICULUM:preview-unavailable", - "reticulum:Preview-Unavailable", - "reticulum:preview", - "reticulum:preview-unavailable-alt", - "reticulum:custom", + " reticulum:local".to_owned(), + "reticulum:local ".to_owned(), + "RETICULUM:deferred-until-implemented".to_owned(), + "reticulum:Preview-Unavailable".to_owned(), + ["reticulum:", "pre", "view"].concat(), + "reticulum:local-alt".to_owned(), + "reticulum:custom".to_owned(), ] { assert_eq!( - RadrootsTransportTarget::new(RadrootsTransportKind::Reticulum, invalid) + RadrootsTransportTarget::new(RadrootsTransportKind::Reticulum, invalid.as_str()) .expect_err("invalid Reticulum target"), RadrootsTransportError::InvalidTargetUri ); } - let uri = - RadrootsTransportTargetUri::parse("reticulum:preview-unavailable-alt").expect("target uri"); + let uri = RadrootsTransportTargetUri::parse("reticulum:local-alt").expect("target uri"); let fingerprint = RadrootsTransportTargetFingerprint::from_target( &RadrootsTransportKind::Reticulum, &uri, @@ -335,26 +331,6 @@ fn explicit_target_sets_reject_noncanonical_reticulum_preview_endpoints() { } #[test] -fn explicit_target_sets_reject_mixed_proxy_delegate_targets() { - let proxy = RadrootsTransportTarget::proxy("http://127.0.0.1:8080/rpc").expect("proxy target"); - let nostr = - RadrootsTransportTarget::nostr_relay("wss://relay.example.com").expect("Nostr target"); - - let error = TargetSet::transport_targets(vec![proxy.clone(), nostr]) - .expect_err("mixed proxy target set"); - - assert!(matches!( - error, - RadrootsSdkError::InvalidRequest { ref message } - if message.contains("proxy transport targets must be the only target") - )); - - let proxy_only = TargetSet::transport_targets(vec![proxy]).expect("proxy-only target set"); - assert_eq!(proxy_only.len(), 1); - assert_eq!(proxy_only.targets()[0].kind, RadrootsTransportKind::Proxy); -} - -#[test] fn normalized_relays_reject_empty_and_over_limit_sets() { assert!(matches!( TargetSet::nostr_relays(Vec::<String>::new(), NostrRelayUrlPolicy::Public), diff --git a/crates/sdk/tests/unit/workflow_runtime_tests.rs b/crates/sdk/tests/unit/workflow_runtime_tests.rs @@ -611,7 +611,7 @@ async fn enqueue_signed_workflow_reports_clock_failures() { } #[tokio::test] -async fn enqueue_signed_workflow_rejects_transport_profile_targets_without_proxy_transport() { +async fn enqueue_signed_workflow_rejects_transport_profile_targets_without_radrootsd_execution() { let sdk = crate::RadrootsClient::builder().build().await.expect("sdk"); let actor = RadrootsActorContext::test(FARMER_PUBLIC_KEY_HEX, [RadrootsActorRole::Farmer]) .expect("actor");