commit 144e339e75230a43b9c66c69a1a49e71cfacf3db parent f1ad4be91b0d2de3915b0e755caa760306befa76 Author: triesap <tyson@radroots.org> Date: Sun, 9 Aug 2026 18:51:40 +0000 domain: rename HarvestCircle leaf crates - move domain and preferences sources under HarvestCircle crate names - update every downstream manifest dependency and Rust import - rename the product-owned preferences API - regenerate and verify the locked workspace graph Diffstat:
56 files changed, 573 insertions(+), 564 deletions(-)
diff --git a/core/Cargo.lock b/core/Cargo.lock @@ -251,9 +251,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.91" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", @@ -490,7 +490,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -504,9 +504,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" +checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" dependencies = [ "find-msvc-tools", "shlex", @@ -842,9 +842,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" [[package]] name = "foldhash" @@ -1063,6 +1063,24 @@ dependencies = [ ] [[package]] +name = "harvestcircle_domain" +version = "0.1.0-alpha" +dependencies = [ + "bech32", + "radroots_identity", + "secrecy", + "url", + "zeroize", +] + +[[package]] +name = "harvestcircle_preferences" +version = "0.1.0-alpha" +dependencies = [ + "url", +] + +[[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1328,9 +1346,9 @@ checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -1976,7 +1994,7 @@ source = "git+https://github.com/radrootslabs/lib?rev=09065a610d95e57acdc895a14c dependencies = [ "k256", "serde", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -1990,7 +2008,7 @@ dependencies = [ "radroots_identity", "serde", "serde_json", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -2005,33 +2023,22 @@ dependencies = [ name = "radroots_studio_application" version = "0.1.0-alpha" dependencies = [ - "radroots_studio_domain", + "harvestcircle_domain", "secrecy", "tokio", ] [[package]] -name = "radroots_studio_domain" -version = "0.1.0-alpha" -dependencies = [ - "bech32", - "radroots_identity", - "secrecy", - "url", - "zeroize", -] - -[[package]] name = "radroots_studio_ffi" version = "0.1.0-alpha" dependencies = [ "directories", + "harvestcircle_domain", "nostr 0.44.1", "nostr-relay-builder", "nostr-sdk 0.44.0", "quote", "radroots_studio_application", - "radroots_studio_domain", "radroots_studio_nostr", "radroots_studio_runtime", "radroots_studio_storage", @@ -2046,33 +2053,26 @@ dependencies = [ name = "radroots_studio_nostr" version = "0.1.0-alpha" dependencies = [ + "harvestcircle_domain", "nostr 0.44.1", "nostr-relay-builder", "nostr-sdk 0.44.0", "radroots_identity", "radroots_studio_application", - "radroots_studio_domain", "radroots_transport", "radroots_transport_nostr", "tokio", ] [[package]] -name = "radroots_studio_preferences" -version = "0.1.0-alpha" -dependencies = [ - "url", -] - -[[package]] name = "radroots_studio_runtime" version = "0.1.0-alpha" dependencies = [ + "harvestcircle_domain", "nostr 0.44.1", "nostr-relay-builder", "nostr-sdk 0.44.0", "radroots_studio_application", - "radroots_studio_domain", "radroots_studio_nostr", "radroots_studio_storage", "tempfile", @@ -2086,10 +2086,10 @@ version = "0.1.0-alpha" dependencies = [ "fs2", "getrandom 0.2.17", + "harvestcircle_domain", "hmac", "keyring", "radroots_studio_application", - "radroots_studio_domain", "refinery", "rusqlite", "rustix", @@ -2201,7 +2201,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -2226,7 +2226,7 @@ dependencies = [ "regex", "rusqlite", "siphasher", - "thiserror 2.0.19", + "thiserror 2.0.20", "time", "url", "walkdir", @@ -2295,7 +2295,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" dependencies = [ "hashbrown 0.16.1", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -2771,11 +2771,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.19", + "thiserror-impl 2.0.20", ] [[package]] @@ -2791,9 +2791,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", @@ -3019,7 +3019,7 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.19", + "thiserror 2.0.20", "utf-8", ] @@ -3292,9 +3292,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -3306,9 +3306,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -3316,9 +3316,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3326,9 +3326,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", @@ -3339,18 +3339,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -3570,9 +3570,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.18.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe18fb60dc696039e738717b76eaea21e7a4489bbb1885020b43c94236d7e98a" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" dependencies = [ "async-broadcast", "async-executor", @@ -3616,14 +3616,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.18.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", "zbus_names", "zvariant", "zvariant_utils", @@ -3641,6 +3641,15 @@ dependencies = [ ] [[package]] +name = "zcheapstr" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "769381976ea05c1f0abde0ca527b02e415bccbdbad148534a2b0c1901c9ab934" +dependencies = [ + "serde", +] + +[[package]] name = "zerocopy" version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3728,40 +3737,41 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zvariant" -version = "5.13.1" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911" +checksum = "b5e28c25bd8bb8da5a1f3e7065d0c156b9ee9a7973adf78b0e35eaefdf3b1b5c" dependencies = [ "endi", "enumflags2", "serde", "winnow", + "zcheapstr", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.13.1" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12" +checksum = "d496a145685283b67e232bd9e47377f6b60ad9d51e3601b23867f77c42477f96" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.5.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" +checksum = "629d80ece222cad20fe0e8741be493c4ab166acf3b85341bdc2cdbcfd8f3c2d6" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.119", + "syn 3.0.3", "winnow", ] diff --git a/core/Cargo.toml b/core/Cargo.toml @@ -1,10 +1,10 @@ [workspace] members = [ "crates/studio_application", - "crates/studio_domain", + "crates/harvestcircle_domain", "crates/studio_ffi", "crates/studio_nostr", - "crates/studio_preferences", + "crates/harvestcircle_preferences", "crates/studio_runtime", "crates/studio_storage", "crates/studio_uniffi_bindgen", @@ -34,10 +34,10 @@ unimplemented = "deny" [workspace.dependencies] radroots_studio_application = { path = "crates/studio_application", version = "=0.1.0-alpha" } -radroots_studio_domain = { path = "crates/studio_domain", version = "=0.1.0-alpha" } +harvestcircle_domain = { path = "crates/harvestcircle_domain", version = "=0.1.0-alpha" } radroots_studio_ffi = { path = "crates/studio_ffi", version = "=0.1.0-alpha" } radroots_studio_nostr = { path = "crates/studio_nostr", version = "=0.1.0-alpha" } -radroots_studio_preferences = { path = "crates/studio_preferences", version = "=0.1.0-alpha" } +harvestcircle_preferences = { path = "crates/harvestcircle_preferences", version = "=0.1.0-alpha" } radroots_studio_runtime = { path = "crates/studio_runtime", version = "=0.1.0-alpha" } radroots_studio_storage = { path = "crates/studio_storage", version = "=0.1.0-alpha" } radroots_studio_uniffi_bindgen = { path = "crates/studio_uniffi_bindgen", version = "=0.1.0-alpha" } diff --git a/core/crates/harvestcircle_domain/Cargo.toml b/core/crates/harvestcircle_domain/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "harvestcircle_domain" +description = "Private domain model for Radroots Studio" +version = "0.1.0-alpha" +edition.workspace = true +authors.workspace = true +rust-version.workspace = true +license = "GPL-3.0-only" +repository.workspace = true +homepage.workspace = true +publish = false +include = ["src/**", "Cargo.toml"] + +[dependencies] +bech32 = "=0.11.1" +radroots_identity.workspace = true +secrecy = "=0.10.3" +url = "=2.5.8" +zeroize = "=1.9.0" + +[lints] +workspace = true diff --git a/core/crates/studio_domain/src/account.rs b/core/crates/harvestcircle_domain/src/account.rs diff --git a/core/crates/studio_domain/src/error.rs b/core/crates/harvestcircle_domain/src/error.rs diff --git a/core/crates/studio_domain/src/key.rs b/core/crates/harvestcircle_domain/src/key.rs diff --git a/core/crates/studio_domain/src/lib.rs b/core/crates/harvestcircle_domain/src/lib.rs diff --git a/core/crates/studio_domain/src/profile.rs b/core/crates/harvestcircle_domain/src/profile.rs diff --git a/core/crates/studio_domain/src/relay.rs b/core/crates/harvestcircle_domain/src/relay.rs diff --git a/core/crates/studio_domain/src/time.rs b/core/crates/harvestcircle_domain/src/time.rs diff --git a/core/crates/harvestcircle_preferences/Cargo.toml b/core/crates/harvestcircle_preferences/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "harvestcircle_preferences" +description = "Private preference model for Radroots Studio" +version = "0.1.0-alpha" +edition.workspace = true +authors.workspace = true +rust-version.workspace = true +license = "MPL-2.0" +repository.workspace = true +homepage.workspace = true +publish = false +include = ["src/**", "Cargo.toml"] + +[dependencies] +url = "=2.5.8" + +[lints] +workspace = true diff --git a/core/crates/harvestcircle_preferences/src/lib.rs b/core/crates/harvestcircle_preferences/src/lib.rs @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: MPL-2.0 +//! UI-neutral Studio preference state. +//! +//! This module carries forward the uniquely required preference behavior from +//! source commit `6074a4745be361f21bb47d4778c74a14b2d57954`. It intentionally +//! excludes that source's process-global state, sample account, and FFI layer. + +use url::Url; + +pub const PREFERENCES_SCHEMA_VERSION: u32 = 1; +const MAX_SUMMARY_BYTES: usize = 256; +const MAX_SERVER_URL_BYTES: usize = 2_048; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum UpdateChannel { + #[default] + Stable, + Preview, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HarvestCirclePreferences { + pub allow_incoming_connections: bool, + pub use_radroots_dns: bool, + pub use_radroots_subnets: bool, + pub launch_at_login: bool, + pub hide_dock_icon: bool, + pub vpn_on_demand_enabled: bool, + pub run_as_exit_node: bool, + pub allow_local_network_access: bool, + pub automatically_check_for_updates: bool, + pub update_channel: UpdateChannel, + pub last_update_check_summary: String, + pub alternate_server_url: String, +} + +impl Default for HarvestCirclePreferences { + fn default() -> Self { + Self { + allow_incoming_connections: true, + use_radroots_dns: true, + use_radroots_subnets: true, + launch_at_login: true, + hide_dock_icon: false, + vpn_on_demand_enabled: false, + run_as_exit_node: false, + allow_local_network_access: false, + automatically_check_for_updates: true, + update_channel: UpdateChannel::Stable, + last_update_check_summary: String::new(), + alternate_server_url: String::new(), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PreferencesState { + schema_version: u32, + revision: u64, + preferences: HarvestCirclePreferences, +} + +impl Default for PreferencesState { + fn default() -> Self { + Self { + schema_version: PREFERENCES_SCHEMA_VERSION, + revision: 1, + preferences: HarvestCirclePreferences::default(), + } + } +} + +impl PreferencesState { + #[must_use] + pub const fn schema_version(&self) -> u32 { + self.schema_version + } + + #[must_use] + pub const fn revision(&self) -> u64 { + self.revision + } + + #[must_use] + pub const fn preferences(&self) -> &HarvestCirclePreferences { + &self.preferences + } + + pub fn apply(&mut self, change: PreferenceChange) -> Result<bool, PreferencesError> { + let mut candidate = self.preferences.clone(); + change.apply_to(&mut candidate)?; + if candidate == self.preferences { + return Ok(false); + } + self.revision = self + .revision + .checked_add(1) + .ok_or(PreferencesError::RevisionExhausted)?; + self.preferences = candidate; + Ok(true) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PreferenceChange { + AllowIncomingConnections(bool), + UseRadrootsDns(bool), + UseRadrootsSubnets(bool), + LaunchAtLogin(bool), + HideDockIcon(bool), + VpnOnDemandEnabled(bool), + RunAsExitNode(bool), + AllowLocalNetworkAccess(bool), + AutomaticallyCheckForUpdates(bool), + UpdateChannel(UpdateChannel), + LastUpdateCheckSummary(String), + AlternateServerUrl(String), +} + +impl PreferenceChange { + fn apply_to(self, preferences: &mut HarvestCirclePreferences) -> Result<(), PreferencesError> { + match self { + Self::AllowIncomingConnections(value) => { + preferences.allow_incoming_connections = value; + } + Self::UseRadrootsDns(value) => preferences.use_radroots_dns = value, + Self::UseRadrootsSubnets(value) => preferences.use_radroots_subnets = value, + Self::LaunchAtLogin(value) => preferences.launch_at_login = value, + Self::HideDockIcon(value) => preferences.hide_dock_icon = value, + Self::VpnOnDemandEnabled(value) => preferences.vpn_on_demand_enabled = value, + Self::RunAsExitNode(value) => preferences.run_as_exit_node = value, + Self::AllowLocalNetworkAccess(value) => { + preferences.allow_local_network_access = value; + } + Self::AutomaticallyCheckForUpdates(value) => { + preferences.automatically_check_for_updates = value; + } + Self::UpdateChannel(value) => preferences.update_channel = value, + Self::LastUpdateCheckSummary(value) => { + preferences.last_update_check_summary = validated_summary(value)?; + } + Self::AlternateServerUrl(value) => { + preferences.alternate_server_url = validated_server_url(value)?; + } + } + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PreferencesError { + InvalidSummary, + InvalidAlternateServerUrl, + RevisionExhausted, +} + +fn validated_summary(value: String) -> Result<String, PreferencesError> { + let value = value.trim(); + if value.len() > MAX_SUMMARY_BYTES || value.chars().any(char::is_control) { + return Err(PreferencesError::InvalidSummary); + } + Ok(value.to_owned()) +} + +fn validated_server_url(value: String) -> Result<String, PreferencesError> { + let value = value.trim(); + if value.is_empty() { + return Ok(String::new()); + } + if value.len() > MAX_SERVER_URL_BYTES || value.chars().any(char::is_control) { + return Err(PreferencesError::InvalidAlternateServerUrl); + } + let url = Url::parse(value).map_err(|_| PreferencesError::InvalidAlternateServerUrl)?; + if url.scheme() != "https" + || url.host_str().is_none() + || !url.username().is_empty() + || url.password().is_some() + || url.fragment().is_some() + { + return Err(PreferencesError::InvalidAlternateServerUrl); + } + Ok(url.to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn defaults_preserve_the_reviewed_boolean_policy_without_sample_identity() { + let state = PreferencesState::default(); + assert_eq!(state.schema_version(), PREFERENCES_SCHEMA_VERSION); + assert_eq!(state.revision(), 1); + assert!(state.preferences().allow_incoming_connections); + assert!(state.preferences().use_radroots_dns); + assert!(state.preferences().use_radroots_subnets); + assert!(state.preferences().launch_at_login); + assert!(state.preferences().automatically_check_for_updates); + assert_eq!(state.preferences().update_channel, UpdateChannel::Stable); + assert!(state.preferences().last_update_check_summary.is_empty()); + assert!(state.preferences().alternate_server_url.is_empty()); + } + + #[test] + fn revisions_advance_only_when_a_valid_canonical_value_changes() { + let mut state = PreferencesState::default(); + assert!( + !state + .apply(PreferenceChange::HideDockIcon(false)) + .expect("unchanged value") + ); + assert_eq!(state.revision(), 1); + assert!( + state + .apply(PreferenceChange::HideDockIcon(true)) + .expect("changed value") + ); + assert_eq!(state.revision(), 2); + assert!(state.preferences().hide_dock_icon); + } + + #[test] + fn every_boolean_and_channel_change_updates_exactly_one_revision() { + let mut state = PreferencesState::default(); + let changes = [ + PreferenceChange::AllowIncomingConnections(false), + PreferenceChange::UseRadrootsDns(false), + PreferenceChange::UseRadrootsSubnets(false), + PreferenceChange::LaunchAtLogin(false), + PreferenceChange::VpnOnDemandEnabled(true), + PreferenceChange::RunAsExitNode(true), + PreferenceChange::AllowLocalNetworkAccess(true), + PreferenceChange::AutomaticallyCheckForUpdates(false), + PreferenceChange::UpdateChannel(UpdateChannel::Preview), + ]; + for (index, change) in changes.into_iter().enumerate() { + assert!(state.apply(change).expect("valid preference change")); + assert_eq!(state.revision(), index as u64 + 2); + } + let preferences = state.preferences(); + assert!(!preferences.allow_incoming_connections); + assert!(!preferences.use_radroots_dns); + assert!(!preferences.use_radroots_subnets); + assert!(!preferences.launch_at_login); + assert!(preferences.vpn_on_demand_enabled); + assert!(preferences.run_as_exit_node); + assert!(preferences.allow_local_network_access); + assert!(!preferences.automatically_check_for_updates); + assert_eq!(preferences.update_channel, UpdateChannel::Preview); + } + + #[test] + fn revision_overflow_is_rejected_without_mutation() { + let mut state = PreferencesState { + revision: u64::MAX, + ..PreferencesState::default() + }; + assert_eq!( + state.apply(PreferenceChange::HideDockIcon(true)), + Err(PreferencesError::RevisionExhausted) + ); + assert!(!state.preferences().hide_dock_icon); + } + + #[test] + fn alternate_server_is_trimmed_canonical_and_credential_free() { + let mut state = PreferencesState::default(); + state + .apply(PreferenceChange::AlternateServerUrl( + " https://example.com/api ".to_owned(), + )) + .expect("valid URL"); + assert_eq!( + state.preferences().alternate_server_url, + "https://example.com/api" + ); + for invalid in [ + "http://example.com", + "https://user@example.com", + "https://user:password@example.com", + "https://example.com/#fragment", + "not a URL", + "https://example.com/a\nb", + ] { + assert_eq!( + state.apply(PreferenceChange::AlternateServerUrl(invalid.to_owned())), + Err(PreferencesError::InvalidAlternateServerUrl) + ); + } + state + .apply(PreferenceChange::AlternateServerUrl(" ".to_owned())) + .expect("empty URL resets the override"); + assert!(state.preferences().alternate_server_url.is_empty()); + assert_eq!( + state.apply(PreferenceChange::AlternateServerUrl(format!( + "https://example.com/{}", + "x".repeat(MAX_SERVER_URL_BYTES) + ))), + Err(PreferencesError::InvalidAlternateServerUrl) + ); + } + + #[test] + fn summary_is_bounded_trimmed_and_control_free() { + let mut state = PreferencesState::default(); + state + .apply(PreferenceChange::LastUpdateCheckSummary( + " Checked today ".to_owned(), + )) + .expect("valid summary"); + assert_eq!( + state.preferences().last_update_check_summary, + "Checked today" + ); + assert_eq!( + state.apply(PreferenceChange::LastUpdateCheckSummary( + "bad\nvalue".to_owned() + )), + Err(PreferencesError::InvalidSummary) + ); + assert_eq!( + state.apply(PreferenceChange::LastUpdateCheckSummary( + "x".repeat(MAX_SUMMARY_BYTES + 1) + )), + Err(PreferencesError::InvalidSummary) + ); + } +} diff --git a/core/crates/studio_application/Cargo.toml b/core/crates/studio_application/Cargo.toml @@ -12,7 +12,7 @@ publish = false include = ["src/**", "tests/**", "Cargo.toml"] [dependencies] -radroots_studio_domain.workspace = true +harvestcircle_domain.workspace = true secrecy = "=0.10.3" tokio = { version = "=1.47.1", features = ["macros", "rt-multi-thread", "sync", "time"] } diff --git a/core/crates/studio_application/src/accounts.rs b/core/crates/studio_application/src/accounts.rs @@ -7,7 +7,7 @@ use crate::{ OperationId, OperationJournal, OperationPriorState, PendingAccountOperation, RemovalConfirmationToken, SecretStore, StagedGeneratedKey, StateTransition, }; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, Nsec, PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, }; @@ -734,7 +734,7 @@ impl OperationJournal for InMemoryOperationJournal { &self, kind: AccountOperationKind, subject: PublicKey, - updated_at: radroots_studio_domain::UnixTimestamp, + updated_at: harvestcircle_domain::UnixTimestamp, ) -> Result<OperationId, SafeError> { let mut state = self .state @@ -757,7 +757,7 @@ impl OperationJournal for InMemoryOperationJournal { &self, id: OperationId, phase: AccountOperationPhase, - updated_at: radroots_studio_domain::UnixTimestamp, + updated_at: harvestcircle_domain::UnixTimestamp, diagnostic: Option<OperationDiagnostic>, ) -> Result<(), SafeError> { let mut state = self @@ -923,7 +923,7 @@ const fn operation_conflict() -> SafeError { mod tests { use std::sync::atomic::{AtomicBool, Ordering}; - use radroots_studio_domain::{ + use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, UnixTimestamp, }; diff --git a/core/crates/studio_application/src/actor.rs b/core/crates/studio_application/src/actor.rs @@ -1,7 +1,7 @@ use std::num::{NonZeroU64, NonZeroUsize}; use std::time::Instant; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountIdentity, BindingAvailability, LocalSignerBinding, PublicKey, SafeError, SafeErrorCode, SafeMessage, }; @@ -346,8 +346,8 @@ impl LifecycleGate { const fn invalid_lifecycle_transition() -> SafeError { SafeError::new( - radroots_studio_domain::SafeErrorCode::InvalidApplicationState, - radroots_studio_domain::SafeMessage::new("The runtime lifecycle transition is invalid."), + harvestcircle_domain::SafeErrorCode::InvalidApplicationState, + harvestcircle_domain::SafeMessage::new("The runtime lifecycle transition is invalid."), ) } @@ -572,7 +572,7 @@ mod tests { use std::num::NonZeroUsize; use std::time::{Duration, Instant}; - use radroots_studio_domain::{AccountIdentity, BindingAvailability, LocalSignerBinding}; + use harvestcircle_domain::{AccountIdentity, BindingAvailability, LocalSignerBinding}; use crate::{ ActorMailbox, CommandContext, CommandReceipt, CommandRejection, CommandResult, @@ -620,7 +620,7 @@ mod tests { ForegroundSessionBinding::new( identity.clone(), LocalSignerBinding::new( - radroots_studio_domain::PublicKey::from_hex( + harvestcircle_domain::PublicKey::from_hex( "e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af", ) .expect("different valid public key"), @@ -746,9 +746,9 @@ mod tests { #[test] fn blocked_degraded_fatal_and_closed_states_fail_safe() { - let problem = radroots_studio_domain::SafeError::new( - radroots_studio_domain::SafeErrorCode::StorageUnavailable, - radroots_studio_domain::SafeMessage::new("The runtime is unavailable."), + let problem = harvestcircle_domain::SafeError::new( + harvestcircle_domain::SafeErrorCode::StorageUnavailable, + harvestcircle_domain::SafeMessage::new("The runtime is unavailable."), ); let mut blocked = LifecycleGate::opening(); blocked.block(problem); diff --git a/core/crates/studio_application/src/app_core.rs b/core/crates/studio_application/src/app_core.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use std::sync::{Arc, Mutex, MutexGuard}; -use radroots_studio_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp}; +use harvestcircle_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp}; use crate::{ AccountRepository, AppSnapshot, AppStateRepository, KeyMaterialProvider, RelayConfiguration, @@ -169,7 +169,7 @@ impl AppCore { return Err(account_not_found()); }; let deletes_local_credential = account.signer().availability() - != radroots_studio_domain::BindingAvailability::CredentialMissing; + != harvestcircle_domain::BindingAvailability::CredentialMissing; let id = state.next_removal_token; state.next_removal_token = id.checked_add(1).ok_or_else(invalid_application_state)?; let revision = state.state_machine.snapshot().revision(); @@ -261,7 +261,7 @@ const fn account_not_found() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_domain::{ + use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, UnixTimestamp, }; diff --git a/core/crates/studio_application/src/config.rs b/core/crates/studio_application/src/config.rs @@ -1,4 +1,4 @@ -use radroots_studio_domain::{ +use harvestcircle_domain::{ RelayDestinationPolicy, SafeError, SafeErrorCode, SafeMessage, normalize_relay_urls, }; @@ -64,7 +64,7 @@ const fn invalid_configuration() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_domain::SafeErrorCode; + use harvestcircle_domain::SafeErrorCode; use super::{RelayRuntimeMode, relay_configuration_from_value}; @@ -90,7 +90,7 @@ mod tests { let relays = configuration .relays() .iter() - .map(radroots_studio_domain::RelayUrl::as_str) + .map(harvestcircle_domain::RelayUrl::as_str) .collect::<Vec<_>>(); assert_eq!(relays, ["wss://relay.one/", "wss://relay.two/"]); } diff --git a/core/crates/studio_application/src/custody.rs b/core/crates/studio_application/src/custody.rs @@ -3,7 +3,7 @@ use std::sync::Mutex; use std::time::Duration; use crate::KeyMaterialProvider; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, Nsec, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, UnixTimestamp, }; @@ -234,7 +234,7 @@ const fn invalid_stage_expiry() -> SafeError { mod tests { use std::num::NonZeroU64; - use radroots_studio_domain::UnixTimestamp; + use harvestcircle_domain::UnixTimestamp; use super::{GENERATED_KEY_STAGE_TTL, GeneratedKeyStage, RecoveryStageId}; use crate::test_support::TestKeyMaterialProvider; diff --git a/core/crates/studio_application/src/ports.rs b/core/crates/studio_application/src/ports.rs @@ -2,7 +2,7 @@ use std::future::Future; use std::pin::Pin; use std::time::Instant; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountSummary, BindingAvailability, Kind0ProfileCandidate, Npub, Nsec, PublicKey, RelayUrl, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, UnixTimestamp, }; @@ -707,7 +707,7 @@ mod tests { use std::sync::Mutex; - use radroots_studio_domain::{AccountSummary, PublicKey, RelayUrl, SafeError, UnixTimestamp}; + use harvestcircle_domain::{AccountSummary, PublicKey, RelayUrl, SafeError, UnixTimestamp}; use super::{ AccountNamespaceRepository, AccountOperationKind, AccountOperationPhase, diff --git a/core/crates/studio_application/src/profile_refresh.rs b/core/crates/studio_application/src/profile_refresh.rs @@ -1,4 +1,4 @@ -use radroots_studio_domain::{PublicKey, RelayUrl, SafeError, SafeErrorCode}; +use harvestcircle_domain::{PublicKey, RelayUrl, SafeError, SafeErrorCode}; use std::time::Instant; use crate::{ @@ -166,7 +166,7 @@ impl AppCore { &self, plan: &ProfileRefreshPlan, current_active: ActiveAccountSnapshot, - candidate: Option<radroots_studio_domain::Kind0ProfileCandidate>, + candidate: Option<harvestcircle_domain::Kind0ProfileCandidate>, completeness: RelayFetchCompleteness, profiles: &(impl ProfileRepository + ?Sized), clock: &(impl Clock + ?Sized), @@ -223,14 +223,14 @@ impl AppCore { const fn partial_relay_result() -> SafeError { SafeError::new( SafeErrorCode::RelayConnectionFailed, - radroots_studio_domain::SafeMessage::new("One or more Nostr relays did not complete."), + harvestcircle_domain::SafeMessage::new("One or more Nostr relays did not complete."), ) } const fn invalid_profile_completion() -> SafeError { SafeError::new( SafeErrorCode::InvalidApplicationState, - radroots_studio_domain::SafeMessage::new("The active profile refresh is no longer valid."), + harvestcircle_domain::SafeMessage::new("The active profile refresh is no longer valid."), ) } @@ -254,7 +254,7 @@ mod tests { use std::sync::Mutex; use std::time::{Duration, Instant}; - use radroots_studio_domain::{ + use harvestcircle_domain::{ EventId, Kind0ProfileCandidate, ProfileMetadata, PublicKey, RelayDestinationPolicy, RelayUrl, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, UnixTimestamp, select_latest_kind0, diff --git a/core/crates/studio_application/src/recovery.rs b/core/crates/studio_application/src/recovery.rs @@ -1,4 +1,4 @@ -use radroots_studio_domain::{PublicKey, SafeError}; +use harvestcircle_domain::{PublicKey, SafeError}; use crate::{ AccountOperationKind, AccountOperationPhase, AccountRepository, AppCore, AppStateRepository, @@ -153,7 +153,7 @@ fn recover_durable_addition( let metadata = accounts.find_account(account)?; let committed = metadata.as_ref().is_some_and(|saved| { saved.signer().availability() - == radroots_studio_domain::BindingAvailability::Available + == harvestcircle_domain::BindingAvailability::Available }); if committed { operations.advance_durable_operation( @@ -281,7 +281,7 @@ fn recover_removal( match secrets.delete(public_key) { Ok(()) => {} Err(error) - if error.code() == radroots_studio_domain::SafeErrorCode::CredentialMissing => {} + if error.code() == harvestcircle_domain::SafeErrorCode::CredentialMissing => {} Err(error) => return Err(error), } journal.update_operation( @@ -324,8 +324,7 @@ fn recover_addition( match secrets.delete(operation.subject()) { Ok(()) => {} Err(error) - if error.code() == radroots_studio_domain::SafeErrorCode::CredentialMissing => { - } + if error.code() == harvestcircle_domain::SafeErrorCode::CredentialMissing => {} Err(error) => return Err(error), } journal.update_operation( @@ -341,7 +340,7 @@ fn recover_addition( } fn removal_fallback( - registry: &[radroots_studio_domain::AccountSummary], + registry: &[harvestcircle_domain::AccountSummary], selected: Option<PublicKey>, removed: PublicKey, ) -> Option<PublicKey> { @@ -354,14 +353,14 @@ fn removal_fallback( registry .get(index + 1) .or_else(|| index.checked_sub(1).and_then(|before| registry.get(before))) - .map(radroots_studio_domain::AccountSummary::public_key) + .map(harvestcircle_domain::AccountSummary::public_key) } #[cfg(test)] pub(crate) mod tests { use std::sync::{Mutex, MutexGuard}; - use radroots_studio_domain::{ + use harvestcircle_domain::{ BindingAvailability, PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, UnixTimestamp, }; diff --git a/core/crates/studio_application/src/secrets.rs b/core/crates/studio_application/src/secrets.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use std::sync::{Mutex, MutexGuard}; -use radroots_studio_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput}; +use harvestcircle_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput}; use secrecy::{ExposeSecret, SecretString}; pub trait SecretStore: Send + Sync { @@ -202,7 +202,7 @@ const fn keyring_unavailable() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_domain::{PublicKey, SafeErrorCode, SecretKeyInput}; + use harvestcircle_domain::{PublicKey, SafeErrorCode, SecretKeyInput}; use super::{FailureSecretStore, InMemorySecretStore, SecretStore, SecretStoreOperation}; diff --git a/core/crates/studio_application/src/session.rs b/core/crates/studio_application/src/session.rs @@ -2,7 +2,7 @@ use crate::{ AccountRepository, ActiveAccountSnapshot, AppCore, AppSnapshot, AppStateRepository, Clock, ProfileLoadState, ProfileRepository, RelayConnectionState, SecretStore, StateTransition, }; -use radroots_studio_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage}; +use harvestcircle_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage}; impl AppCore { /// Drops the active session while retaining accounts, selection, and credentials. @@ -87,7 +87,7 @@ const fn invalid_credential() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_domain::{PublicKey, SafeError, SecretKeyInput, UnixTimestamp}; + use harvestcircle_domain::{PublicKey, SafeError, SecretKeyInput, UnixTimestamp}; use crate::{ AppCore, CachedProfile, Clock, InMemoryAccountRepository, InMemoryOperationJournal, @@ -195,7 +195,7 @@ mod tests { .expect_err("missing credential"); assert_eq!( error.code(), - radroots_studio_domain::SafeErrorCode::CredentialMissing + harvestcircle_domain::SafeErrorCode::CredentialMissing ); secrets .put( @@ -215,7 +215,7 @@ mod tests { .expect_err("mismatched credential"); assert_eq!( invalid.code(), - radroots_studio_domain::SafeErrorCode::InvalidSecretKey + harvestcircle_domain::SafeErrorCode::InvalidSecretKey ); assert_eq!(core.snapshot().session(), SessionState::Active); assert_eq!( diff --git a/core/crates/studio_application/src/snapshot.rs b/core/crates/studio_application/src/snapshot.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountSummary, ProfileMetadata, PublicKey, RelayUrl, SafeError, SafeErrorCode, SafeMessage, }; @@ -294,7 +294,7 @@ const fn invalid_snapshot() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_domain::{ + use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, RelayDestinationPolicy, RelayUrl, SafeErrorCode, UnixTimestamp, }; diff --git a/core/crates/studio_application/src/state_machine.rs b/core/crates/studio_application/src/state_machine.rs @@ -1,4 +1,4 @@ -use radroots_studio_domain::{AccountSummary, PublicKey, SafeError, SafeErrorCode, SafeMessage}; +use harvestcircle_domain::{AccountSummary, PublicKey, SafeError, SafeErrorCode, SafeMessage}; use crate::{ActiveAccountSnapshot, AppLifecycle, AppSnapshot, RelayConfiguration, SessionState}; @@ -356,7 +356,7 @@ const fn account_not_found() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_domain::{ + use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp, }; diff --git a/core/crates/studio_application/src/test_support.rs b/core/crates/studio_application/src/test_support.rs @@ -1,6 +1,6 @@ use std::sync::atomic::{AtomicU8, Ordering}; -use radroots_studio_domain::{ +use harvestcircle_domain::{ Npub, Nsec, PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, }; diff --git a/core/crates/studio_application/tests/redaction.rs b/core/crates/studio_application/tests/redaction.rs @@ -1,10 +1,10 @@ -use radroots_studio_application::{ - AppSnapshot, RelayConfiguration, SessionState, SnapshotRevision, -}; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp, }; +use radroots_studio_application::{ + AppSnapshot, RelayConfiguration, SessionState, SnapshotRevision, +}; const SECRET_HEX: &str = "1111111111111111111111111111111111111111111111111111111111111111"; const SECRET_NSEC: &str = "nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5"; diff --git a/core/crates/studio_domain/Cargo.toml b/core/crates/studio_domain/Cargo.toml @@ -1,22 +0,0 @@ -[package] -name = "radroots_studio_domain" -description = "Private domain model for Radroots Studio" -version = "0.1.0-alpha" -edition.workspace = true -authors.workspace = true -rust-version.workspace = true -license = "GPL-3.0-only" -repository.workspace = true -homepage.workspace = true -publish = false -include = ["src/**", "Cargo.toml"] - -[dependencies] -bech32 = "=0.11.1" -radroots_identity.workspace = true -secrecy = "=0.10.3" -url = "=2.5.8" -zeroize = "=1.9.0" - -[lints] -workspace = true diff --git a/core/crates/studio_ffi/Cargo.toml b/core/crates/studio_ffi/Cargo.toml @@ -18,7 +18,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] directories = "=6.0.0" radroots_studio_application.workspace = true -radroots_studio_domain.workspace = true +harvestcircle_domain.workspace = true radroots_studio_nostr.workspace = true radroots_studio_runtime.workspace = true radroots_studio_storage.workspace = true diff --git a/core/crates/studio_ffi/src/commands.rs b/core/crates/studio_ffi/src/commands.rs @@ -7,11 +7,11 @@ use std::sync::{Arc, Mutex, OnceLock}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use directories::ProjectDirs; +use harvestcircle_domain::{PublicKey, SafeError, SecretKeyInput, UnixTimestamp}; use radroots_studio_application::{ Clock, DurableRequestId, GeneratedKeyRecoveryHandle, RelayConfiguration, RelayRuntimeMode, RemovalConfirmationToken, relay_configuration_from_environment, }; -use radroots_studio_domain::{PublicKey, SafeError, SecretKeyInput, UnixTimestamp}; use radroots_studio_nostr::SdkNostrClient; use radroots_studio_runtime::{ RuntimeActorHandle, RuntimeDependencies, UuidInstallationIdentitySource, @@ -716,8 +716,8 @@ mod tests { use std::num::NonZeroUsize; use std::sync::Arc; + use harvestcircle_domain::SafeError; use radroots_studio_application::{InMemorySecretStore, RelayConfiguration}; - use radroots_studio_domain::SafeError; use radroots_studio_nostr::SdkNostrClient; use radroots_studio_runtime::{ RuntimeActorHandle, RuntimeDependencies, UuidInstallationIdentitySource, @@ -984,8 +984,8 @@ mod tests { ), ( generated_commit_failed(SafeError::new( - radroots_studio_domain::SafeErrorCode::StorageUnavailable, - radroots_studio_domain::SafeMessage::new("internal detail"), + harvestcircle_domain::SafeErrorCode::StorageUnavailable, + harvestcircle_domain::SafeMessage::new("internal detail"), )), WireErrorCode::StorageUnavailable, WireErrorCategory::Storage, @@ -1110,8 +1110,8 @@ mod tests { #[test] fn invalid_relay_configuration_preserves_local_startup_as_degraded() { let problem = SafeError::new( - radroots_studio_domain::SafeErrorCode::InvalidRelayConfiguration, - radroots_studio_domain::SafeMessage::new("The Nostr relay configuration is invalid."), + harvestcircle_domain::SafeErrorCode::InvalidRelayConfiguration, + harvestcircle_domain::SafeMessage::new("The Nostr relay configuration is invalid."), ); let (relays, degraded) = local_first_relay_configuration(Err(problem)); diff --git a/core/crates/studio_ffi/src/dto.rs b/core/crates/studio_ffi/src/dto.rs @@ -1,10 +1,10 @@ +use harvestcircle_domain::{ + AccountSummary, BindingAvailability, ProfileMetadata, SafeError, SafeErrorCode, +}; use radroots_studio_application::{ ActiveAccountSnapshot, AppLifecycle, AppSnapshot, ProfileLoadState, RelayConnectionState, RuntimeLifecycle, SessionState, }; -use radroots_studio_domain::{ - AccountSummary, BindingAvailability, ProfileMetadata, SafeError, SafeErrorCode, -}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(not(coverage_nightly), derive(uniffi::Enum))] @@ -210,7 +210,7 @@ impl From<&AppSnapshot> for AppSnapshotDto { accounts: snapshot.accounts().iter().map(AccountDto::from).collect(), selected_public_key_hex: snapshot .selected_account() - .map(radroots_studio_domain::PublicKey::to_hex), + .map(harvestcircle_domain::PublicKey::to_hex), session, session_subject_public_key_hex, session_error, @@ -261,7 +261,7 @@ impl From<&AccountSummary> for AccountDto { created_at_seconds: account.created_at().timestamp().as_seconds(), last_used_at_seconds: account .last_used_at() - .map(radroots_studio_domain::UnixTimestamp::as_seconds), + .map(harvestcircle_domain::UnixTimestamp::as_seconds), } } } @@ -450,7 +450,7 @@ mod tests { }; use radroots_studio_nostr::NostrKeyMaterialProvider; - use radroots_studio_domain::{BindingAvailability, SafeError, SafeErrorCode, SafeMessage}; + use harvestcircle_domain::{BindingAvailability, SafeError, SafeErrorCode, SafeMessage}; use super::{ AppLifecycleDto, AppSnapshotDto, KeyAvailabilityDto, ProfileLoadStateDto, @@ -503,7 +503,7 @@ mod tests { WireRecoveryAction::Authenticate, ), ] { - let dto = SafeErrorDto::from(radroots_studio_domain::SafeError::new( + let dto = SafeErrorDto::from(harvestcircle_domain::SafeError::new( code, SafeMessage::new("Safe compatibility failure."), )); diff --git a/core/crates/studio_ffi/src/observer.rs b/core/crates/studio_ffi/src/observer.rs @@ -215,11 +215,11 @@ mod tests { use std::sync::{Arc, Mutex}; use std::time::Duration; + use harvestcircle_domain::{RelayDestinationPolicy, RelayUrl}; use nostr::{EventBuilder, Keys, Metadata}; use nostr_relay_builder::MockRelay; use nostr_sdk::Client; use radroots_studio_application::{InMemorySecretStore, RelayConfiguration}; - use radroots_studio_domain::{RelayDestinationPolicy, RelayUrl}; use radroots_studio_nostr::SdkNostrClient; use radroots_studio_runtime::{ RuntimeActorHandle, RuntimeDependencies, UuidInstallationIdentitySource, diff --git a/core/crates/studio_nostr/Cargo.toml b/core/crates/studio_nostr/Cargo.toml @@ -15,7 +15,7 @@ include = ["src/**", "Cargo.toml"] nostr = { git = "https://github.com/rust-nostr/nostr.git", rev = "5bba5163eb77107f82c4a8262cf29d7f33a73219", package = "nostr" } nostr-sdk = { git = "https://github.com/rust-nostr/nostr.git", rev = "5bba5163eb77107f82c4a8262cf29d7f33a73219", package = "nostr-sdk" } radroots_studio_application.workspace = true -radroots_studio_domain.workspace = true +harvestcircle_domain.workspace = true radroots_identity.workspace = true radroots_transport.workspace = true radroots_transport_nostr.workspace = true diff --git a/core/crates/studio_nostr/src/client.rs b/core/crates/studio_nostr/src/client.rs @@ -1,6 +1,6 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; -use radroots_studio_domain::{ +use harvestcircle_domain::{ PublicKey, RelayDestinationPolicy, RelayUrl, SafeError, SafeErrorCode, SafeMessage, select_latest_kind0, }; @@ -172,10 +172,10 @@ const fn relay_connection_failed() -> SafeError { mod tests { use std::time::Duration; + use harvestcircle_domain::{PublicKey, RelayDestinationPolicy, RelayUrl, SafeErrorCode}; use nostr::{EventBuilder, Keys, Metadata}; use nostr_relay_builder::MockRelay; use nostr_sdk::Client; - use radroots_studio_domain::{PublicKey, RelayDestinationPolicy, RelayUrl, SafeErrorCode}; use radroots_studio_application::NostrClient; diff --git a/core/crates/studio_nostr/src/keys.rs b/core/crates/studio_nostr/src/keys.rs @@ -1,8 +1,8 @@ -use nostr::{Keys, ToBech32}; -use radroots_studio_application::{GeneratedKeyMaterial, ImportedKeyMaterial, KeyMaterialProvider}; -use radroots_studio_domain::{ +use harvestcircle_domain::{ Npub, Nsec, PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, }; +use nostr::{Keys, ToBech32}; +use radroots_studio_application::{GeneratedKeyMaterial, ImportedKeyMaterial, KeyMaterialProvider}; #[derive(Clone, Copy, Debug, Default)] pub struct NostrKeyMaterialProvider; @@ -68,7 +68,7 @@ const fn invalid_public_key() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_domain::{SafeErrorCode, SecretKeyInput}; + use harvestcircle_domain::{SafeErrorCode, SecretKeyInput}; use radroots_studio_application::KeyMaterialProvider; diff --git a/core/crates/studio_nostr/src/profile.rs b/core/crates/studio_nostr/src/profile.rs @@ -1,8 +1,8 @@ -use nostr::{Event, JsonUtil, Kind, Metadata}; -use radroots_studio_domain::{ +use harvestcircle_domain::{ EventId, Kind0ProfileCandidate, ProfileMetadata, PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp, }; +use nostr::{Event, JsonUtil, Kind, Metadata}; const MAX_EVENT_JSON_BYTES: usize = 64 * 1_024; const MAX_PROFILE_CONTENT_BYTES: usize = 16 * 1_024; @@ -67,8 +67,8 @@ const fn invalid_metadata() -> SafeError { #[cfg(test)] mod tests { + use harvestcircle_domain::{PublicKey, SafeErrorCode}; use nostr::{EventBuilder, JsonUtil, Keys, Metadata, Url}; - use radroots_studio_domain::{PublicKey, SafeErrorCode}; use super::parse_verified_kind0; diff --git a/core/crates/studio_preferences/Cargo.toml b/core/crates/studio_preferences/Cargo.toml @@ -1,18 +0,0 @@ -[package] -name = "radroots_studio_preferences" -description = "Private preference model for Radroots Studio" -version = "0.1.0-alpha" -edition.workspace = true -authors.workspace = true -rust-version.workspace = true -license = "MPL-2.0" -repository.workspace = true -homepage.workspace = true -publish = false -include = ["src/**", "Cargo.toml"] - -[dependencies] -url = "=2.5.8" - -[lints] -workspace = true diff --git a/core/crates/studio_preferences/src/lib.rs b/core/crates/studio_preferences/src/lib.rs @@ -1,328 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -//! UI-neutral Studio preference state. -//! -//! This module carries forward the uniquely required preference behavior from -//! source commit `6074a4745be361f21bb47d4778c74a14b2d57954`. It intentionally -//! excludes that source's process-global state, sample account, and FFI layer. - -use url::Url; - -pub const PREFERENCES_SCHEMA_VERSION: u32 = 1; -const MAX_SUMMARY_BYTES: usize = 256; -const MAX_SERVER_URL_BYTES: usize = 2_048; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum UpdateChannel { - #[default] - Stable, - Preview, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StudioPreferences { - pub allow_incoming_connections: bool, - pub use_radroots_dns: bool, - pub use_radroots_subnets: bool, - pub launch_at_login: bool, - pub hide_dock_icon: bool, - pub vpn_on_demand_enabled: bool, - pub run_as_exit_node: bool, - pub allow_local_network_access: bool, - pub automatically_check_for_updates: bool, - pub update_channel: UpdateChannel, - pub last_update_check_summary: String, - pub alternate_server_url: String, -} - -impl Default for StudioPreferences { - fn default() -> Self { - Self { - allow_incoming_connections: true, - use_radroots_dns: true, - use_radroots_subnets: true, - launch_at_login: true, - hide_dock_icon: false, - vpn_on_demand_enabled: false, - run_as_exit_node: false, - allow_local_network_access: false, - automatically_check_for_updates: true, - update_channel: UpdateChannel::Stable, - last_update_check_summary: String::new(), - alternate_server_url: String::new(), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PreferencesState { - schema_version: u32, - revision: u64, - preferences: StudioPreferences, -} - -impl Default for PreferencesState { - fn default() -> Self { - Self { - schema_version: PREFERENCES_SCHEMA_VERSION, - revision: 1, - preferences: StudioPreferences::default(), - } - } -} - -impl PreferencesState { - #[must_use] - pub const fn schema_version(&self) -> u32 { - self.schema_version - } - - #[must_use] - pub const fn revision(&self) -> u64 { - self.revision - } - - #[must_use] - pub const fn preferences(&self) -> &StudioPreferences { - &self.preferences - } - - pub fn apply(&mut self, change: PreferenceChange) -> Result<bool, PreferencesError> { - let mut candidate = self.preferences.clone(); - change.apply_to(&mut candidate)?; - if candidate == self.preferences { - return Ok(false); - } - self.revision = self - .revision - .checked_add(1) - .ok_or(PreferencesError::RevisionExhausted)?; - self.preferences = candidate; - Ok(true) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PreferenceChange { - AllowIncomingConnections(bool), - UseRadrootsDns(bool), - UseRadrootsSubnets(bool), - LaunchAtLogin(bool), - HideDockIcon(bool), - VpnOnDemandEnabled(bool), - RunAsExitNode(bool), - AllowLocalNetworkAccess(bool), - AutomaticallyCheckForUpdates(bool), - UpdateChannel(UpdateChannel), - LastUpdateCheckSummary(String), - AlternateServerUrl(String), -} - -impl PreferenceChange { - fn apply_to(self, preferences: &mut StudioPreferences) -> Result<(), PreferencesError> { - match self { - Self::AllowIncomingConnections(value) => { - preferences.allow_incoming_connections = value; - } - Self::UseRadrootsDns(value) => preferences.use_radroots_dns = value, - Self::UseRadrootsSubnets(value) => preferences.use_radroots_subnets = value, - Self::LaunchAtLogin(value) => preferences.launch_at_login = value, - Self::HideDockIcon(value) => preferences.hide_dock_icon = value, - Self::VpnOnDemandEnabled(value) => preferences.vpn_on_demand_enabled = value, - Self::RunAsExitNode(value) => preferences.run_as_exit_node = value, - Self::AllowLocalNetworkAccess(value) => { - preferences.allow_local_network_access = value; - } - Self::AutomaticallyCheckForUpdates(value) => { - preferences.automatically_check_for_updates = value; - } - Self::UpdateChannel(value) => preferences.update_channel = value, - Self::LastUpdateCheckSummary(value) => { - preferences.last_update_check_summary = validated_summary(value)?; - } - Self::AlternateServerUrl(value) => { - preferences.alternate_server_url = validated_server_url(value)?; - } - } - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum PreferencesError { - InvalidSummary, - InvalidAlternateServerUrl, - RevisionExhausted, -} - -fn validated_summary(value: String) -> Result<String, PreferencesError> { - let value = value.trim(); - if value.len() > MAX_SUMMARY_BYTES || value.chars().any(char::is_control) { - return Err(PreferencesError::InvalidSummary); - } - Ok(value.to_owned()) -} - -fn validated_server_url(value: String) -> Result<String, PreferencesError> { - let value = value.trim(); - if value.is_empty() { - return Ok(String::new()); - } - if value.len() > MAX_SERVER_URL_BYTES || value.chars().any(char::is_control) { - return Err(PreferencesError::InvalidAlternateServerUrl); - } - let url = Url::parse(value).map_err(|_| PreferencesError::InvalidAlternateServerUrl)?; - if url.scheme() != "https" - || url.host_str().is_none() - || !url.username().is_empty() - || url.password().is_some() - || url.fragment().is_some() - { - return Err(PreferencesError::InvalidAlternateServerUrl); - } - Ok(url.to_string()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn defaults_preserve_the_reviewed_boolean_policy_without_sample_identity() { - let state = PreferencesState::default(); - assert_eq!(state.schema_version(), PREFERENCES_SCHEMA_VERSION); - assert_eq!(state.revision(), 1); - assert!(state.preferences().allow_incoming_connections); - assert!(state.preferences().use_radroots_dns); - assert!(state.preferences().use_radroots_subnets); - assert!(state.preferences().launch_at_login); - assert!(state.preferences().automatically_check_for_updates); - assert_eq!(state.preferences().update_channel, UpdateChannel::Stable); - assert!(state.preferences().last_update_check_summary.is_empty()); - assert!(state.preferences().alternate_server_url.is_empty()); - } - - #[test] - fn revisions_advance_only_when_a_valid_canonical_value_changes() { - let mut state = PreferencesState::default(); - assert!( - !state - .apply(PreferenceChange::HideDockIcon(false)) - .expect("unchanged value") - ); - assert_eq!(state.revision(), 1); - assert!( - state - .apply(PreferenceChange::HideDockIcon(true)) - .expect("changed value") - ); - assert_eq!(state.revision(), 2); - assert!(state.preferences().hide_dock_icon); - } - - #[test] - fn every_boolean_and_channel_change_updates_exactly_one_revision() { - let mut state = PreferencesState::default(); - let changes = [ - PreferenceChange::AllowIncomingConnections(false), - PreferenceChange::UseRadrootsDns(false), - PreferenceChange::UseRadrootsSubnets(false), - PreferenceChange::LaunchAtLogin(false), - PreferenceChange::VpnOnDemandEnabled(true), - PreferenceChange::RunAsExitNode(true), - PreferenceChange::AllowLocalNetworkAccess(true), - PreferenceChange::AutomaticallyCheckForUpdates(false), - PreferenceChange::UpdateChannel(UpdateChannel::Preview), - ]; - for (index, change) in changes.into_iter().enumerate() { - assert!(state.apply(change).expect("valid preference change")); - assert_eq!(state.revision(), index as u64 + 2); - } - let preferences = state.preferences(); - assert!(!preferences.allow_incoming_connections); - assert!(!preferences.use_radroots_dns); - assert!(!preferences.use_radroots_subnets); - assert!(!preferences.launch_at_login); - assert!(preferences.vpn_on_demand_enabled); - assert!(preferences.run_as_exit_node); - assert!(preferences.allow_local_network_access); - assert!(!preferences.automatically_check_for_updates); - assert_eq!(preferences.update_channel, UpdateChannel::Preview); - } - - #[test] - fn revision_overflow_is_rejected_without_mutation() { - let mut state = PreferencesState { - revision: u64::MAX, - ..PreferencesState::default() - }; - assert_eq!( - state.apply(PreferenceChange::HideDockIcon(true)), - Err(PreferencesError::RevisionExhausted) - ); - assert!(!state.preferences().hide_dock_icon); - } - - #[test] - fn alternate_server_is_trimmed_canonical_and_credential_free() { - let mut state = PreferencesState::default(); - state - .apply(PreferenceChange::AlternateServerUrl( - " https://example.com/api ".to_owned(), - )) - .expect("valid URL"); - assert_eq!( - state.preferences().alternate_server_url, - "https://example.com/api" - ); - for invalid in [ - "http://example.com", - "https://user@example.com", - "https://user:password@example.com", - "https://example.com/#fragment", - "not a URL", - "https://example.com/a\nb", - ] { - assert_eq!( - state.apply(PreferenceChange::AlternateServerUrl(invalid.to_owned())), - Err(PreferencesError::InvalidAlternateServerUrl) - ); - } - state - .apply(PreferenceChange::AlternateServerUrl(" ".to_owned())) - .expect("empty URL resets the override"); - assert!(state.preferences().alternate_server_url.is_empty()); - assert_eq!( - state.apply(PreferenceChange::AlternateServerUrl(format!( - "https://example.com/{}", - "x".repeat(MAX_SERVER_URL_BYTES) - ))), - Err(PreferencesError::InvalidAlternateServerUrl) - ); - } - - #[test] - fn summary_is_bounded_trimmed_and_control_free() { - let mut state = PreferencesState::default(); - state - .apply(PreferenceChange::LastUpdateCheckSummary( - " Checked today ".to_owned(), - )) - .expect("valid summary"); - assert_eq!( - state.preferences().last_update_check_summary, - "Checked today" - ); - assert_eq!( - state.apply(PreferenceChange::LastUpdateCheckSummary( - "bad\nvalue".to_owned() - )), - Err(PreferencesError::InvalidSummary) - ); - assert_eq!( - state.apply(PreferenceChange::LastUpdateCheckSummary( - "x".repeat(MAX_SUMMARY_BYTES + 1) - )), - Err(PreferencesError::InvalidSummary) - ); - } -} diff --git a/core/crates/studio_runtime/Cargo.toml b/core/crates/studio_runtime/Cargo.toml @@ -13,7 +13,7 @@ include = ["src/**", "tests/**", "Cargo.toml"] [dependencies] radroots_studio_application.workspace = true -radroots_studio_domain.workspace = true +harvestcircle_domain.workspace = true radroots_studio_nostr.workspace = true radroots_studio_storage.workspace = true tokio = { version = "=1.47.1", features = ["macros", "rt-multi-thread", "sync", "time"] } diff --git a/core/crates/studio_runtime/src/installation.rs b/core/crates/studio_runtime/src/installation.rs @@ -1,4 +1,4 @@ -use radroots_studio_domain::{SafeError, SafeErrorCode, SafeMessage}; +use harvestcircle_domain::{SafeError, SafeErrorCode, SafeMessage}; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct InstallationIdentity(String); diff --git a/core/crates/studio_runtime/src/persistence.rs b/core/crates/studio_runtime/src/persistence.rs @@ -1,12 +1,12 @@ use std::path::Path; use std::sync::Arc; +use harvestcircle_domain::{PublicKey, SafeError, SecretKeyInput}; use radroots_studio_application::{ AppCore, AppSnapshot, Clock, DurableRequestId, GenerateAccountReceipt, ImportAccountReceipt, KeyMaterialProvider, RelayConfiguration, RemovalConfirmationToken, SecretStore, StagedGeneratedKey, }; -use radroots_studio_domain::{PublicKey, SafeError, SecretKeyInput}; use radroots_studio_nostr::NostrKeyMaterialProvider; use radroots_studio_storage::Database; @@ -317,6 +317,10 @@ impl PersistentAppCore { mod tests { use std::fs; + use harvestcircle_domain::{ + AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, + PublicKey, SafeErrorCode, SecretKeyInput, UnixTimestamp, + }; use radroots_studio_application::{ AccountOperationKind, AccountOperationPhase, AccountRepository, AppLifecycle, AppStateRepository, Clock, DurableOperationKind, DurableOperationPhase, @@ -324,10 +328,6 @@ mod tests { InMemorySecretStore, OperationJournal, OperationPriorState, RelayConfiguration, SecretStore, SecretStoreOperation, SessionState, }; - use radroots_studio_domain::{ - AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, - PublicKey, SafeErrorCode, SecretKeyInput, UnixTimestamp, - }; use tempfile::tempdir; use super::PersistentAppCore; diff --git a/core/crates/studio_runtime/src/runtime_actor.rs b/core/crates/studio_runtime/src/runtime_actor.rs @@ -6,6 +6,10 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; +use harvestcircle_domain::{ + AccountIdentity, BindingAvailability, LocalSignerBinding, PublicKey, SafeError, SafeErrorCode, + SafeMessage, SecretKeyInput, +}; use radroots_studio_application::{ ActorMailbox, AppSnapshot, ChangeSubscriptionId, Clock, CommandContext, CommandEnvelope, CommandReceipt, CommandResult, CommandSubmission, DurableRequestId, ForegroundSessionBinding, @@ -15,10 +19,6 @@ use radroots_studio_application::{ RuntimeLifecycle, SecretStore, SessionGeneration, SnapshotChange, SnapshotChangeReceiver, SnapshotRevision, StagedGeneratedKey, TaskCorrelation, }; -use radroots_studio_domain::{ - AccountIdentity, BindingAvailability, LocalSignerBinding, PublicKey, SafeError, SafeErrorCode, - SafeMessage, SecretKeyInput, -}; use tokio::runtime::Handle; use tokio::sync::{mpsc, oneshot, watch}; @@ -1383,15 +1383,15 @@ mod tests { use std::thread::{self, Thread}; use std::time::{Duration, Instant}; + use harvestcircle_domain::{ + AccountIdentity, BindingAvailability, LocalSignerBinding, PublicKey, + RelayDestinationPolicy, RelayUrl, SafeError, SafeErrorCode, SecretKeyInput, UnixTimestamp, + }; use radroots_studio_application::{ BoxFuture, Clock, DurableRequestId, FailureSecretStore, ForegroundSessionBinding, InMemorySecretStore, NostrClient, ProfileFetchResult, RelayConfiguration, RuntimeLifecycle, SecretStore, SecretStoreOperation, SessionGeneration, SessionState, SnapshotRevision, }; - use radroots_studio_domain::{ - AccountIdentity, BindingAvailability, LocalSignerBinding, PublicKey, - RelayDestinationPolicy, RelayUrl, SafeError, SafeErrorCode, SecretKeyInput, UnixTimestamp, - }; use super::{ DEFAULT_COMMAND_TIMEOUT, RuntimeActorHandle, RuntimeDependencies, command_unavailable, diff --git a/core/crates/studio_runtime/tests/local_relay_e2e.rs b/core/crates/studio_runtime/tests/local_relay_e2e.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use harvestcircle_domain::{RelayDestinationPolicy, RelayUrl, SecretKeyInput, UnixTimestamp}; use nostr::{EventBuilder, Keys, Metadata}; use nostr_relay_builder::MockRelay; use nostr_sdk::Client; @@ -7,7 +8,6 @@ use radroots_studio_application::{ Clock, InMemorySecretStore, ProfileLoadState, ProfileRepository, RelayConfiguration, RelayConnectionState, SecretStore, SessionState, }; -use radroots_studio_domain::{RelayDestinationPolicy, RelayUrl, SecretKeyInput, UnixTimestamp}; use radroots_studio_nostr::SdkNostrClient; use radroots_studio_runtime::PersistentAppCore; diff --git a/core/crates/studio_runtime/tests/restart_isolation.rs b/core/crates/studio_runtime/tests/restart_isolation.rs @@ -1,10 +1,10 @@ use std::fs; +use harvestcircle_domain::{SecretKeyInput, UnixTimestamp}; use radroots_studio_application::{ AccountNamespaceRepository, AccountPreferenceKey, Clock, InMemorySecretStore, RelayConfiguration, SessionState, }; -use radroots_studio_domain::{SecretKeyInput, UnixTimestamp}; use radroots_studio_runtime::PersistentAppCore; use tempfile::tempdir; diff --git a/core/crates/studio_storage/Cargo.toml b/core/crates/studio_storage/Cargo.toml @@ -15,7 +15,7 @@ include = ["src/**", "tests/**", "migrations/**", "Cargo.toml"] fs2 = "=0.4.3" keyring = "=4.1.6" radroots_studio_application.workspace = true -radroots_studio_domain.workspace = true +harvestcircle_domain.workspace = true refinery = { version = "=0.9.2", default-features = false, features = ["rusqlite"] } getrandom.workspace = true hmac.workspace = true diff --git a/core/crates/studio_storage/src/account_namespace.rs b/core/crates/studio_storage/src/account_namespace.rs @@ -1,5 +1,5 @@ +use harvestcircle_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage}; use radroots_studio_application::{AccountNamespaceRepository, AccountPreferenceKey}; -use radroots_studio_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage}; use rusqlite::{OptionalExtension, params}; use crate::Database; @@ -76,13 +76,13 @@ const fn invalid_preference() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_application::{ - AccountNamespaceRepository, AccountPreferenceKey, AccountRepository, AppStateRepository, - }; - use radroots_studio_domain::{ + use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, PublicKey, UnixTimestamp, }; + use radroots_studio_application::{ + AccountNamespaceRepository, AccountPreferenceKey, AccountRepository, AppStateRepository, + }; use crate::Database; diff --git a/core/crates/studio_storage/src/accounts.rs b/core/crates/studio_storage/src/accounts.rs @@ -1,8 +1,8 @@ -use radroots_studio_application::{AccountRepository, AppStateRepository}; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountLabel, AccountSummary, BindingAvailability, LocalSignerBinding, PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp, }; +use radroots_studio_application::{AccountRepository, AppStateRepository}; use rusqlite::{OptionalExtension, Row, params}; use crate::Database; @@ -300,11 +300,11 @@ const fn account_not_found() -> SafeError { mod tests { use std::fs; - use radroots_studio_application::{AccountRepository, AppStateRepository}; - use radroots_studio_domain::{ + use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountLabel, AccountSummary, BindingAvailability, LocalSignerBinding, PublicKey, SafeErrorCode, UnixTimestamp, }; + use radroots_studio_application::{AccountRepository, AppStateRepository}; use tempfile::tempdir; use crate::Database; diff --git a/core/crates/studio_storage/src/compatibility.rs b/core/crates/studio_storage/src/compatibility.rs @@ -1,6 +1,6 @@ use std::path::Path; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountIdentity, PersistedPublicKeyClassification, SafeError, SafeErrorCode, SafeMessage, classify_persisted_public_key, }; @@ -339,7 +339,7 @@ mod tests { scan_display_identities, scan_public_key_column, }; use crate::Database; - use radroots_studio_domain::{AccountIdentity, PublicKey, SafeErrorCode}; + use harvestcircle_domain::{AccountIdentity, PublicKey, SafeErrorCode}; #[test] fn preflight_rejects_non_files_missing_schema_zero_version_and_unknown_tables() { diff --git a/core/crates/studio_storage/src/db.rs b/core/crates/studio_storage/src/db.rs @@ -5,7 +5,7 @@ use std::sync::{Mutex, MutexGuard}; use std::time::Duration; use fs2::FileExt; -use radroots_studio_domain::{SafeError, SafeErrorCode, SafeMessage}; +use harvestcircle_domain::{SafeError, SafeErrorCode, SafeMessage}; use refinery::embed_migrations; use rusqlite::{Connection, OpenFlags}; @@ -379,8 +379,8 @@ mod tests { use tempfile::tempdir; + use harvestcircle_domain::{PublicKey, SafeErrorCode}; use radroots_studio_application::{AccountRepository, AppStateRepository}; - use radroots_studio_domain::{PublicKey, SafeErrorCode}; use refinery::Target; use rusqlite::Connection; diff --git a/core/crates/studio_storage/src/installation.rs b/core/crates/studio_storage/src/installation.rs @@ -1,4 +1,4 @@ -use radroots_studio_domain::{SafeError, SafeErrorCode, SafeMessage}; +use harvestcircle_domain::{SafeError, SafeErrorCode, SafeMessage}; use rusqlite::OptionalExtension; use crate::Database; diff --git a/core/crates/studio_storage/src/journal.rs b/core/crates/studio_storage/src/journal.rs @@ -1,12 +1,12 @@ +use harvestcircle_domain::{ + BindingAvailability, PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp, +}; use radroots_studio_application::{ AccountOperationKind, AccountOperationPhase, DurableAccountOperation, DurableOperationKind, DurableOperationPhase, DurableOperationReceipt, DurableOperationRepository, DurableOperationStart, DurableRequestId, DurableTerminalOutcome, OperationDiagnostic, OperationId, OperationJournal, OperationPriorState, PendingAccountOperation, }; -use radroots_studio_domain::{ - BindingAvailability, PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp, -}; use rusqlite::{OptionalExtension, Row, params}; use crate::Database; @@ -504,12 +504,12 @@ const fn operation_conflict() -> SafeError { #[cfg(test)] mod tests { + use harvestcircle_domain::{BindingAvailability, PublicKey, UnixTimestamp}; use radroots_studio_application::{ AccountOperationKind, AccountOperationPhase, DurableOperationKind, DurableOperationPhase, DurableOperationRepository, DurableOperationStart, DurableRequestId, DurableTerminalOutcome, OperationDiagnostic, OperationJournal, OperationPriorState, }; - use radroots_studio_domain::{BindingAvailability, PublicKey, UnixTimestamp}; use crate::Database; diff --git a/core/crates/studio_storage/src/os_keyring.rs b/core/crates/studio_storage/src/os_keyring.rs @@ -1,8 +1,8 @@ use std::sync::{Mutex, MutexGuard}; +use harvestcircle_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput}; use keyring::{Entry, Error as KeyringError}; use radroots_studio_application::SecretStore; -use radroots_studio_domain::{PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput}; use zeroize::Zeroizing; pub const CREDENTIAL_SERVICE: &str = "org.radroots.studio.nostr"; @@ -99,8 +99,8 @@ const fn keyring_unavailable() -> SafeError { #[cfg(test)] mod tests { + use harvestcircle_domain::{PublicKey, SecretKeyInput}; use radroots_studio_application::SecretStore; - use radroots_studio_domain::{PublicKey, SecretKeyInput}; use super::{CREDENTIAL_SERVICE, OsKeyringSecretStore}; diff --git a/core/crates/studio_storage/src/profiles.rs b/core/crates/studio_storage/src/profiles.rs @@ -1,8 +1,8 @@ -use radroots_studio_application::{CachedProfile, ProfileRefreshStatus, ProfileRepository}; -use radroots_studio_domain::{ +use harvestcircle_domain::{ EventId, Kind0ProfileCandidate, ProfileMetadata, PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp, }; +use radroots_studio_application::{CachedProfile, ProfileRefreshStatus, ProfileRepository}; use rusqlite::{OptionalExtension, Row, params}; use crate::Database; @@ -146,13 +146,13 @@ const fn corrupt_storage_error() -> SafeError { #[cfg(test)] mod tests { - use radroots_studio_application::{ - AccountRepository, CachedProfile, ProfileRefreshStatus, ProfileRepository, - }; - use radroots_studio_domain::{ + use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, EventId, Kind0ProfileCandidate, LocalSignerBinding, ProfileMetadata, PublicKey, UnixTimestamp, }; + use radroots_studio_application::{ + AccountRepository, CachedProfile, ProfileRefreshStatus, ProfileRepository, + }; use crate::Database; diff --git a/core/crates/studio_storage/src/recovery.rs b/core/crates/studio_storage/src/recovery.rs @@ -2,8 +2,8 @@ use std::fs::{self, File, OpenOptions}; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; +use harvestcircle_domain::{SafeError, SafeErrorCode, SafeMessage}; use hmac::{Hmac, Mac}; -use radroots_studio_domain::{SafeError, SafeErrorCode, SafeMessage}; use rusqlite::{Connection, MAIN_DB, OpenFlags}; use sha2::{Digest, Sha256}; use zeroize::Zeroizing; diff --git a/core/crates/studio_storage/src/repair.rs b/core/crates/studio_storage/src/repair.rs @@ -2,8 +2,8 @@ use std::fs::{self, File, OpenOptions}; use std::io::Read; use std::path::{Path, PathBuf}; +use harvestcircle_domain::{SafeError, SafeErrorCode, SafeMessage}; use hmac::{Hmac, Mac}; -use radroots_studio_domain::{SafeError, SafeErrorCode, SafeMessage}; use rusqlite::{Connection, MAIN_DB, OpenFlags}; use sha2::{Digest, Sha256}; use zeroize::Zeroizing; diff --git a/core/crates/studio_storage/tests/redaction.rs b/core/crates/studio_storage/tests/redaction.rs @@ -1,10 +1,10 @@ use std::fs; -use radroots_studio_application::{AccountOperationKind, AccountRepository, OperationJournal}; -use radroots_studio_domain::{ +use harvestcircle_domain::{ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding, PublicKey, UnixTimestamp, }; +use radroots_studio_application::{AccountOperationKind, AccountRepository, OperationJournal}; use radroots_studio_storage::Database; use tempfile::tempdir;