myc

Self-custodial remote signer for Radroots apps
git clone https://radroots.dev/git/myc.git
Log | Files | Refs | README | LICENSE

commit d4b19949492e07ec5ec4c3767961b3052eef37d7
parent 0c47dc077bec713a3a4523b4af8f5e99452455ee
Author: triesap <tyson@radroots.org>
Date:   Fri, 27 Mar 2026 13:24:50 +0000

runtime: make identity path reporting backend aware

Diffstat:
Msrc/app/mod.rs | 16++++++++++++++--
Msrc/app/runtime.rs | 53+++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/src/app/mod.rs b/src/app/mod.rs @@ -75,8 +75,20 @@ mod tests { assert!(snapshot.state_dir.ends_with("state")); assert!(snapshot.audit_dir.ends_with("audit")); - assert!(snapshot.signer_identity_path.ends_with("identity.json")); - assert!(snapshot.user_identity_path.ends_with("user.json")); + assert!( + snapshot + .signer_identity_path + .as_ref() + .expect("filesystem signer path") + .ends_with("identity.json") + ); + assert!( + snapshot + .user_identity_path + .as_ref() + .expect("filesystem user path") + .ends_with("user.json") + ); assert_eq!( snapshot.signer_identity_source.backend.as_str(), "filesystem" diff --git a/src/app/runtime.rs b/src/app/runtime.rs @@ -57,8 +57,10 @@ pub struct MycStartupSnapshot { pub observability_bind_addr: SocketAddr, pub state_dir: PathBuf, pub audit_dir: PathBuf, - pub signer_identity_path: PathBuf, - pub user_identity_path: PathBuf, + #[serde(skip_serializing_if = "Option::is_none")] + pub signer_identity_path: Option<PathBuf>, + #[serde(skip_serializing_if = "Option::is_none")] + pub user_identity_path: Option<PathBuf>, pub signer_identity_source: MycIdentitySourceSpec, pub user_identity_source: MycIdentitySourceSpec, pub signer_state_backend: MycSignerStateBackend, @@ -94,6 +96,15 @@ pub struct MycRuntime { delivery_outbox_store: Arc<dyn MycDeliveryOutboxStore>, } +fn startup_identity_path(source: &MycIdentitySourceSpec) -> Option<PathBuf> { + source.path.clone() +} + +fn format_startup_identity_path(path: Option<&Path>) -> String { + path.map(|path| path.display().to_string()) + .unwrap_or_default() +} + impl MycRuntime { pub fn bootstrap(config: MycConfig) -> Result<Self, MycError> { config.validate()?; @@ -185,8 +196,8 @@ impl MycRuntime { observability_bind_addr: self.config.observability.bind_addr, state_dir: self.paths.state_dir.clone(), audit_dir: self.paths.audit_dir.clone(), - signer_identity_path: self.paths.signer_identity_path.clone(), - user_identity_path: self.paths.user_identity_path.clone(), + signer_identity_path: startup_identity_path(self.signer.signer_identity_source()), + user_identity_path: startup_identity_path(self.signer.user_identity_source()), signer_identity_source: self.signer.signer_identity_source().clone(), user_identity_source: self.signer.user_identity_source().clone(), signer_state_backend: self.config.persistence.signer_state_backend, @@ -214,12 +225,16 @@ impl MycRuntime { F: Future<Output = ()>, { let snapshot = self.snapshot(); + let signer_identity_path = + format_startup_identity_path(snapshot.signer_identity_path.as_deref()); + let user_identity_path = + format_startup_identity_path(snapshot.user_identity_path.as_deref()); tracing::info!( instance_name = %snapshot.instance_name, state_dir = %snapshot.state_dir.display(), audit_dir = %snapshot.audit_dir.display(), - signer_identity_path = %snapshot.signer_identity_path.display(), - user_identity_path = %snapshot.user_identity_path.display(), + signer_identity_path = %signer_identity_path, + user_identity_path = %user_identity_path, signer_identity_backend = %snapshot.signer_identity_source.backend.as_str(), user_identity_backend = %snapshot.user_identity_source.backend.as_str(), signer_keyring_account_id = snapshot.signer_identity_source.keyring_account_id.as_deref().unwrap_or(""), @@ -1226,9 +1241,11 @@ mod tests { RadrootsNostrSignerManager, RadrootsNostrSqliteSignerStore, }; - use super::MycRuntime; + use super::{MycRuntime, startup_identity_path}; use crate::audit::{MycOperationAuditKind, MycOperationAuditOutcome, MycOperationAuditRecord}; - use crate::config::{MycConfig, MycRuntimeAuditBackend, MycSignerStateBackend}; + use crate::config::{ + MycConfig, MycIdentityBackend, MycRuntimeAuditBackend, MycSignerStateBackend, + }; use crate::error::MycError; use crate::outbox::{MycDeliveryOutboxKind, MycDeliveryOutboxRecord, MycDeliveryOutboxStatus}; @@ -1582,6 +1599,26 @@ mod tests { assert!(runtime.paths().delivery_outbox_path.is_file()); } + #[test] + fn startup_identity_path_reporting_matches_backend_sources() { + let mut config = MycConfig::default(); + config.paths.signer_identity_backend = MycIdentityBackend::OsKeyring; + config.paths.signer_identity_keyring_account_id = + Some("1111111111111111111111111111111111111111111111111111111111111111".to_owned()); + config.paths.signer_identity_profile_path = Some(PathBuf::from("/tmp/signer-profile.json")); + config.paths.user_identity_backend = MycIdentityBackend::ManagedAccount; + config.paths.user_identity_path = PathBuf::from("/tmp/user-accounts.json"); + + assert_eq!( + startup_identity_path(&config.paths.signer_identity_source()), + None + ); + assert_eq!( + startup_identity_path(&config.paths.user_identity_source()), + Some(PathBuf::from("/tmp/user-accounts.json")) + ); + } + #[tokio::test] async fn startup_recovery_rejects_orphaned_signer_publish_workflow() { let temp = tempfile::tempdir().expect("tempdir");