From 6cce0d8bea26a4c180f356bad2a9a6502a836965 Mon Sep 17 00:00:00 2001 From: reya Date: Mon, 9 Feb 2026 10:12:21 +0700 Subject: [PATCH] set default data for newly created identity --- Cargo.lock | 2 +- crates/auto_update/src/lib.rs | 1 - crates/chat/src/room.rs | 1 - crates/common/Cargo.toml | 1 - crates/common/src/constants.rs | 28 ------ crates/common/src/lib.rs | 55 ---------- crates/coop/src/dialogs/screening.rs | 4 +- crates/coop/src/main.rs | 2 +- crates/coop/src/panels/relay_list.rs | 3 +- crates/device/src/lib.rs | 6 +- crates/person/src/lib.rs | 4 +- crates/state/Cargo.toml | 1 + crates/state/src/constants.rs | 60 +++++++++++ crates/state/src/lib.rs | 144 ++++++++++++++++----------- 14 files changed, 158 insertions(+), 154 deletions(-) delete mode 100644 crates/common/src/constants.rs create mode 100644 crates/state/src/constants.rs diff --git a/Cargo.lock b/Cargo.lock index 90d384c..6830357 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,7 +1319,6 @@ dependencies = [ "reqwest", "smallvec", "smol", - "whoami", ] [[package]] @@ -6230,6 +6229,7 @@ dependencies = [ "serde_json", "smol", "webbrowser", + "whoami", ] [[package]] diff --git a/crates/auto_update/src/lib.rs b/crates/auto_update/src/lib.rs index a525f40..f680a0a 100644 --- a/crates/auto_update/src/lib.rs +++ b/crates/auto_update/src/lib.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use std::time::Duration; use anyhow::{anyhow, Context as AnyhowContext, Error}; -use common::BOOTSTRAP_RELAYS; use gpui::http_client::{AsyncBody, HttpClient}; use gpui::{ App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, Global, Subscription, Task, diff --git a/crates/chat/src/room.rs b/crates/chat/src/room.rs index 638449c..516e785 100644 --- a/crates/chat/src/room.rs +++ b/crates/chat/src/room.rs @@ -440,7 +440,6 @@ impl Room { // Construct a direct message event // // WARNING: never sign and send this event to relays - // TODO let mut event = EventBuilder::new(Kind::PrivateDirectMessage, content) .tags(tags) .build(Keys::generate().public_key()); diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 857374e..3fd083a 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -19,5 +19,4 @@ log.workspace = true dirs = "5.0" qrcode = "0.14.1" -whoami = "1.6.1" nostr = { git = "https://github.com/rust-nostr/nostr" } diff --git a/crates/common/src/constants.rs b/crates/common/src/constants.rs deleted file mode 100644 index 9cad0a5..0000000 --- a/crates/common/src/constants.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub const CLIENT_NAME: &str = "Coop"; -pub const APP_ID: &str = "su.reya.coop"; - -/// Bootstrap Relays. -pub const BOOTSTRAP_RELAYS: [&str; 4] = [ - "wss://relay.damus.io", - "wss://relay.primal.net", - "wss://relay.nos.social", - "wss://user.kindpag.es", -]; - -/// Search Relays. -pub const SEARCH_RELAYS: [&str; 2] = ["wss://search.nos.today", "wss://relay.noswhere.com"]; - -/// Default relay for Nostr Connect -pub const NOSTR_CONNECT_RELAY: &str = "wss://relay.nsec.app"; - -/// Default retry count for fetching NIP-17 relays -pub const RELAY_RETRY: u64 = 2; - -/// Default retry count for sending messages -pub const SEND_RETRY: u64 = 10; - -/// Default timeout (in seconds) for Nostr Connect -pub const NOSTR_CONNECT_TIMEOUT: u64 = 200; - -/// Default timeout (in seconds) for Nostr Connect (Bunker) -pub const BUNKER_TIMEOUT: u64 = 30; diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 653d1dc..75511a4 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -1,66 +1,11 @@ -use std::sync::OnceLock; - -pub use constants::*; pub use debounced_delay::*; pub use display::*; pub use event::*; pub use nip96::*; -use nostr_sdk::prelude::*; pub use paths::*; -mod constants; mod debounced_delay; mod display; mod event; mod nip96; mod paths; - -static APP_NAME: OnceLock = OnceLock::new(); -static NIP65_RELAYS: OnceLock)>> = OnceLock::new(); -static NIP17_RELAYS: OnceLock> = OnceLock::new(); - -/// Get the app name -pub fn app_name() -> &'static String { - APP_NAME.get_or_init(|| { - let devicename = whoami::devicename(); - let platform = whoami::platform(); - - format!("{CLIENT_NAME} on {platform} ({devicename})") - }) -} - -/// Default NIP-65 Relays. Used for new account -pub fn default_nip65_relays() -> &'static Vec<(RelayUrl, Option)> { - NIP65_RELAYS.get_or_init(|| { - vec![ - ( - RelayUrl::parse("wss://nostr.mom").unwrap(), - Some(RelayMetadata::Read), - ), - ( - RelayUrl::parse("wss://nostr.bitcoiner.social").unwrap(), - Some(RelayMetadata::Read), - ), - ( - RelayUrl::parse("wss://nos.lol").unwrap(), - Some(RelayMetadata::Write), - ), - ( - RelayUrl::parse("wss://relay.snort.social").unwrap(), - Some(RelayMetadata::Write), - ), - (RelayUrl::parse("wss://relay.primal.net").unwrap(), None), - (RelayUrl::parse("wss://relay.damus.io").unwrap(), None), - ] - }) -} - -/// Default NIP-17 Relays. Used for new account -pub fn default_nip17_relays() -> &'static Vec { - NIP17_RELAYS.get_or_init(|| { - vec![ - RelayUrl::parse("wss://nip17.com").unwrap(), - RelayUrl::parse("wss://auth.nostr1.com").unwrap(), - ] - }) -} diff --git a/crates/coop/src/dialogs/screening.rs b/crates/coop/src/dialogs/screening.rs index e27609a..4c2e617 100644 --- a/crates/coop/src/dialogs/screening.rs +++ b/crates/coop/src/dialogs/screening.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::time::Duration; use anyhow::{Context as AnyhowContext, Error}; -use common::{shorten_pubkey, RenderedTimestamp, BOOTSTRAP_RELAYS}; +use common::{shorten_pubkey, RenderedTimestamp}; use gpui::prelude::FluentBuilder; use gpui::{ div, px, relative, rems, uniform_list, App, AppContext, Context, Div, Entity, @@ -11,7 +11,7 @@ use gpui::{ use nostr_sdk::prelude::*; use person::{Person, PersonRegistry}; use smallvec::{smallvec, SmallVec}; -use state::{NostrAddress, NostrRegistry, TIMEOUT}; +use state::{NostrAddress, NostrRegistry, BOOTSTRAP_RELAYS, TIMEOUT}; use theme::ActiveTheme; use ui::avatar::Avatar; use ui::button::{Button, ButtonVariants}; diff --git a/crates/coop/src/main.rs b/crates/coop/src/main.rs index 6a40e92..060910c 100644 --- a/crates/coop/src/main.rs +++ b/crates/coop/src/main.rs @@ -1,12 +1,12 @@ use std::sync::{Arc, Mutex}; use assets::Assets; -use common::{APP_ID, CLIENT_NAME}; use gpui::{ point, px, size, App, AppContext, Application, Bounds, KeyBinding, Menu, MenuItem, SharedString, Size, TitlebarOptions, WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions, }; +use state::{APP_ID, CLIENT_NAME}; use ui::Root; use crate::actions::Quit; diff --git a/crates/coop/src/panels/relay_list.rs b/crates/coop/src/panels/relay_list.rs index 262556b..3c2c930 100644 --- a/crates/coop/src/panels/relay_list.rs +++ b/crates/coop/src/panels/relay_list.rs @@ -2,7 +2,6 @@ use std::collections::HashSet; use std::time::Duration; use anyhow::{anyhow, Context as AnyhowContext, Error}; -use common::BOOTSTRAP_RELAYS; use dock::panel::{Panel, PanelEvent}; use gpui::prelude::FluentBuilder; use gpui::{ @@ -12,7 +11,7 @@ use gpui::{ }; use nostr_sdk::prelude::*; use smallvec::{smallvec, SmallVec}; -use state::NostrRegistry; +use state::{NostrRegistry, BOOTSTRAP_RELAYS}; use theme::ActiveTheme; use ui::button::{Button, ButtonVariants}; use ui::input::{InputEvent, InputState, TextInput}; diff --git a/crates/device/src/lib.rs b/crates/device/src/lib.rs index d94ac41..4a02aeb 100644 --- a/crates/device/src/lib.rs +++ b/crates/device/src/lib.rs @@ -3,15 +3,15 @@ use std::sync::Arc; use std::time::Duration; use anyhow::{anyhow, Context as AnyhowContext, Error}; -use common::app_name; -pub use device::*; use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task}; use nostr_sdk::prelude::*; use smallvec::{smallvec, SmallVec}; -use state::{NostrRegistry, RelayState, DEVICE_GIFTWRAP, TIMEOUT, USER_GIFTWRAP}; +use state::{app_name, NostrRegistry, RelayState, DEVICE_GIFTWRAP, TIMEOUT, USER_GIFTWRAP}; mod device; +pub use device::*; + const IDENTIFIER: &str = "coop:device"; pub fn init(cx: &mut App) { diff --git a/crates/person/src/lib.rs b/crates/person/src/lib.rs index 3e1ef3e..3d15c38 100644 --- a/crates/person/src/lib.rs +++ b/crates/person/src/lib.rs @@ -4,12 +4,12 @@ use std::rc::Rc; use std::time::Duration; use anyhow::{anyhow, Error}; -use common::{EventUtils, BOOTSTRAP_RELAYS}; +use common::EventUtils; use gpui::{App, AppContext, Context, Entity, Global, Task}; use nostr_sdk::prelude::*; pub use person::*; use smallvec::{smallvec, SmallVec}; -use state::{Announcement, NostrRegistry, TIMEOUT}; +use state::{Announcement, NostrRegistry, BOOTSTRAP_RELAYS, TIMEOUT}; mod person; diff --git a/crates/state/Cargo.toml b/crates/state/Cargo.toml index 2d04fc2..009c0a3 100644 --- a/crates/state/Cargo.toml +++ b/crates/state/Cargo.toml @@ -25,3 +25,4 @@ serde_json.workspace = true rustls = "0.23" petname = "2.0.2" +whoami = "1.6.1" diff --git a/crates/state/src/constants.rs b/crates/state/src/constants.rs new file mode 100644 index 0000000..5e54cfb --- /dev/null +++ b/crates/state/src/constants.rs @@ -0,0 +1,60 @@ +use std::sync::OnceLock; + +/// Client name (Application name) +pub const CLIENT_NAME: &str = "Coop"; + +/// COOP's public key +pub const COOP_PUBKEY: &str = "npub126kl5fruqan90py77gf6pvfvygefl2mu2ukew6xdx5pc5uqscwgsnkgarv"; + +/// App ID +pub const APP_ID: &str = "su.reya.coop"; + +/// Keyring name +pub const KEYRING: &str = "Coop Secret Storage"; + +/// Default timeout for subscription +pub const TIMEOUT: u64 = 3; + +/// Default delay for searching +pub const FIND_DELAY: u64 = 600; + +/// Default limit for searching +pub const FIND_LIMIT: usize = 20; + +/// Default timeout for Nostr Connect +pub const NOSTR_CONNECT_TIMEOUT: u64 = 200; + +/// Default Nostr Connect relay +pub const NOSTR_CONNECT_RELAY: &str = "wss://relay.nsec.app"; + +/// Default subscription id for device gift wrap events +pub const DEVICE_GIFTWRAP: &str = "device-gift-wraps"; + +/// Default subscription id for user gift wrap events +pub const USER_GIFTWRAP: &str = "user-gift-wraps"; + +/// Default vertex relays +pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"]; + +/// Default search relays +pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://search.nos.today"]; + +/// Default bootstrap relays +pub const BOOTSTRAP_RELAYS: [&str; 4] = [ + "wss://relay.damus.io", + "wss://relay.primal.net", + "wss://relay.nos.social", + "wss://user.kindpag.es", +]; + +static APP_NAME: OnceLock = OnceLock::new(); + +/// Get the app name +pub fn app_name() -> &'static String { + APP_NAME.get_or_init(|| { + let devicename = whoami::devicename(); + let platform = whoami::platform(); + + format!("{CLIENT_NAME} on {platform} ({devicename})") + }) +} diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 8b7992f..66615f4 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -4,53 +4,25 @@ use std::sync::Arc; use std::time::Duration; use anyhow::{anyhow, Context as AnyhowContext, Error}; -use common::{config_dir, CLIENT_NAME}; +use common::config_dir; use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task}; use nostr_connect::prelude::*; use nostr_gossip_memory::prelude::*; use nostr_lmdb::NostrLmdb; use nostr_sdk::prelude::*; +mod constants; mod device; mod event; -mod gossip; mod nip05; mod signer; +pub use constants::*; pub use device::*; pub use event::*; -pub use gossip::*; pub use nip05::*; pub use signer::*; -/// Default timeout for subscription -pub const TIMEOUT: u64 = 3; -/// Default delay for searching -pub const FIND_DELAY: u64 = 600; -/// Default limit for searching -pub const FIND_LIMIT: usize = 20; -/// Default timeout for Nostr Connect -pub const NOSTR_CONNECT_TIMEOUT: u64 = 200; -/// Default Nostr Connect relay -pub const NOSTR_CONNECT_RELAY: &str = "wss://relay.nsec.app"; -/// Default subscription id for device gift wrap events -pub const DEVICE_GIFTWRAP: &str = "device-gift-wraps"; -/// Default subscription id for user gift wrap events -pub const USER_GIFTWRAP: &str = "user-gift-wraps"; -/// Default avatar for new users -pub const DEFAULT_AVATAR: &str = "https://image.nostr.build/93bb6084457a42620849b6827f3f34f111ae5a4ac728638a989d4ed4b4bb3ac8.png"; -/// Default vertex relays -pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"]; -/// Default search relays -pub const SEARCH_RELAYS: [&str; 2] = ["wss://search.nos.today", "wss://relay.noswhere.com"]; -/// Default bootstrap relays -pub const BOOTSTRAP_RELAYS: [&str; 4] = [ - "wss://relay.damus.io", - "wss://relay.primal.net", - "wss://relay.nos.social", - "wss://user.kindpag.es", -]; - pub fn init(cx: &mut App) { // rustls uses the `aws_lc_rs` provider by default // This only errors if the default provider has already @@ -626,8 +598,8 @@ impl NostrRegistry { let builder = EventBuilder::metadata(&metadata); let event = client.sign_event_builder(builder).await?; - // Send event to user's write relayss - client.send_event(&event).to_nip65().await?; + // Send event to user's relays + client.send_event(&event).await?; Ok(()) }) @@ -635,7 +607,7 @@ impl NostrRegistry { /// Get local stored identity fn get_identity(&mut self, cx: &mut Context) { - let read_credential = cx.read_credentials(CLIENT_NAME); + let read_credential = cx.read_credentials(KEYRING); self.tasks.push(cx.spawn(async move |this, cx| { match read_credential.await { @@ -662,42 +634,72 @@ impl NostrRegistry { /// Create a new identity fn create_identity(&mut self, cx: &mut Context) { + let client = self.client(); let keys = Keys::generate(); + let async_keys = keys.clone(); // Get write credential task let write_credential = cx.write_credentials( - CLIENT_NAME, + KEYRING, &keys.public_key().to_hex(), &keys.secret_key().to_secret_bytes(), ); - // Update the signer - self.set_signer(keys, false, cx); + // Run async tasks in background + let task: Task> = cx.background_spawn(async move { + // Build and sign the relay list event + let relay_list = default_relay_list(); + let event = EventBuilder::relay_list(relay_list) + .sign(&async_keys) + .await?; - // Generate a unique name and avatar for the identity - let name = petname::petname(2, "-").unwrap_or("Cooper".to_string()); - let avatar = Url::parse(DEFAULT_AVATAR).unwrap(); + // Publish relay list event + client.send_event(&event).await?; - // Construct metadata for the identity - let metadata = Metadata::new() - .display_name(&name) - .name(&name) - .picture(avatar); + // Build and sign the metadata event + let name = petname::petname(2, "-").unwrap_or("Cooper".to_string()); + let avatar = Url::parse(&format!("https://avatar.vercel.sh/{name}")).unwrap(); + let metadata = Metadata::new().display_name(&name).picture(avatar); + let event = EventBuilder::metadata(&metadata).sign(&async_keys).await?; - // Update user's metadata - let task = self.set_metadata(&metadata, cx); + // Publish metadata event + client.send_event(&event).await?; - // Spawn a task to write the credentials - cx.background_spawn(async move { - if let Err(e) = task.await { - log::error!("Failed to update metadata: {}", e); - } + // Build and sign the contact list event + let contacts = vec![Contact::new(PublicKey::parse(COOP_PUBKEY).unwrap())]; + let event = EventBuilder::contact_list(contacts) + .sign(&async_keys) + .await?; - if let Err(e) = write_credential.await { - log::error!("Failed to write credentials: {}", e); - } - }) - .detach(); + // Publish contact list event + client.send_event(&event).await?; + + // Build and sign the messaging relay list event + let relays = default_messaging_relays(); + let event = EventBuilder::nip17_relay_list(relays) + .sign(&async_keys) + .await?; + + // Publish messaging relay list event + client.send_event(&event).await?; + + // Write user's credentials to the system keyring + write_credential.await?; + + Ok(()) + }); + + self.tasks.push(cx.spawn(async move |this, cx| { + // Wait for the task to complete + task.await?; + + // Update the signer + this.update(cx, |this, cx| { + this.set_signer(keys, false, cx); + })?; + + Ok(()) + })); } /// Get local stored bunker connection @@ -922,6 +924,34 @@ impl NostrRegistry { } } +fn default_relay_list() -> Vec<(RelayUrl, Option)> { + vec![ + ( + RelayUrl::parse("wss://relay.gulugulu.moe").unwrap(), + Some(RelayMetadata::Write), + ), + ( + RelayUrl::parse("wss://relay.primal.net/").unwrap(), + Some(RelayMetadata::Write), + ), + ( + RelayUrl::parse("wss://relay.primal.net/").unwrap(), + Some(RelayMetadata::Read), + ), + ( + RelayUrl::parse("wss://nos.lol/").unwrap(), + Some(RelayMetadata::Read), + ), + ] +} + +fn default_messaging_relays() -> Vec { + vec![ + RelayUrl::parse("wss://auth.nostr1.com/").unwrap(), + RelayUrl::parse("wss://nip17.com/").unwrap(), + ] +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum RelayState { #[default]