nostr-sdk 0.45.0

A full-featured SDK for building high-performance and reliable nostr applications.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
// Copyright (c) 2022-2023 Yuki Kishimoto
// Copyright (c) 2023-2025 Rust Nostr Developers
// Distributed under the MIT software license

//! Client builder

use std::num::NonZeroUsize;
use std::sync::Arc;
use std::time::Duration;

use nostr_database::{IntoNostrDatabase, NostrDatabase};
use nostr_gossip::{GossipAllowedRelays, IntoNostrGossip, NostrGossip};

use crate::authenticator::Authenticator;
use crate::client::Client;
use crate::events_tracker::MemoryEventsTracker;
use crate::monitor::Monitor;
use crate::policy::AdmitPolicy;
#[cfg(not(target_arch = "wasm32"))]
use crate::proxy::Proxy;
use crate::relay::{RelayLimits, SleepWhenIdle};
use crate::transport::websocket::{
    DefaultWebsocketTransport, IntoWebSocketTransport, WebSocketTransport,
};

const DEFAULT_NOTIFICATION_CHANNEL_SIZE: NonZeroUsize = NonZeroUsize::new(4096).unwrap();

/// Max number of relays to use for gossip
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GossipRelayLimits {
    /// Max number of **read** relays per user (default: 3)
    pub read_relays_per_user: u8,
    /// Max number of **write** relays per user (default: 3)
    pub write_relays_per_user: u8,
    /// Max number of **hint** relays per user (default: 1)
    pub hint_relays_per_user: u8,
    /// Max number of **most used** relays per user (default: 1)
    pub most_used_relays_per_user: u8,
    /// Max number of NIP-17 relays per user (default: 3)
    pub nip17_relays: u8,
}

impl Default for GossipRelayLimits {
    fn default() -> Self {
        Self {
            read_relays_per_user: 3,
            write_relays_per_user: 3,
            hint_relays_per_user: 1,
            most_used_relays_per_user: 1,
            nip17_relays: 3,
        }
    }
}

/// Background gossip refresh configuration.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GossipBackgroundRefresh {
    /// Interval between refresh rounds.
    pub interval: Duration,
    /// Maximum number of public keys refreshed per round and list kind.
    pub max_public_keys_per_round: NonZeroUsize,
}

impl Default for GossipBackgroundRefresh {
    fn default() -> Self {
        Self {
            interval: Duration::from_secs(5 * 60),
            max_public_keys_per_round: NonZeroUsize::new(512).unwrap(),
        }
    }
}

impl GossipBackgroundRefresh {
    /// Set refresh interval. (default: 5 min)
    pub fn interval(mut self, interval: Duration) -> Self {
        self.interval = interval;
        self
    }

    /// Set max public keys refreshed per round and list kind (default: 512)
    pub fn max_public_keys_per_round(mut self, max: NonZeroUsize) -> Self {
        self.max_public_keys_per_round = max;
        self
    }
}

/// Gossip config
#[derive(Debug, Clone)]
pub struct GossipConfig {
    /// Max number of gossip relays to use
    pub limits: GossipRelayLimits,
    /// Allowed relays during gossip selection
    pub allowed: GossipAllowedRelays,
    /// Timeout for checking if negentropy is supported, when updating gossip data
    pub sync_initial_timeout: Duration,
    /// Idle timeout when syncing gossip data
    pub sync_idle_timeout: Duration,
    /// Fetch timeout when updating gossip data (fallback of the sync)
    pub fetch_timeout: Duration,
    /// REQ chunks when fetching gossip data
    pub fetch_chunks: usize,
    /// Background refresh config
    pub background_refresh: Option<GossipBackgroundRefresh>,
}

impl Default for GossipConfig {
    fn default() -> Self {
        Self {
            limits: GossipRelayLimits::default(),
            allowed: GossipAllowedRelays::default(),
            sync_initial_timeout: Duration::from_secs(10),
            sync_idle_timeout: Duration::from_secs(10),
            fetch_timeout: Duration::from_secs(10),
            fetch_chunks: 10,
            background_refresh: Some(GossipBackgroundRefresh::default()),
        }
    }
}

impl GossipConfig {
    /// Max number of gossip relays to use
    pub fn limits(mut self, limits: GossipRelayLimits) -> Self {
        self.limits = limits;
        self
    }

    /// Allowed relays during gossip selection
    pub fn allowed(mut self, allowed: GossipAllowedRelays) -> Self {
        self.allowed = allowed;
        self
    }

    /// Timeout for checking if negentropy is supported, when updating gossip data
    pub fn sync_initial_timeout(mut self, timeout: Duration) -> Self {
        self.sync_initial_timeout = timeout;
        self
    }

    /// Idle timeout when syncing gossip data
    pub fn sync_idle_timeout(mut self, timeout: Duration) -> Self {
        self.sync_idle_timeout = timeout;
        self
    }

    /// Fetch timeout when updating gossip data (fallback of the sync)
    pub fn fetch_timeout(mut self, timeout: Duration) -> Self {
        self.fetch_timeout = timeout;
        self
    }

    /// REQ chunks when fetching gossip data
    pub fn fetch_chunks(mut self, chunks: usize) -> Self {
        self.fetch_chunks = chunks;
        self
    }

    /// Configure background refresh
    ///
    /// The refresher runs periodically and updates a limited number of keys per round,
    /// combining tracked keys with DB-seen outdated keys.
    #[inline]
    pub fn background_refresh(mut self, config: GossipBackgroundRefresh) -> Self {
        self.background_refresh = Some(config);
        self
    }

    /// Disable background refresh.
    #[inline]
    pub fn no_background_refresh(mut self) -> Self {
        self.background_refresh = None;
        self
    }
}

/// Client builder
#[derive(Debug, Clone)]
pub struct ClientBuilder {
    /// WebSocket transport
    pub websocket_transport: Arc<dyn WebSocketTransport>,
    /// Admission policy
    pub admit_policy: Option<Arc<dyn AdmitPolicy>>,
    /// Authenticator
    pub authenticator: Option<Arc<dyn Authenticator>>,
    /// Database
    pub database: Arc<dyn NostrDatabase>,
    /// Gossip
    pub gossip: Option<Arc<dyn NostrGossip>>,
    /// Gossip config
    pub gossip_config: GossipConfig,
    /// Relay monitor
    pub monitor: Option<Monitor>,
    /// Proxy
    #[cfg(not(target_arch = "wasm32"))]
    pub proxy: Option<Proxy>,
    /// Max relays allowed in the pool
    pub max_relays: Option<NonZeroUsize>,
    /// Notification channel size
    pub notification_channel_size: NonZeroUsize,
    /// Connection timeout (default: 15 sec)
    ///
    /// This is the default timeout use when attempting to establish a connection with the relay
    pub connect_timeout: Duration,
    /// Relay limits
    pub relay_limits: RelayLimits,
    /// Max average latency
    pub max_avg_latency: Option<Duration>,
    /// Sleep when idle
    pub sleep_when_idle: SleepWhenIdle,
    /// Verify subscriptions
    pub verify_subscriptions: bool,
    /// Ban relay on mismatch
    pub ban_relay_on_mismatch: bool,
}

impl Default for ClientBuilder {
    fn default() -> Self {
        Self {
            websocket_transport: Arc::new(DefaultWebsocketTransport),
            admit_policy: None,
            authenticator: None,
            database: Arc::new(MemoryEventsTracker::default()),
            gossip: None,
            gossip_config: GossipConfig::default(),
            monitor: None,
            #[cfg(not(target_arch = "wasm32"))]
            proxy: None,
            max_relays: None,
            connect_timeout: Duration::from_secs(15),
            relay_limits: RelayLimits::default(),
            max_avg_latency: None,
            sleep_when_idle: SleepWhenIdle::default(),
            verify_subscriptions: false,
            ban_relay_on_mismatch: false,
            notification_channel_size: DEFAULT_NOTIFICATION_CHANNEL_SIZE,
        }
    }
}

impl ClientBuilder {
    /// New default client builder
    #[inline]
    pub fn new() -> Self {
        Self::default()
    }

    /// Set custom WebSocket transport
    ///
    /// By default [`DefaultWebsocketTransport`] is used.
    #[inline]
    pub fn websocket_transport<T>(mut self, transport: T) -> Self
    where
        T: IntoWebSocketTransport,
    {
        self.websocket_transport = transport.into_transport();
        self
    }

    /// Set an admission policy
    #[inline]
    pub fn admit_policy<T>(mut self, policy: T) -> Self
    where
        T: AdmitPolicy + 'static,
    {
        self.admit_policy = Some(Arc::new(policy));
        self
    }

    /// Set a NIP-42 authenticator.
    ///
    /// The authenticator is used when a relay requires authentication and the
    /// client needs to build an `AUTH` event.
    ///
    /// If you already have a signer that implements
    /// [`AsyncGetPublicKey`](nostr::key::AsyncGetPublicKey) and
    /// [`AsyncSignEvent`](nostr::event::AsyncSignEvent), you can wrap it with
    /// [`SignerAuthenticator`](crate::authenticator::SignerAuthenticator).
    ///
    /// # Example
    ///
    /// ```rust
    /// # use nostr_sdk::prelude::*;
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let keys = Keys::generate();
    /// let authenticator = SignerAuthenticator::new(keys);
    /// let client = Client::builder().authenticator(authenticator).build();
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn authenticator<T>(mut self, authenticator: T) -> Self
    where
        T: Authenticator + 'static,
    {
        self.authenticator = Some(Arc::new(authenticator));
        self
    }

    /// Set database
    #[inline]
    pub fn database<D>(mut self, database: D) -> Self
    where
        D: IntoNostrDatabase,
    {
        self.database = database.into_nostr_database();
        self
    }

    /// Set a gossip database
    #[inline]
    pub fn gossip<T>(mut self, gossip: T) -> Self
    where
        T: IntoNostrGossip,
    {
        self.gossip = Some(gossip.into_nostr_gossip());
        self
    }

    /// Set gossip config
    #[inline]
    pub fn gossip_config(mut self, config: GossipConfig) -> Self {
        self.gossip_config = config;
        self
    }

    /// Set monitor
    #[inline]
    pub fn monitor(mut self, monitor: Monitor) -> Self {
        self.monitor = Some(monitor);
        self
    }

    /// Proxy
    #[inline]
    #[cfg(not(target_arch = "wasm32"))]
    pub fn proxy(mut self, proxy: Proxy) -> Self {
        self.proxy = Some(proxy);
        self
    }

    /// Max relays allowed in the pool (default: None)
    ///
    /// `None` means no limit.
    #[inline]
    pub fn max_relays(mut self, num: Option<NonZeroUsize>) -> Self {
        self.max_relays = num;
        self
    }

    /// Connection timeout (default: 15 sec)
    ///
    /// This is the default timeout use when attempting to establish a connection with the relay
    #[inline]
    pub fn connect_timeout(mut self, timeout: Duration) -> Self {
        self.connect_timeout = timeout;
        self
    }

    /// Set relay limits
    #[inline]
    pub fn relay_limits(mut self, limits: RelayLimits) -> Self {
        self.relay_limits = limits;
        self
    }

    /// Set max latency (default: None)
    ///
    /// Relays with an avg. latency greater that this value will be skipped.
    #[inline]
    pub fn max_avg_latency(mut self, max: Duration) -> Self {
        self.max_avg_latency = Some(max);
        self
    }

    /// Set sleep when idle config
    #[inline]
    pub fn sleep_when_idle(mut self, config: SleepWhenIdle) -> Self {
        self.sleep_when_idle = config;
        self
    }

    /// Verify that received events belong to a subscription and match the filter.
    pub fn verify_subscriptions(mut self, enable: bool) -> Self {
        self.verify_subscriptions = enable;
        self
    }

    /// If true, ban a relay when it sends an event that doesn't match the subscription filter.
    pub fn ban_relay_on_mismatch(mut self, ban_relay: bool) -> Self {
        self.ban_relay_on_mismatch = ban_relay;
        self
    }

    /// Notification channel size (default: 4096)
    #[inline]
    pub fn notification_channel_size(mut self, size: NonZeroUsize) -> Self {
        self.notification_channel_size = size;
        self
    }

    /// Build [`Client`]
    #[inline]
    pub fn build(self) -> Client {
        Client::from_builder(self)
    }
}