Skip to main content

nym_sdk_session/
config.rs

1// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4//! Session configuration: [`SessionConfig`] and the opt-in [`RestockPolicy`].
5
6use std::path::PathBuf;
7use std::sync::Arc;
8use std::time::Duration;
9
10use nym_bandwidth_controller::config::BandwidthControllerConfig;
11use nym_bandwidth_controller::BandwidthTicketProvider;
12use nym_network_defaults::NymNetworkDetails;
13
14/// Opt-in policy for automatic chain-side ticketbook restock. Maps onto the bandwidth controller's
15/// restock thresholds. Only in effect when set via [`SessionConfig::automatic_topups`].
16#[derive(Clone, Copy, Debug)]
17pub struct RestockPolicy {
18    /// Restock a ticket type once its usable stock drops to/below this many tickets.
19    pub restock_below_tickets: u64,
20    /// Minimum usable tickets for a type to be considered "ready to connect".
21    pub readiness_min_tickets: u64,
22    /// How often to proactively check stock.
23    pub check_interval: Duration,
24    /// Treat a ticketbook expiring within this window as needing replacement.
25    pub soon_expiry: Duration,
26}
27
28impl Default for RestockPolicy {
29    fn default() -> Self {
30        // Mirror `BandwidthControllerConfig::default()`.
31        Self {
32            restock_below_tickets: 20,
33            readiness_min_tickets: 5,
34            check_interval: Duration::from_secs(3 * 3600),
35            soon_expiry: Duration::from_secs(12 * 3600),
36        }
37    }
38}
39
40impl From<RestockPolicy> for BandwidthControllerConfig {
41    fn from(p: RestockPolicy) -> Self {
42        BandwidthControllerConfig {
43            topup_interval: p.check_interval,
44            soon_expiry_threshold: p.soon_expiry,
45            nb_ticket_restock: p.restock_below_tickets,
46            min_nb_ticket_needed: p.readiness_min_tickets,
47            // The session scopes this to its WireGuard types when installing the config; the
48            // default is only a placeholder.
49            ..Default::default()
50        }
51    }
52}
53
54/// Configuration for creating a [`Session`].
55pub struct SessionConfig {
56    /// Funded chain mnemonic used to deposit NYM and issue ticketbooks. Ignored when
57    /// [`bandwidth_provider`](Self::bandwidth_provider) is set.
58    pub mnemonic: bip39::Mnemonic,
59    /// Network to operate against (contract addresses, endpoints, denoms).
60    pub network: NymNetworkDetails,
61    /// Persistent credential store path. `None` uses a file under `data_path`
62    /// (a fully ephemeral in-memory store is not used so tickets survive a
63    /// bring-down/bring-up cycle).
64    pub credential_store_path: Option<PathBuf>,
65    /// Directory for the fetcher's pending-request recovery database and other
66    /// per-session data.
67    pub data_path: PathBuf,
68    /// Optional dVPN gateway-directory URL. When set, the session fetches it to
69    /// enrich gateway monikers and to enable QUIC-bridge entry selection
70    /// (`register_two_hop_quic`). Fetched best-effort — a failure is logged and
71    /// treated as an empty directory.
72    pub dvpn_directory_url: Option<String>,
73    /// Opt-in automatic chain-side restock. `None` (default) provisions once and never deposits in
74    /// the background; the tunnel still tops up from already-stored tickets. `Some(policy)` lets a
75    /// long-lived session re-issue ticketbooks when stock runs low (this spends NYM).
76    pub automatic_topups: Option<RestockPolicy>,
77    /// Externally-managed bandwidth provider. When set, the session uses it for all ticket
78    /// spending and does NOT spawn its own controller — for callers already running a controller
79    /// over the same credential store (preserving the single-writer invariant). `mnemonic` and the
80    /// credential store are then unused, and the caller is responsible for provisioning.
81    pub bandwidth_provider: Option<Arc<dyn BandwidthTicketProvider>>,
82    /// Reuse persisted gateway registrations (default: `true`). A successful registration is
83    /// stored under `data_path` (client WireGuard key + assigned configuration) and served back
84    /// on later registrations against the same gateway/role — no gateway exchange, no ticket
85    /// spent, resuming the peer's remaining bandwidth allowance. Validate reused registrations
86    /// by use (`Tunnel::await_established`) and fall back via
87    /// [`crate::Session::invalidate_registration`].
88    ///
89    /// Privacy trade-off: reuse links this client's connections to the same WireGuard peer
90    /// identity at the gateway across sessions. Set to `false` for an unlinkable fresh peer per
91    /// connection — every registration then spends a ticket, and nothing is persisted.
92    pub reuse_registrations: bool,
93}
94
95impl SessionConfig {
96    /// A config with the required fields and sensible defaults (no automatic topups, own controller).
97    pub fn new(mnemonic: bip39::Mnemonic, network: NymNetworkDetails, data_path: PathBuf) -> Self {
98        Self {
99            mnemonic,
100            network,
101            credential_store_path: None,
102            data_path,
103            dvpn_directory_url: None,
104            automatic_topups: None,
105            bandwidth_provider: None,
106            reuse_registrations: true,
107        }
108    }
109
110    /// Opt into automatic chain-side restock with the given policy (this can spend NYM).
111    #[must_use]
112    pub fn with_automatic_topups(mut self, policy: RestockPolicy) -> Self {
113        self.automatic_topups = Some(policy);
114        self
115    }
116
117    /// Use an externally-managed bandwidth provider instead of spawning an own controller.
118    #[must_use]
119    pub fn with_bandwidth_provider(mut self, provider: Arc<dyn BandwidthTicketProvider>) -> Self {
120        self.bandwidth_provider = Some(provider);
121        self
122    }
123
124    /// Set the dVPN directory URL.
125    #[must_use]
126    pub fn with_dvpn_directory_url(mut self, url: impl Into<String>) -> Self {
127        self.dvpn_directory_url = Some(url.into());
128        self
129    }
130
131    /// Set the credential store path.
132    #[must_use]
133    pub fn with_credential_store_path(mut self, path: impl Into<PathBuf>) -> Self {
134        self.credential_store_path = Some(path.into());
135        self
136    }
137}