myc

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

commit b85da271483bc07f48274a8b992279220bde7203
parent 28ba80f395ec6ffa1c5a9ea2eef2c1e91ac29764
Author: triesap <tyson@radroots.org>
Date:   Fri, 27 Mar 2026 12:53:43 +0000

persistence: reject workflow-only sqlite destinations

Diffstat:
Msrc/persistence.rs | 31++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/src/persistence.rs b/src/persistence.rs @@ -367,6 +367,7 @@ fn signer_store_state_is_empty( state.signer_identity.is_none() && state.connections.is_empty() && state.audit_records.is_empty() + && state.publish_workflows.is_empty() } fn require_existing_restore_file(path: &std::path::Path, label: String) -> Result<(), MycError> { @@ -641,11 +642,14 @@ mod tests { use nostr::PublicKey; use radroots_identity::RadrootsIdentity; use radroots_nostr_signer::prelude::{ - RadrootsNostrFileSignerStore, RadrootsNostrSignerConnectionDraft, RadrootsNostrSignerStore, - RadrootsNostrSqliteSignerStore, + RADROOTS_NOSTR_SIGNER_STORE_VERSION, RadrootsNostrFileSignerStore, + RadrootsNostrSignerConnectionDraft, RadrootsNostrSignerConnectionId, + RadrootsNostrSignerStore, RadrootsNostrSignerStoreState, RadrootsNostrSqliteSignerStore, }; - use super::{MycPersistenceImportSelection, import_json_to_sqlite}; + use super::{ + MycPersistenceImportSelection, import_json_to_sqlite, signer_store_state_is_empty, + }; use crate::app::MycRuntime; use crate::audit::{MycOperationAuditKind, MycOperationAuditOutcome, MycOperationAuditRecord}; use crate::audit_sqlite::MycSqliteOperationAuditStore; @@ -681,6 +685,27 @@ mod tests { } #[test] + fn signer_store_state_is_not_empty_when_only_publish_workflows_are_present() { + let workflow = radroots_nostr_signer::prelude::RadrootsNostrSignerPublishWorkflowRecord::new_connect_secret_finalization( + RadrootsNostrSignerConnectionId::parse("workflow-only-connection") + .expect("workflow connection id"), + 17, + ); + let state = RadrootsNostrSignerStoreState { + version: RADROOTS_NOSTR_SIGNER_STORE_VERSION, + signer_identity: None, + connections: Vec::new(), + audit_records: Vec::new(), + publish_workflows: vec![workflow], + }; + + assert!( + !signer_store_state_is_empty(&state), + "publish workflows must make the signer-state destination non-empty" + ); + } + + #[test] fn import_json_to_sqlite_moves_signer_state_and_runtime_audit() { let temp = tempfile::tempdir().expect("tempdir"); let runtime = bootstrap_json_runtime(temp.path());