lib

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

commit 6a9cdfb1bda0181ab824967433cac737e3ceb79a
parent c5d49822d16a2653f2ec4f4e6253319f27a2dabb
Author: triesap <tyson@radroots.org>
Date:   Mon, 13 Jul 2026 06:49:46 +0000

sdk: enforce accepted proxy satisfaction

- reject non-accepted satisfaction classes before daemon proxy publish
- update proxy completion fixtures for accepted delivery protocol semantics
- cover forwarded stored seen delivered and durable proxy rejection
- refresh source-boundary witnesses for the accepted-class gate

Diffstat:
Mcrates/sdk/src/sync_runtime.rs | 13+++++++------
Mcrates/sdk/tests/source_boundary.rs | 6+++---
Mcrates/sdk/tests/unit/adapters_radrootsd_tests.rs | 4++--
Mcrates/sdk/tests/unit/sync_runtime_tests.rs | 95+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
4 files changed, 66 insertions(+), 52 deletions(-)

diff --git a/crates/sdk/src/sync_runtime.rs b/crates/sdk/src/sync_runtime.rs @@ -1208,7 +1208,7 @@ async fn proxy_delivery_policy( .iter() .filter(|target| target.delivery_plan_id == active_delivery_plan_id) .collect::<Vec<_>>(); - reject_delivered_proxy_satisfaction(&plan.satisfaction_policy)?; + reject_non_accepted_proxy_satisfaction(&plan.satisfaction_policy)?; let ready_target_count = active_targets .iter() .filter(|target| target.status.is_ready_for_attempt()) @@ -1249,7 +1249,7 @@ fn proxy_delivery_policy_from_remaining( required_remaining_targets: Option<&[RadrootsTransportTargetFingerprint]>, satisfaction_policy: &RadrootsTransportSatisfactionPolicy, ) -> Result<TransportPublishDeliveryPolicy, RadrootsSdkError> { - reject_delivered_proxy_satisfaction(satisfaction_policy)?; + reject_non_accepted_proxy_satisfaction(satisfaction_policy)?; if required_remaining == 0 { return Ok(TransportPublishDeliveryPolicy::Any); } @@ -1333,11 +1333,12 @@ fn proxy_required_remaining_targets( } #[cfg(all(feature = "runtime", feature = "radrootsd-proxy"))] -fn reject_delivered_proxy_satisfaction( +fn reject_non_accepted_proxy_satisfaction( satisfaction_policy: &RadrootsTransportSatisfactionPolicy, ) -> Result<(), RadrootsSdkError> { - if satisfaction_policy.target_satisfaction_class() - == Some(RadrootsTransportSatisfactionClass::Delivered) + if satisfaction_policy + .target_satisfaction_class() + .is_some_and(|class| class != RadrootsTransportSatisfactionClass::Accepted) { return Err(RadrootsSdkError::InvalidRequest { message: "radrootsd proxy publish only supports accepted-class satisfaction policies" @@ -1528,7 +1529,7 @@ async fn complete_proxy_delivery_target( outcome: &TransportPublishTargetOutcome, now_ms: i64, ) -> Result<(), RadrootsSdkError> { - if outcome.outcome_kind.counts_toward_satisfaction() { + if outcome.outcome_kind.counts_toward_accepted_delivery() { sync.sdk ._outbox .mark_delivery_target_accepted( diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs @@ -1621,7 +1621,7 @@ fn sdk_proxy_surfaces_reject_removed_daemon_publish_proxy_identifiers() { "assert!(!stored_before.event_store_ingested)", "assert!(!stored.event_store_ingested)", "with_timeout(Duration::from_millis(50))", - "proxy_delivery_policy_rejects_delivered_satisfaction_before_daemon_publish", + "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", @@ -1657,8 +1657,8 @@ fn sdk_proxy_surfaces_reject_removed_daemon_publish_proxy_identifiers() { "PushOutboxEventState::PreviewUnavailable", "PushOutboxTargetOutcomeKind::DeferredUntilImplemented", "PushOutboxTargetOutcomeKind::PreviewUnavailable", - "reject_delivered_proxy_satisfaction", - "RadrootsTransportSatisfactionClass::Delivered", + "reject_non_accepted_proxy_satisfaction", + "class != RadrootsTransportSatisfactionClass::Accepted", "target.target_scope.as_ref()", "outcome.target_scope.as_deref()", "target_scope: outcome.target_scope", diff --git a/crates/sdk/tests/unit/adapters_radrootsd_tests.rs b/crates/sdk/tests/unit/adapters_radrootsd_tests.rs @@ -121,7 +121,7 @@ fn publish_request() -> TransportPublishEventRequest { } fn job_status_for_outcome(outcome_kind: TransportPublishOutcomeKind) -> TransportPublishJobStatus { - if outcome_kind.counts_toward_satisfaction() { + if outcome_kind.counts_toward_accepted_delivery() { TransportPublishJobStatus::DeliverySatisfied } else if outcome_kind.is_retryable() { TransportPublishJobStatus::DeliveryUnsatisfiedRetryable @@ -159,7 +159,7 @@ fn job(outcome_kind: TransportPublishOutcomeKind) -> TransportPublishJobView { ), delivery_policy: TransportPublishDeliveryPolicy::Any, target_count: 1, - acknowledged_count: usize::from(outcome_kind.counts_toward_satisfaction()), + acknowledged_count: usize::from(outcome_kind.counts_toward_accepted_delivery()), retryable_count: usize::from(outcome_kind.is_retryable()), terminal_count: usize::from(outcome_kind.is_terminal_failure()), requested_at_ms: 1_700_000_000_000, diff --git a/crates/sdk/tests/unit/sync_runtime_tests.rs b/crates/sdk/tests/unit/sync_runtime_tests.rs @@ -51,8 +51,8 @@ use radroots_transport::{ RadrootsTransportSatisfactionPolicy, }; use radroots_transport::{ - RadrootsTransportKind, RadrootsTransportMeshScopeId, RadrootsTransportTarget, - RadrootsTransportTargetLabel, + RadrootsTransportDeliveryTargetStatus, RadrootsTransportKind, RadrootsTransportMeshScopeId, + RadrootsTransportTarget, RadrootsTransportTargetLabel, }; use radroots_transport_nostr::{ RadrootsNostrTransport, RadrootsOutboxPublishReceipt, RadrootsOutboxPublishTargetReceipt, @@ -322,7 +322,7 @@ fn delivery_target_record( #[cfg(feature = "radrootsd-proxy")] fn proxy_job(event_id: &str, outcome_kind: TransportPublishOutcomeKind) -> TransportPublishJobView { - let delivery_satisfied = outcome_kind.counts_toward_satisfaction(); + let delivery_satisfied = outcome_kind.counts_toward_accepted_delivery(); let retryable = outcome_kind.is_retryable(); let terminal_failure = outcome_kind.is_terminal_failure(); let status = if delivery_satisfied { @@ -929,49 +929,61 @@ async fn proxy_push_empty_queue_and_private_helpers_are_deterministic() { #[cfg(feature = "radrootsd-proxy")] #[tokio::test] -async fn proxy_delivery_policy_rejects_delivered_satisfaction_before_daemon_publish() { +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"); let endpoint = format!("http://{}/rpc", listener.local_addr().expect("addr")); - let (sdk, claimed) = claimed_uningested_proxy_event_with_satisfaction( - "proxy-delivered-rejected", - endpoint.as_str(), + for (index, satisfaction_policy) in [ + RadrootsTransportSatisfactionPolicy::all_forwarded(), + RadrootsTransportSatisfactionPolicy::all_stored(), + RadrootsTransportSatisfactionPolicy::all_seen(), RadrootsTransportSatisfactionPolicy::all_delivered(), - ) - .await; - let sync = sdk.sync(); - let adapter = RadrootsdProxyPublishAdapter::new( - RadrootsdProxyConfig::new(endpoint).with_timeout(Duration::from_millis(50)), - ); + RadrootsTransportSatisfactionPolicy::all_durable_or_observed(), + ] + .into_iter() + .enumerate() + { + let d_tag = format!("proxy-non-accepted-rejected-{index}"); + let (sdk, claimed) = claimed_uningested_proxy_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 error = - push_proxy_claimed_outbox_event(&sync, &adapter, &claimed, 60_000, 1_700_000_000_000) + 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"); + assert_no_transport_publish_request(&listener); + + assert!(matches!( + error, + RadrootsSdkError::InvalidRequest { message } + if message.contains("radrootsd proxy publish") + && message.contains("accepted-class satisfaction") + )); + let stored = sdk + ._outbox + .get_event(claimed.outbox_event_id) .await - .expect_err("delivered-class proxy satisfaction rejected"); - assert_no_transport_publish_request(&listener); - - assert!(matches!( - error, - RadrootsSdkError::InvalidRequest { message } - if message.contains("radrootsd proxy publish") - && message.contains("accepted-class satisfaction") - )); - let stored = sdk - ._outbox - .get_event(claimed.outbox_event_id) - .await - .expect("stored") - .expect("stored"); - assert_eq!(stored.state, RadrootsOutboxEventState::FailedTerminal); - assert!(stored.claim_token.is_none()); - assert!(!stored.event_store_ingested); - assert_eq!(stored.event_store_ingested_at_ms, None); - assert!( - stored - .last_error - .as_deref() - .expect("last error") - .contains("accepted-class satisfaction") - ); + .expect("stored") + .expect("stored"); + assert_eq!(stored.state, RadrootsOutboxEventState::FailedTerminal); + assert!(stored.claim_token.is_none()); + assert!(!stored.event_store_ingested); + assert_eq!(stored.event_store_ingested_at_ms, None); + assert!( + stored + .last_error + .as_deref() + .expect("last error") + .contains("accepted-class satisfaction") + ); + } } #[cfg(feature = "radrootsd-proxy")] @@ -1762,6 +1774,7 @@ impl OutboxPublishReceiptFixture for RadrootsOutboxPublishReceipt { target_scope: Some("farm.local".to_owned()), target_label: Some("Farm relay".to_owned()), attempted: true, + transport_status: RadrootsTransportDeliveryTargetStatus::Accepted, outcome: radroots_transport_nostr::RadrootsRelayOutcome::accepted(), }); self