lib

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

commit 4f922d5cea7240d6f1750297aca1b40891ba63f2
parent 1bce0fbf74b7bbd8e1c745a909ce025bc91e9163
Author: triesap <tyson@radroots.org>
Date:   Sun,  2 Aug 2026 19:08:16 +0000

core(session): implement sign out semantics

- clear active account and profile session state
- retain saved accounts selected identity and credentials
- make repeated sign out idempotent without revision churn
- keep the resulting public snapshot free of secret material

Diffstat:
Mcrates/studio_application/src/session.rs | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/studio_storage/src/application_adapter.rs | 9+++++++++
2 files changed, 61 insertions(+), 0 deletions(-)

diff --git a/crates/studio_application/src/session.rs b/crates/studio_application/src/session.rs @@ -7,6 +7,18 @@ use crate::{ }; impl AppCore { + /// Drops the active session while retaining accounts, selection, and credentials. + /// + /// # Errors + /// + /// Returns a safe application-state error if the transition cannot be applied. + pub fn sign_out(&self) -> Result<AppSnapshot, SafeError> { + if matches!(self.snapshot().session(), crate::SessionState::SignedOut) { + return Ok(self.snapshot()); + } + self.apply_transition(StateTransition::SignOut) + } + /// Validates and prepares a saved local account before replacing the active session. /// /// # Errors @@ -195,4 +207,44 @@ mod tests { Some(first) ); } + + #[test] + fn sign_out_retains_saved_account_selection_and_credential() { + let core = AppCore::in_memory(RelayConfiguration::default()); + let accounts = InMemoryAccountRepository::default(); + let secrets = InMemorySecretStore::default(); + let journal = InMemoryOperationJournal::default(); + let profiles = EmptyProfiles; + core.bootstrap().expect("bootstrap"); + let public_key = core + .import_secret_key( + input("7e7e9c42a91bfef19fa7ea99d52d8afdb67d893a8fefba1f5cb9793f2107f6d7"), + &accounts, + &accounts, + &secrets, + &journal, + &FixedClock, + ) + .expect("import") + .account() + .public_key(); + core.activate_account( + public_key, + &accounts, + &accounts, + &profiles, + &secrets, + &FixedClock, + ) + .expect("activate"); + + let signed_out = core.sign_out().expect("sign out"); + let repeated = core.sign_out().expect("idempotent sign out"); + assert_eq!(signed_out, repeated); + assert_eq!(signed_out.session(), SessionState::SignedOut); + assert!(signed_out.active_account().is_none()); + assert_eq!(signed_out.accounts().len(), 1); + assert_eq!(signed_out.selected_account(), Some(public_key)); + assert!(secrets.contains(public_key).expect("credential retained")); + } } diff --git a/crates/studio_storage/src/application_adapter.rs b/crates/studio_storage/src/application_adapter.rs @@ -119,6 +119,15 @@ impl PersistentAppCore { ) } + /// Signs out while retaining durable account data and credentials. + /// + /// # Errors + /// + /// Returns a safe application-state error if sign out cannot complete. + pub fn sign_out(&self) -> Result<AppSnapshot, SafeError> { + self.core.sign_out() + } + #[must_use] pub const fn core(&self) -> &AppCore { &self.core