lib

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

commit 419e6176bf98528f997ec7c99cda9da79e20eb69
parent 971bcdb70904040295c44b43c2ca92b3fdcd57da
Author: triesap <tyson@radroots.org>
Date:   Mon, 13 Apr 2026 05:04:35 +0000

sdk: redact secret-bearing debug output

Diffstat:
Mcrates/sdk/src/adapters/radrootsd.rs | 57++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mcrates/sdk/src/client.rs | 25++++++++++++++++++++++++-
Mcrates/sdk/src/config.rs | 11++++++++++-
Mcrates/sdk/tests/config.rs | 11+++++++++++
Mcrates/sdk/tests/radrootsd.rs | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 164 insertions(+), 8 deletions(-)

diff --git a/crates/sdk/src/adapters/radrootsd.rs b/crates/sdk/src/adapters/radrootsd.rs @@ -1,3 +1,4 @@ +use core::fmt; use core::time::Duration; use crate::RadrootsNostrEvent; @@ -9,7 +10,7 @@ use reqwest::header::{AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue}; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SdkRadrootsdSignerAuthority { pub provider_runtime_id: String, pub account_identity_id: String, @@ -17,7 +18,23 @@ pub struct SdkRadrootsdSignerAuthority { pub provider_signer_session_id: Option<String>, } -#[derive(Debug, Clone, Serialize)] +impl fmt::Debug for SdkRadrootsdSignerAuthority { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug = f.debug_struct("SdkRadrootsdSignerAuthority"); + debug.field("provider_runtime_id", &self.provider_runtime_id); + debug.field("account_identity_id", &self.account_identity_id); + debug.field( + "provider_signer_session_id", + &self + .provider_signer_session_id + .as_ref() + .map(|_| "<redacted>"), + ); + debug.finish() + } +} + +#[derive(Clone, Serialize)] pub struct SdkRadrootsdListingPublishRequest { pub listing: RadrootsListing, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -29,6 +46,18 @@ pub struct SdkRadrootsdListingPublishRequest { pub idempotency_key: Option<String>, } +impl fmt::Debug for SdkRadrootsdListingPublishRequest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug = f.debug_struct("SdkRadrootsdListingPublishRequest"); + debug.field("listing", &self.listing); + debug.field("kind", &self.kind); + debug.field("signer_session_id", &"<redacted>"); + debug.field("signer_authority", &self.signer_authority); + debug.field("idempotency_key", &self.idempotency_key); + debug.finish() + } +} + impl SdkRadrootsdListingPublishRequest { pub fn from_event( event: &RadrootsNostrEvent, @@ -57,7 +86,7 @@ pub struct SdkRadrootsdBridgePublishResponse { pub job: SdkRadrootsdBridgeJob, } -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[derive(Clone, PartialEq, Eq, Deserialize)] pub struct SdkRadrootsdBridgeJob { pub job_id: String, pub command: String, @@ -76,6 +105,28 @@ pub struct SdkRadrootsdBridgeJob { pub acknowledged_relay_count: usize, } +impl fmt::Debug for SdkRadrootsdBridgeJob { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug = f.debug_struct("SdkRadrootsdBridgeJob"); + debug.field("job_id", &self.job_id); + debug.field("command", &self.command); + debug.field("status", &self.status); + debug.field("terminal", &self.terminal); + debug.field("recovered_after_restart", &self.recovered_after_restart); + debug.field("signer_mode", &"<redacted>"); + debug.field( + "signer_session_id", + &self.signer_session_id.as_ref().map(|_| "<redacted>"), + ); + debug.field("event_kind", &self.event_kind); + debug.field("event_id", &self.event_id); + debug.field("event_addr", &self.event_addr); + debug.field("relay_count", &self.relay_count); + debug.field("acknowledged_relay_count", &self.acknowledged_relay_count); + debug.finish() + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum RadrootsdError { InvalidAuthHeader(String), diff --git a/crates/sdk/src/client.rs b/crates/sdk/src/client.rs @@ -1,5 +1,6 @@ #[cfg(not(feature = "std"))] use alloc::{string::String, vec::Vec}; +use core::fmt; #[cfg(feature = "std")] use std::{string::String, vec::Vec}; @@ -60,7 +61,7 @@ pub struct SdkRelayFailure { pub error: String, } -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Clone, PartialEq, Eq, Default)] pub struct SdkRadrootsdPublishReceipt { pub accepted: bool, pub deduplicated: bool, @@ -73,6 +74,28 @@ pub struct SdkRadrootsdPublishReceipt { pub acknowledged_relay_count: Option<usize>, } +impl fmt::Debug for SdkRadrootsdPublishReceipt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug = f.debug_struct("SdkRadrootsdPublishReceipt"); + debug.field("accepted", &self.accepted); + debug.field("deduplicated", &self.deduplicated); + debug.field("job_id", &self.job_id); + debug.field("status", &self.status); + debug.field( + "signer_mode", + &self.signer_mode.as_ref().map(|_| "<redacted>"), + ); + debug.field( + "signer_session_id", + &self.signer_session_id.as_ref().map(|_| "<redacted>"), + ); + debug.field("event_addr", &self.event_addr); + debug.field("relay_count", &self.relay_count); + debug.field("acknowledged_relay_count", &self.acknowledged_relay_count); + debug.finish() + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum SdkPublishError { Config(SdkConfigError), diff --git a/crates/sdk/src/config.rs b/crates/sdk/src/config.rs @@ -149,13 +149,22 @@ impl Default for RadrootsdConfig { } } -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Clone, PartialEq, Eq, Default)] pub enum RadrootsdAuth { #[default] None, BearerToken(String), } +impl fmt::Debug for RadrootsdAuth { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::None => f.write_str("None"), + Self::BearerToken(_) => f.write_str("BearerToken(\"<redacted>\")"), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum SignerConfig { #[default] diff --git a/crates/sdk/tests/config.rs b/crates/sdk/tests/config.rs @@ -151,3 +151,14 @@ fn retry_policy_is_explicit_and_non_ambient() { assert_eq!(config.network.retry_policy, RetryPolicy::None); } + +#[test] +fn sdk_config_debug_redacts_bearer_tokens() { + let mut config = RadrootsSdkConfig::production(); + config.radrootsd.auth = RadrootsdAuth::BearerToken("sdk-secret-token".to_owned()); + + let debug = format!("{config:?}"); + + assert!(!debug.contains("sdk-secret-token")); + assert!(debug.contains("BearerToken(\"<redacted>\")")); +} diff --git a/crates/sdk/tests/radrootsd.rs b/crates/sdk/tests/radrootsd.rs @@ -12,8 +12,9 @@ use radroots_sdk::listing::{ }; use radroots_sdk::{ RadrootsNostrEvent, RadrootsSdkClient, RadrootsSdkConfig, RadrootsdAuth, RadrootsdConfig, - SdkEnvironment, SdkPublishError, SdkRadrootsdListingPublishRequest, SdkTransportMode, - SdkTransportReceipt, SignerConfig, + SdkEnvironment, SdkPublishError, SdkRadrootsdBridgeJob, SdkRadrootsdBridgePublishResponse, + SdkRadrootsdListingPublishRequest, SdkRadrootsdPublishReceipt, SdkRadrootsdSignerAuthority, + SdkTransportMode, SdkTransportReceipt, SignerConfig, }; use serde_json::{Value, json}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; @@ -256,6 +257,65 @@ fn sdk_event( } } +#[test] +fn radrootsd_debug_redacts_signer_session_values() { + let signer_authority = SdkRadrootsdSignerAuthority { + provider_runtime_id: "runtime-1".to_owned(), + account_identity_id: "identity-1".to_owned(), + provider_signer_session_id: Some("provider-session-123".to_owned()), + }; + let request = SdkRadrootsdListingPublishRequest { + listing: sample_listing(), + kind: Some(30402), + signer_session_id: "session-123".to_owned(), + signer_authority: Some(signer_authority), + idempotency_key: Some("idem-1".to_owned()), + }; + let job = SdkRadrootsdBridgeJob { + job_id: "job-1".to_owned(), + command: "bridge.listing.publish".to_owned(), + status: "published".to_owned(), + terminal: true, + recovered_after_restart: false, + signer_mode: "nip46_session:session-123".to_owned(), + signer_session_id: Some("session-123".to_owned()), + event_kind: 30402, + event_id: Some("event-1".to_owned()), + event_addr: Some("30402:seller:listing-1".to_owned()), + relay_count: 1, + acknowledged_relay_count: 1, + }; + let response = SdkRadrootsdBridgePublishResponse { + deduplicated: false, + job, + }; + let receipt = SdkRadrootsdPublishReceipt { + accepted: true, + deduplicated: false, + job_id: Some("job-1".to_owned()), + status: Some("published".to_owned()), + signer_mode: Some("nip46_session:session-123".to_owned()), + signer_session_id: Some("session-123".to_owned()), + event_addr: Some("30402:seller:listing-1".to_owned()), + relay_count: Some(1), + acknowledged_relay_count: Some(1), + }; + + let request_debug = format!("{request:?}"); + let response_debug = format!("{response:?}"); + let receipt_debug = format!("{receipt:?}"); + + assert!(!request_debug.contains("session-123")); + assert!(!request_debug.contains("provider-session-123")); + assert!(request_debug.contains("<redacted>")); + + assert!(!response_debug.contains("session-123")); + assert!(response_debug.contains("<redacted>")); + + assert!(!receipt_debug.contains("session-123")); + assert!(receipt_debug.contains("<redacted>")); +} + #[tokio::test] async fn radrootsd_listing_publish_accepts_sdk_built_draft() -> TestResult<()> { let (server, request_rx) = JsonRpcServer::spawn( @@ -441,7 +501,9 @@ fn radrootsd_listing_request_from_event_rejects_listing_draft_kind() -> TestResu assert!(matches!( SdkRadrootsdListingPublishRequest::from_event(&event, "session-123", None, None), - Err(RadrootsTradeListingParseError::InvalidKind(KIND_LISTING_DRAFT)) + Err(RadrootsTradeListingParseError::InvalidKind( + KIND_LISTING_DRAFT + )) )); Ok(())