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}