diff --git a/src/nostr/client.rs b/src/nostr/client.rs index 3a3ec39..7de639d 100644 --- a/src/nostr/client.rs +++ b/src/nostr/client.rs @@ -73,6 +73,11 @@ pub struct NostrService { /// SDK client, present while the service loop runs. client: RwLock>, + /// Handle to the service's tokio runtime. One-shot fetches (e.g. profile + /// lookups) from worker threads MUST run here, not on a throwaway runtime: + /// the relay connections (incl. the custom Nym mixnet transport) are driven + /// by this runtime, and a foreign runtime can't reach them. + rt_handle: RwLock>, /// Service thread started flag. started: AtomicBool, /// Shutdown request flag. @@ -124,6 +129,7 @@ impl NostrService { store: Arc::new(store), nostr_dir, client: RwLock::new(None), + rt_handle: RwLock::new(None), started: AtomicBool::new(false), shutdown: AtomicBool::new(false), connected: AtomicBool::new(false), @@ -185,15 +191,25 @@ impl NostrService { let client = self.client.read().clone()?; let pk = PublicKey::from_hex(hex).ok()?; let hints: Vec = hints.to_vec(); - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .ok()?; - rt.block_on(async { - // Dial the target's own relays so their kind-0 is reachable even when - // it isn't mirrored to any relay we already hold. - if !hints.is_empty() { - connect_relays(&client, &hints).await; + // Run on the SERVICE runtime — the relay connections (and the custom Nym + // mixnet transport) live there. A throwaway current-thread runtime can't + // drive them, which is why bare-npub profile lookups silently returned + // nothing even though the relay serves the kind-0 fine. + let handle = self.rt_handle.read().clone()?; + let own_relays = self.relays(); + handle.block_on(async { + // Dial the target's own relays (hints) AND our own relay set so the + // kind-0 is reachable whether it lives on their relays or ours (most + // Goblin users share relay.goblin.st). Without this, a bare-npub scan + // only queried whatever happened to be connected and often saw nothing. + let mut dial: Vec = hints.clone(); + for r in &own_relays { + if !dial.contains(r) { + dial.push(r.clone()); + } + } + if !dial.is_empty() { + connect_relays(&client, &dial).await; } let filter = Filter::new().kind(Kind::Metadata).author(pk).limit(1); let events = client @@ -660,6 +676,9 @@ impl NostrService { /// Main service loop: connect, publish identity, catch up, listen. async fn run_service(svc: Arc, wallet: Wallet) { + // Publish the service runtime handle so worker-thread one-shots (profile + // lookups) can run their fetches here, where the relay I/O actually lives. + *svc.rt_handle.write() = Some(tokio::runtime::Handle::current()); let relays = svc.relays(); info!( "nostr: starting service for {} with relays {:?}",