commit 6d5823cbb6da79f9490414be91881cf9181729d8
parent 311fac6626bfdfb14f49d50f1cd4c78b82f51e49
Author: triesap <tyson@radroots.org>
Date: Sun, 14 Jun 2026 01:30:38 -0700
outbox: remove concrete key signing orchestration
- remove the public claimed-event signing helper from outbox
- keep complete_signing as the state-machine boundary for signed events
- update outbox and relay transport tests to pre-sign drafts directly
- validate outbox, relay transport, and authority check and test lanes
Diffstat:
4 files changed, 61 insertions(+), 66 deletions(-)
diff --git a/crates/outbox/Cargo.toml b/crates/outbox/Cargo.toml
@@ -26,10 +26,6 @@ radroots_event_store = { workspace = true, default-features = false, features =
"sqlite",
"runtime-tokio",
] }
-radroots_nostr = { workspace = true, default-features = false, features = [
- "std",
- "events",
-] }
hex = { workspace = true }
serde = { workspace = true, features = ["std"] }
serde_json = { workspace = true, features = ["std"] }
@@ -38,4 +34,8 @@ sqlx = { workspace = true, optional = true, features = ["derive"] }
thiserror = { workspace = true }
[dev-dependencies]
+radroots_nostr = { workspace = true, default-features = false, features = [
+ "std",
+ "events",
+] }
tokio = { workspace = true, features = ["macros", "rt"] }
diff --git a/crates/outbox/src/error.rs b/crates/outbox/src/error.rs
@@ -10,9 +10,6 @@ pub enum RadrootsOutboxError {
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
- #[error("Nostr error: {0}")]
- Nostr(#[from] radroots_nostr::prelude::RadrootsNostrError),
-
#[error("Event store error: {0}")]
EventStore(#[from] radroots_event_store::RadrootsEventStoreError),
diff --git a/crates/outbox/src/store.rs b/crates/outbox/src/store.rs
@@ -11,7 +11,6 @@ use crate::model::{
use radroots_event_store::{RadrootsEventIngest, RadrootsEventStore};
use radroots_events::RadrootsNostrEvent;
use radroots_events::draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent};
-use radroots_nostr::prelude::{RadrootsNostrKeys, radroots_nostr_sign_frozen_draft};
use serde::Serialize;
use sha2::{Digest, Sha256};
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteQueryResult};
@@ -290,25 +289,6 @@ impl RadrootsOutbox {
Ok(signed_event)
}
- pub async fn sign_claimed_event(
- &self,
- claimed: &RadrootsOutboxClaimedEvent,
- keys: &RadrootsNostrKeys,
- now_ms: i64,
- ) -> Result<RadrootsSignedNostrEvent, RadrootsOutboxError> {
- if let Some(signed_event) = claimed.signed_event.clone() {
- return Ok(signed_event);
- }
- let signed_event = radroots_nostr_sign_frozen_draft(keys, &claimed.draft)?;
- self.complete_signing(
- claimed.outbox_event_id,
- claimed.claim_token.as_str(),
- signed_event,
- now_ms,
- )
- .await
- }
-
pub async fn mark_sign_retryable(
&self,
outbox_event_id: i64,
@@ -992,7 +972,9 @@ fn bool_i64(value: bool) -> i64 {
mod tests {
use super::*;
use radroots_events::kinds::KIND_POST;
- use radroots_nostr::prelude::RadrootsNostrSecretKey;
+ use radroots_nostr::prelude::{
+ RadrootsNostrKeys, RadrootsNostrSecretKey, radroots_nostr_sign_frozen_draft,
+ };
const FIXTURE_ALICE_SECRET_KEY_HEX: &str =
"10c5304d6c9ae3a1a16f7860f1cc8f5e3a76225a2663b3a989a0d775919b7df5";
@@ -1055,6 +1037,28 @@ mod tests {
(receipt, claimed)
}
+ async fn complete_claimed_signing(
+ outbox: &RadrootsOutbox,
+ claimed: &RadrootsOutboxClaimedEvent,
+ keys: &RadrootsNostrKeys,
+ now_ms: i64,
+ ) -> RadrootsSignedNostrEvent {
+ if let Some(signed_event) = claimed.signed_event.clone() {
+ return signed_event;
+ }
+ let signed_event =
+ radroots_nostr_sign_frozen_draft(keys, &claimed.draft).expect("signed event");
+ outbox
+ .complete_signing(
+ claimed.outbox_event_id,
+ claimed.claim_token.as_str(),
+ signed_event,
+ now_ms,
+ )
+ .await
+ .expect("complete signing")
+ }
+
async fn table_count(outbox: &RadrootsOutbox, table_name: &str) -> i64 {
let sql = format!("SELECT COUNT(*) FROM {table_name}");
sqlx::query_scalar(sql.as_str())
@@ -1363,10 +1367,7 @@ mod tests {
.await
.expect("claim")
.expect("claim");
- let signed = outbox
- .sign_claimed_event(&first_claim, &fixture_keys(), 1_050)
- .await
- .expect("sign");
+ let signed = complete_claimed_signing(&outbox, &first_claim, &fixture_keys(), 1_050).await;
outbox.recover_expired_claims(1_101).await.expect("recover");
let second_claim = outbox
.claim_next_ready_event("worker-b", "claim-b", 1_500, 1_200)
@@ -1430,10 +1431,7 @@ mod tests {
let (receipt, claimed) = enqueue_signed_fixture(&outbox).await;
let keys = fixture_keys();
- let signed = outbox
- .sign_claimed_event(&claimed, &keys, 1_100)
- .await
- .expect("sign");
+ let signed = complete_claimed_signing(&outbox, &claimed, &keys, 1_100).await;
assert_eq!(signed.id, receipt.expected_event_id);
let recovered = outbox.recover_expired_claims(2_001).await.expect("recover");
@@ -1447,10 +1445,7 @@ mod tests {
assert_eq!(publish_claim.state, RadrootsOutboxEventState::Publishing);
assert_eq!(publish_claim.signed_event.as_ref(), Some(&signed));
- let reused = outbox
- .sign_claimed_event(&publish_claim, &keys, 2_200)
- .await
- .expect("reuse signed event");
+ let reused = complete_claimed_signing(&outbox, &publish_claim, &keys, 2_200).await;
assert_eq!(reused, signed);
let event = outbox
@@ -1470,10 +1465,7 @@ mod tests {
.expect("event store");
let (receipt, claimed) = enqueue_signed_fixture(&outbox).await;
let keys = fixture_keys();
- let signed = outbox
- .sign_claimed_event(&claimed, &keys, 1_100)
- .await
- .expect("sign");
+ let signed = complete_claimed_signing(&outbox, &claimed, &keys, 1_100).await;
let first = outbox
.ingest_signed_event_local(&event_store, receipt.outbox_event_id, "claim-a", 1_200)
@@ -1646,10 +1638,7 @@ mod tests {
.await
.expect("claim")
.expect("claim");
- outbox
- .sign_claimed_event(&sign_claim, &fixture_keys(), 1_100)
- .await
- .expect("sign");
+ complete_claimed_signing(&outbox, &sign_claim, &fixture_keys(), 1_100).await;
outbox.recover_expired_claims(2_001).await.expect("recover");
outbox
.claim_next_ready_event("publisher", "publish-a", 3_000, 2_100)
diff --git a/crates/relay_transport/tests/transport.rs b/crates/relay_transport/tests/transport.rs
@@ -7,8 +7,8 @@ use radroots_nostr::prelude::{
radroots_nostr_sign_frozen_draft,
};
use radroots_outbox::{
- RadrootsOutbox, RadrootsOutboxEventState, RadrootsOutboxOperationInput,
- RadrootsOutboxOperationStatus, RadrootsOutboxRelayStatus,
+ RadrootsOutbox, RadrootsOutboxClaimedEvent, RadrootsOutboxEventState,
+ RadrootsOutboxOperationInput, RadrootsOutboxOperationStatus, RadrootsOutboxRelayStatus,
};
use radroots_relay_transport::{
RadrootsMockRelayFetchAdapter, RadrootsMockRelayPublishAdapter, RadrootsOutboxPublishPolicy,
@@ -45,6 +45,27 @@ fn signed_post(content: &str) -> RadrootsSignedNostrEvent {
radroots_nostr_sign_frozen_draft(&fixture_keys(), &draft).expect("signed event")
}
+async fn complete_claimed_signing(
+ outbox: &RadrootsOutbox,
+ claimed: &RadrootsOutboxClaimedEvent,
+ now_ms: i64,
+) -> RadrootsSignedNostrEvent {
+ if let Some(signed_event) = claimed.signed_event.clone() {
+ return signed_event;
+ }
+ let signed_event =
+ radroots_nostr_sign_frozen_draft(&fixture_keys(), &claimed.draft).expect("signed event");
+ outbox
+ .complete_signing(
+ claimed.outbox_event_id,
+ claimed.claim_token.as_str(),
+ signed_event,
+ now_ms,
+ )
+ .await
+ .expect("complete signing")
+}
+
fn unsupported_raw_event() -> String {
let event = radroots_nostr_build_event(999, "unsupported", Vec::new())
.expect("event builder")
@@ -413,10 +434,7 @@ async fn outbox_publish_persists_partial_success_and_skips_accepted_retry() {
.await
.expect("claim")
.expect("claim");
- let signed = outbox
- .sign_claimed_event(&claimed, &fixture_keys(), 1_100)
- .await
- .expect("sign");
+ let signed = complete_claimed_signing(&outbox, &claimed, 1_100).await;
outbox.recover_expired_claims(2_001).await.expect("recover");
let publish_claim = outbox
.claim_next_ready_event("publisher", "publish-a", 3_000, 2_100)
@@ -557,10 +575,7 @@ async fn outbox_publish_marks_published_without_adapter_when_all_relays_already_
.await
.expect("claim")
.expect("claim");
- let signed = outbox
- .sign_claimed_event(&claimed, &fixture_keys(), 1_100)
- .await
- .expect("sign");
+ let signed = complete_claimed_signing(&outbox, &claimed, 1_100).await;
outbox.recover_expired_claims(2_001).await.expect("recover");
let publish_claim = outbox
.claim_next_ready_event("publisher", "publish-a", 3_000, 2_100)
@@ -655,10 +670,7 @@ async fn outbox_publish_uses_persisted_accepted_count_for_explicit_quorum() {
.await
.expect("claim")
.expect("claim");
- outbox
- .sign_claimed_event(&claimed, &fixture_keys(), 1_100)
- .await
- .expect("sign");
+ complete_claimed_signing(&outbox, &claimed, 1_100).await;
outbox.recover_expired_claims(2_001).await.expect("recover");
let publish_claim = outbox
.claim_next_ready_event("publisher", "publish-a", 3_000, 2_100)
@@ -755,10 +767,7 @@ async fn outbox_publish_marks_published_when_policy_quorum_is_met_with_failure_d
.await
.expect("claim")
.expect("claim");
- let signed = outbox
- .sign_claimed_event(&claimed, &fixture_keys(), 1_100)
- .await
- .expect("sign");
+ let signed = complete_claimed_signing(&outbox, &claimed, 1_100).await;
outbox.recover_expired_claims(2_001).await.expect("recover");
let publish_claim = outbox
.claim_next_ready_event("publisher", "publish-a", 3_000, 2_100)