pim_core/config/model.rs
1//! Configuration data model.
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6#[allow(unused_imports)]
7use super::defaults::*;
8use super::peer::PeerConfig;
9use super::peer_cleanup::PeerCleanupConfig;
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12/// Top-level configuration loaded from the node TOML file.
13pub struct Config {
14 /// Identity and local state settings for this node.
15 pub node: NodeConfig,
16 /// TUN interface settings for the mesh dataplane.
17 #[serde(default)]
18 pub interface: InterfaceConfig,
19 /// LAN peer discovery settings.
20 #[serde(default)]
21 pub discovery: DiscoveryConfig,
22 /// Optional private-mesh membership settings. Absent or `mode = "open"` →
23 /// open mesh (any peer can connect; default behaviour). When configured
24 /// with `mode = "private"` and a passphrase, only peers that derive the
25 /// same mesh secret can complete the handshake or decrypt discovery
26 /// advertisements. See [`MeshConfig`] for the schema.
27 #[serde(default)]
28 pub mesh: MeshConfig,
29 /// Peer-to-peer transport settings.
30 #[serde(default)]
31 pub transport: TransportConfig,
32 /// Route propagation and expiry settings.
33 #[serde(default)]
34 pub routing: RoutingConfig,
35 /// Internet gateway behaviour and NAT settings.
36 #[serde(default)]
37 pub gateway: GatewayConfig,
38 /// Relay forwarding settings. When enabled this node forwards traffic for other mesh peers.
39 #[serde(default)]
40 pub relay: RelayConfig,
41 /// Key material and encryption policy settings.
42 #[serde(default)]
43 pub security: SecurityConfig,
44 /// Wi-Fi Direct (IEEE 802.11 P2P) peer discovery and group formation settings.
45 #[serde(default)]
46 pub wifi_direct: WifiDirectConfig,
47 /// Bluetooth PAN peer link monitoring and address handoff settings.
48 #[serde(default)]
49 pub bluetooth: BluetoothConfig,
50 /// Bluetooth RFCOMM direct-channel discovery and TCP bridge settings.
51 #[serde(default)]
52 pub bluetooth_rfcomm: BluetoothRfcommConfig,
53 /// User-to-user encrypted messaging settings, including broadcast policy.
54 #[serde(default)]
55 pub messaging: MessagingConfig,
56 /// Statically configured peers. Optional — nodes can rely entirely on discovery when empty.
57 #[serde(default)]
58 pub peers: Vec<PeerConfig>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
62/// Node-local identity and filesystem settings.
63pub struct NodeConfig {
64 /// Human-readable node name used in logs and operator-facing output.
65 pub name: String,
66 /// Directory for persistent node state such as keys and runtime metadata.
67 #[serde(default = "default_data_dir")]
68 pub data_dir: PathBuf,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
72/// Settings for the Linux TUN interface that carries mesh IP traffic.
73///
74/// Mesh addresses are derived deterministically from each node's
75/// `NodeId` via [`pim_core::derive_mesh_ipv4`] / [`derive_mesh_ipv6`].
76/// Configure only the mesh **prefix**; the per-node host bits come from
77/// the derivation. Two daemons sharing a prefix will never collide on
78/// IPv6 (`/64` is collision-free at PIM scale) and only rarely on IPv4
79/// in small prefixes; collisions degrade gracefully — see the routing
80/// docs.
81pub struct InterfaceConfig {
82 /// Requested interface name, for example `pim0`.
83 #[serde(default = "default_interface_name")]
84 pub name: String,
85 /// Interface MTU in bytes.
86 #[serde(default = "default_mtu")]
87 pub mtu: u32,
88 /// Mesh IPv4 prefix (e.g. `"10.77.0.0/16"`). Defaults to
89 /// [`pim_core::DEFAULT_MESH_IPV4_PREFIX`]. Each node's host bits
90 /// inside this prefix are derived from its `NodeId`.
91 #[serde(default)]
92 pub mesh_ipv4_prefix: Option<String>,
93 /// Mesh IPv6 prefix (e.g. `"fd77::/64"`). Defaults to
94 /// [`pim_core::DEFAULT_MESH_IPV6_PREFIX`]. `/64` recommended.
95 #[serde(default)]
96 pub mesh_ipv6_prefix: Option<String>,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
100/// UDP broadcast discovery timing and policy configuration.
101pub struct DiscoveryConfig {
102 /// Enable or disable the discovery service entirely. When `false` the daemon connects only
103 /// to statically configured `[[peers]]`.
104 #[serde(default = "default_discovery_enabled")]
105 pub enabled: bool,
106 /// UDP port used for sending and receiving discovery broadcasts.
107 #[serde(default = "default_discovery_port")]
108 pub port: u16,
109 /// Interval between outgoing discovery broadcasts, in milliseconds.
110 #[serde(default = "default_broadcast_interval_ms")]
111 pub broadcast_interval_ms: u64,
112 /// Time after which an unseen peer is considered stale, in milliseconds.
113 #[serde(default = "default_peer_timeout_ms")]
114 pub peer_timeout_ms: u64,
115 /// Automatically initiate connections to discovered peers advertising relay capability.
116 #[serde(default = "default_connect_relays")]
117 pub connect_relays: bool,
118 /// Automatically initiate connections to discovered peers advertising gateway capability.
119 #[serde(default = "default_connect_gateways")]
120 pub connect_gateways: bool,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
124#[serde(rename_all = "snake_case")]
125/// Mesh-membership mode. `Open` admits any authenticated peer (mode default
126/// when `[mesh]` is absent). `Private` requires every peer to derive the
127/// same mesh secret from a shared passphrase.
128pub enum MeshMode {
129 /// No mesh-level gating. Behaves identically to a daemon configured
130 /// without a `[mesh]` section.
131 #[default]
132 Open,
133 /// Passphrase-gated mesh: only peers that derive the same secret can
134 /// complete the handshake or decrypt discovery advertisements.
135 Private,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
139/// Private-mesh membership configuration.
140///
141/// All fields are optional; an absent `[mesh]` section is treated as
142/// `mode = "open"`. When `mode = "private"`, `passphrase` must be a
143/// non-empty UTF-8 string. The passphrase is stretched once at daemon
144/// startup via Argon2id (see [`MeshKdfConfig`]) into a 32-byte master,
145/// then split via HKDF-SHA256 into purpose-bound sub-keys for discovery
146/// encryption and handshake binding.
147pub struct MeshConfig {
148 /// Mesh mode. Defaults to [`MeshMode::Open`] (open mesh).
149 #[serde(default)]
150 pub mode: MeshMode,
151 /// Passphrase used to derive the mesh secret. Required when
152 /// `mode = "private"`. Empty string in private mode is rejected at
153 /// startup. Ignored entirely when `mode = "open"`.
154 #[serde(default)]
155 pub passphrase: Option<String>,
156 /// Optional cosmetic mesh label used by the UI / CLI. Also mixed into
157 /// the Argon2id salt, so two meshes that happen to share a passphrase
158 /// but use different `mesh_id` values derive distinct secrets and
159 /// cannot interconnect. Renaming `mesh_id` therefore invalidates
160 /// every existing peer connection — UI surfaces a warning.
161 #[serde(default)]
162 pub mesh_id: Option<String>,
163 /// Argon2id parameters used to stretch the passphrase. The default
164 /// targets ~100 ms on a desktop. Tunable for embedded targets.
165 #[serde(default)]
166 pub kdf: MeshKdfConfig,
167}
168
169#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
170/// Argon2id KDF parameters used when stretching the mesh passphrase. The
171/// derivation runs once at daemon startup and the result is cached, so
172/// values can be conservative without affecting per-handshake latency.
173pub struct MeshKdfConfig {
174 /// Memory cost in KiB. Default 65536 (64 MiB).
175 #[serde(default = "default_mesh_kdf_m_cost_kib")]
176 pub m_cost_kib: u32,
177 /// Number of iterations. Default 3.
178 #[serde(default = "default_mesh_kdf_t_cost")]
179 pub t_cost: u32,
180 /// Parallelism. Default 1.
181 #[serde(default = "default_mesh_kdf_p_cost")]
182 pub p_cost: u32,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
186/// Settings that control whether this node acts as a relay, forwarding traffic for other peers.
187pub struct RelayConfig {
188 /// Enables relay forwarding when `true`. Gateway nodes are implicitly relays regardless of
189 /// this setting.
190 #[serde(default)]
191 pub enabled: bool,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
195/// Wire transport configuration for direct peer connections.
196pub struct TransportConfig {
197 /// Transport backend name, currently `tcp`.
198 #[serde(default = "default_transport_type")]
199 pub r#type: String,
200 /// Local port the transport listens on for inbound peer connections.
201 #[serde(default = "default_listen_port")]
202 pub listen_port: u16,
203 /// Maximum reconnect attempts per peer before giving up.
204 #[serde(default = "default_max_reconnect_attempts")]
205 pub max_reconnect_attempts: u32,
206 /// Timeout for outbound TCP connect attempts in milliseconds.
207 #[serde(default = "default_connect_timeout_ms")]
208 pub connect_timeout_ms: u64,
209 /// Periodic cleanup of `ReconnectManager.discovered_targets` —
210 /// see [`PeerCleanupConfig`]. Discovered targets are
211 /// `(NodeId, ConnectTarget)` pairs accumulated as the daemon
212 /// learns peers from various discovery sources; without
213 /// cleanup the set grows monotonically. Ephemeral defaults
214 /// (7 days / 1 h).
215 #[serde(default = "PeerCleanupConfig::ephemeral_default")]
216 pub peer_cleanup: PeerCleanupConfig,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
220/// Routing behaviour for propagating and aging route advertisements.
221pub struct RoutingConfig {
222 /// Maximum hop count accepted before a route is considered unusable.
223 #[serde(default = "default_max_hops")]
224 pub max_hops: u8,
225 /// Routing algorithm identifier used for compatibility and diagnostics.
226 #[serde(default = "default_route_algorithm")]
227 pub algorithm: String,
228 /// Lifetime of learned routes before expiry, in seconds.
229 #[serde(default = "default_route_expiry_s")]
230 pub route_expiry_s: u64,
231 /// DNS resolvers handed to the system resolver (`resolvectl dns
232 /// pim0 …` on Linux/systemd-resolved) when split-default routing
233 /// is engaged. Without this list the resolver keeps its
234 /// DHCP-provided upstream — which becomes unreachable the moment
235 /// the user disables their wifi/wired uplink, so `curl gmail.com`
236 /// fails even though the IP path through the mesh is live.
237 /// Defaults to a small public anycast set; override for corporate
238 /// split-DNS (`["10.0.0.53"]`), pi-hole on the gateway
239 /// (`["10.77.0.1"]`), or IPv6-only resolvers.
240 #[serde(default = "default_dns_servers")]
241 pub dns_servers: Vec<String>,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
245/// Settings that control whether this node can act as an internet gateway.
246pub struct GatewayConfig {
247 /// Enables gateway and NAT behaviour when `true`.
248 #[serde(default)]
249 pub enabled: bool,
250 /// Name of the internet-facing interface used for masquerading.
251 #[serde(default = "default_nat_interface")]
252 pub nat_interface: String,
253 /// Maximum number of concurrent tracked gateway connections.
254 #[serde(default = "default_max_connections")]
255 pub max_connections: u32,
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
259/// Encryption policy and key storage configuration.
260pub struct SecurityConfig {
261 /// Path to the Ed25519 private key file for this node.
262 #[serde(default = "default_key_file")]
263 pub key_file: PathBuf,
264 /// Whether unencrypted sessions should be rejected.
265 #[serde(default = "default_require_encryption")]
266 pub require_encryption: bool,
267 /// Authorization policy applied after peer identity is authenticated.
268 #[serde(default)]
269 pub authorization_policy: AuthorizationPolicy,
270 /// Explicitly authorized peers when `authorization_policy = "allow_list"`.
271 #[serde(default)]
272 pub authorized_peers: Vec<crate::NodeId>,
273 /// Persistent trust store used by `trust_on_first_use`.
274 #[serde(default = "default_trust_store_file")]
275 pub trust_store_file: PathBuf,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
279#[serde(rename_all = "snake_case")]
280/// Direct-peer authorization policy.
281pub enum AuthorizationPolicy {
282 /// Admit any authenticated peer.
283 #[default]
284 AllowAll,
285 /// Admit only peers listed in `authorized_peers`.
286 AllowList,
287 /// Admit authenticated peers on first contact and persist their identity locally.
288 TrustOnFirstUse,
289}
290
291/// Wi-Fi Direct (IEEE 802.11 P2P) discovery and group negotiation configuration.
292///
293/// When `enabled = true` the daemon will start Wi-Fi Direct peer discovery via
294/// `wpa_cli` and attempt to form P2P groups with discovered devices. Once a group
295/// is established the resulting IP address is used to open a standard TCP transport
296/// connection, so all existing security, routing, and gateway logic applies unchanged.
297///
298/// **Prerequisite:** `wpa_supplicant` compiled with P2P support must be running and
299/// controlling the interface specified by `interface`.
300#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
301pub struct WifiDirectConfig {
302 /// Enable Wi-Fi Direct peer discovery. Defaults to `false` (opt-in).
303 #[serde(default)]
304 pub enabled: bool,
305 /// Physical Wi-Fi interface to use for P2P operations (e.g. `wlan0`).
306 #[serde(default = "default_wfd_interface")]
307 pub interface: String,
308 /// Group Owner intent value (0–15). Higher values make this node more likely to
309 /// become the Group Owner during negotiation. Default 7 (neutral).
310 #[serde(default = "default_wfd_go_intent")]
311 pub go_intent: u8,
312 /// P2P listen channel number. Default 6.
313 #[serde(default = "default_wfd_listen_channel")]
314 pub listen_channel: u8,
315 /// P2P operating channel number. Default 6.
316 #[serde(default = "default_wfd_op_channel")]
317 pub op_channel: u8,
318 /// Connection method: `"pbc"` (push-button) or `"pin:<8-digit-pin>"`. Default `"pbc"`.
319 #[serde(default = "default_wfd_connect_method")]
320 pub connect_method: String,
321 /// Periodic cleanup of unreachable Wi-Fi Direct peers — see
322 /// [`PeerCleanupConfig`]. The destructive action drops the
323 /// peer's row from the in-daemon `wfd_peer_lifecycle` table.
324 /// Bluetooth-flavoured defaults (2 h / 1 h) since the radio-
325 /// layer reachability characteristics are similar.
326 #[serde(default = "PeerCleanupConfig::bluetooth_default")]
327 pub peer_cleanup: PeerCleanupConfig,
328}
329
330/// Bluetooth PAN link-establishment configuration.
331///
332/// This mechanism is intentionally narrow: it does not replace the transport
333/// layer and it does not manage pairing. Instead, it waits for a Bluetooth PAN
334/// interface to appear, then learns peer IPs from the PAN neighbor table and
335/// hands them to the daemon so the existing TCP transport and handshake logic
336/// can connect normally. Static Bluetooth peers are configured under
337/// `[[peers]]` with `mechanism = "bluetooth"`.
338#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
339pub struct BluetoothConfig {
340 /// Enable Bluetooth PAN link monitoring. Defaults to `false` (opt-in).
341 #[serde(default)]
342 pub enabled: bool,
343 /// Preferred PAN-facing interface name or `"auto"` for runtime resolution.
344 #[serde(default = "default_bluetooth_interface")]
345 pub interface: String,
346 /// Enable radio-level Bluetooth discovery and pairing for new peers.
347 #[serde(default = "default_bluetooth_radio_discovery_enabled")]
348 pub radio_discovery_enabled: bool,
349 /// Prefix used to identify PIM peers by Bluetooth device name.
350 #[serde(default = "default_bluetooth_device_name_prefix")]
351 pub device_name_prefix: String,
352 /// Local Bluetooth controller alias to advertise. Empty means derived from node name.
353 #[serde(default)]
354 pub local_alias: String,
355 /// Allow outbound PAN/NAP connection attempts to discovered peers.
356 #[serde(default = "default_bluetooth_connect_pan")]
357 pub connect_pan: bool,
358 /// Start and supervise a local Linux NAP server process.
359 #[serde(default)]
360 pub serve_nap: bool,
361 /// Linux bridge/interface to expose through the local NAP server.
362 #[serde(default = "default_bluetooth_nap_bridge")]
363 pub nap_bridge: String,
364 /// IPv4 address/CIDR assigned to `nap_bridge` when the daemon manages it.
365 #[serde(default = "default_bluetooth_nap_bridge_addr")]
366 pub nap_bridge_addr: String,
367 /// Run a daemon-supervised DHCP server on `nap_bridge` when serving NAP.
368 #[serde(default = "default_bluetooth_dhcp_enabled")]
369 pub dhcp_enabled: bool,
370 /// Explicit DHCP range (`start,end`). When unset, derived from `nap_bridge_addr`.
371 #[serde(default)]
372 pub dhcp_range: Option<String>,
373 /// DHCP lease time passed to dnsmasq (e.g. `"12h"`, `"infinite"`).
374 #[serde(default = "default_bluetooth_dhcp_lease_time")]
375 pub dhcp_lease_time: String,
376 /// Comma-separated DNS server list advertised to DHCP clients. When unset,
377 /// inherited from the host's `/etc/resolv.conf` at runtime.
378 #[serde(default)]
379 pub dhcp_dns: Option<String>,
380 /// Automatically request DHCP on the resolved PAN interface when acting as a
381 /// Linux PAN client (`connect_pan = true`, `serve_nap = false`).
382 #[serde(default = "default_bluetooth_request_dhcp")]
383 pub request_dhcp: bool,
384 /// Automatically discover peer IPs from the PAN interface neighbor table.
385 #[serde(default = "default_bluetooth_auto_discover_peers")]
386 pub auto_discover_peers: bool,
387 /// Poll interval used while waiting for the PAN interface to become ready.
388 #[serde(default = "default_bluetooth_poll_interval_ms")]
389 pub poll_interval_ms: u64,
390 /// Poll interval used for radio-level device scans.
391 #[serde(default = "default_bluetooth_scan_interval_ms")]
392 pub scan_interval_ms: u64,
393 /// Poll interval used for automatic peer discovery after the interface is ready.
394 #[serde(default = "default_bluetooth_peer_discovery_interval_ms")]
395 pub peer_discovery_interval_ms: u64,
396 /// Timeout for `bluetoothctl` operations, in seconds.
397 #[serde(default = "default_bluetoothctl_timeout_s")]
398 pub bluetoothctl_timeout_s: u64,
399 /// How long the controller remains discoverable after startup.
400 #[serde(default = "default_bluetooth_discoverable_timeout_s")]
401 pub discoverable_timeout_s: u64,
402 /// Maximum time to wait for the PAN interface to appear before giving up.
403 #[serde(default = "default_bluetooth_startup_timeout_ms")]
404 pub startup_timeout_ms: u64,
405 /// Periodic cleanup of unreachable PAN-paired peers — see
406 /// [`PeerCleanupConfig`]. Bluetooth-flavoured defaults (2 h /
407 /// 1 h). The destructive action is `bluetoothctl remove
408 /// <bd_addr>`. Coordinates with `[bluetooth_rfcomm.peer_cleanup]`
409 /// via the BlueZ `Connected` check so an active session in
410 /// either subsystem suppresses the unpair.
411 #[serde(default = "PeerCleanupConfig::bluetooth_default")]
412 pub peer_cleanup: PeerCleanupConfig,
413}
414
415/// Bluetooth RFCOMM direct-channel configuration.
416///
417/// RFCOMM is independent from Bluetooth PAN/NAP. It opens a Bluetooth RFCOMM
418/// channel to paired devices whose names match `device_name_prefix`, exchanges
419/// PIM identity frames, and can bridge the resulting byte stream to the local
420/// TCP transport listener so the rest of the daemon sees a normal peer session.
421#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
422pub struct BluetoothRfcommConfig {
423 /// Enable the Linux RFCOMM service. Defaults to `false` (opt-in).
424 #[serde(default)]
425 pub enabled: bool,
426 /// RFCOMM channel to bind and dial.
427 #[serde(default = "default_bluetooth_rfcomm_channel")]
428 pub channel: u8,
429 /// Prefix used to identify paired PIM peers by Bluetooth device name.
430 #[serde(default = "default_bluetooth_device_name_prefix")]
431 pub device_name_prefix: String,
432 /// Enable outbound paired-device scanning and dialing.
433 #[serde(default = "default_bluetooth_rfcomm_outbound_enabled")]
434 pub outbound_enabled: bool,
435 /// Poll interval for outbound paired-device scans, in milliseconds.
436 #[serde(default = "default_bluetooth_rfcomm_poll_interval_ms")]
437 pub poll_interval_ms: u64,
438 /// Bridge established RFCOMM sessions to the local TCP transport listener.
439 #[serde(default = "default_bluetooth_rfcomm_bridge_to_tcp")]
440 pub bridge_to_tcp: bool,
441 /// Enable the desktop-initiated inquiry loop that scans for nearby
442 /// `device_name_prefix` peers and runs `bluetoothctl pair` on them.
443 /// Mirrors the Android side's `startDiscovery`; together they make
444 /// either device able to bootstrap the bond without manual pairing
445 /// in OS Bluetooth Settings.
446 #[serde(default = "default_bluetooth_rfcomm_discovery_enabled")]
447 pub discovery_enabled: bool,
448 /// Cadence between `bluetoothctl scan on` cycles in the inquiry
449 /// loop. BR/EDR inquiry runs for ~12 s per cycle then idles —
450 /// re-arming too often wastes the BT controller and battery.
451 #[serde(default = "default_bluetooth_rfcomm_inquiry_interval_ms")]
452 pub inquiry_interval_ms: u64,
453 /// Periodic cleanup of unreachable paired peers — see
454 /// [`PeerCleanupConfig`]. The destructive action is
455 /// `bluetoothctl remove <bd_addr>`. Bluetooth-flavoured defaults:
456 /// enabled, 2 h lifetime, 1 h sweep cadence.
457 #[serde(default = "PeerCleanupConfig::bluetooth_default")]
458 pub peer_cleanup: PeerCleanupConfig,
459}
460
461/// Messaging subsystem configuration. Exposes the
462/// `[messaging.broadcast]` policy that governs how this node
463/// advertises its identity across the mesh, plus a peer-cleanup
464/// subsection that ages out stale entries from the daemon's
465/// `peers_seen` identity keystore.
466#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
467pub struct MessagingConfig {
468 /// Identity-broadcast policy for the multi-hop mesh.
469 #[serde(default)]
470 pub broadcast: BroadcastConfig,
471 /// Periodic cleanup of unreachable mesh-identity entries in the
472 /// daemon's `peers_seen` table — see [`PeerCleanupConfig`]. The
473 /// destructive action drops the row + emits `peer_forgotten`.
474 /// Mesh-identity defaults (90 days / daily): identity loss
475 /// forces a fresh handshake on next contact (cheap), so a long
476 /// horizon keeps the keystore from growing forever on a long-
477 /// running node.
478 #[serde(default = "PeerCleanupConfig::mesh_identity_default")]
479 pub peer_cleanup: PeerCleanupConfig,
480}
481
482impl Default for MessagingConfig {
483 fn default() -> Self {
484 Self {
485 broadcast: BroadcastConfig::default(),
486 peer_cleanup: PeerCleanupConfig::mesh_identity_default(),
487 }
488 }
489}
490
491/// Identity-broadcast policy.
492///
493/// Broadcasts are routed `PeerInfo` control frames sent to every node
494/// in the local routing table — they let multi-hop peers learn each
495/// other's `node_id + x25519_pubkey` without an out-of-band exchange.
496#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
497pub struct BroadcastConfig {
498 /// Cadence between scheduled outbound broadcast cycles, in seconds.
499 /// `None` (or omitted) disables the periodic task; one-shot
500 /// `peers.broadcast_identity_now` calls still work. Values below
501 /// `MIN_OUTGOING_INTERVAL_S` are rejected at the RPC layer.
502 #[serde(default)]
503 pub outgoing_interval_s: Option<u64>,
504 /// When `true`, routed `PeerInfo` arrivals (the inbound side of a
505 /// broadcast) are surfaced as `peer_seen` events and a discovered
506 /// row in the UI. When `false`, the X25519 key is still cached
507 /// (replies need it) but no event is emitted — useful on noisy
508 /// meshes where the local node does not want surfaced discovery.
509 #[serde(default = "default_broadcast_watch_incoming")]
510 pub watch_incoming: bool,
511 /// Minimum seconds that must elapse between accepted broadcasts
512 /// from any single peer. Subsequent broadcasts from the same peer
513 /// arriving sooner are dropped before the keystore is touched —
514 /// a single misbehaving peer cannot flood the keystore or the UI.
515 #[serde(default = "default_broadcast_min_peer_interval_s")]
516 pub min_peer_interval_s: u64,
517 /// Periodic cleanup of `state.broadcast_peer_last_seen` rate-
518 /// limit entries — see [`PeerCleanupConfig`]. Ephemeral defaults
519 /// (7 days / 1 h): the rate-limit map costs ~32 bytes per peer,
520 /// so the horizon is generous; the goal is bounding worst-case
521 /// growth on long-running nodes that see thousands of unique
522 /// broadcasters.
523 #[serde(default = "PeerCleanupConfig::ephemeral_default")]
524 pub peer_cleanup: PeerCleanupConfig,
525}
526
527impl Default for BroadcastConfig {
528 fn default() -> Self {
529 Self {
530 outgoing_interval_s: None,
531 watch_incoming: default_broadcast_watch_incoming(),
532 min_peer_interval_s: default_broadcast_min_peer_interval_s(),
533 peer_cleanup: PeerCleanupConfig::ephemeral_default(),
534 }
535 }
536}
537
538impl BroadcastConfig {
539 /// Floor enforced by the RPC layer when a non-`None`
540 /// `outgoing_interval_s` is supplied. Below this we'd start
541 /// hammering the mesh.
542 pub const MIN_OUTGOING_INTERVAL_S: u64 = 30;
543}
544
545fn default_broadcast_watch_incoming() -> bool {
546 true
547}
548
549fn default_broadcast_min_peer_interval_s() -> u64 {
550 60
551}
552
553// Default value functions
554
555fn default_data_dir() -> PathBuf {
556 PathBuf::from("~/.pim")
557}
558
559fn default_interface_name() -> String {
560 "pim0".into()
561}
562
563fn default_mtu() -> u32 {
564 1400
565}
566
567fn default_discovery_enabled() -> bool {
568 true
569}
570
571fn default_discovery_port() -> u16 {
572 9101
573}
574
575fn default_broadcast_interval_ms() -> u64 {
576 5000
577}
578
579fn default_peer_timeout_ms() -> u64 {
580 30000
581}
582
583fn default_connect_relays() -> bool {
584 true
585}
586
587fn default_connect_gateways() -> bool {
588 true
589}
590
591fn default_transport_type() -> String {
592 "tcp".into()
593}
594
595fn default_listen_port() -> u16 {
596 9100
597}
598
599fn default_max_reconnect_attempts() -> u32 {
600 20
601}
602
603fn default_connect_timeout_ms() -> u64 {
604 3_000
605}
606
607fn default_max_hops() -> u8 {
608 10
609}
610
611fn default_route_algorithm() -> String {
612 "distance-vector".into()
613}
614
615fn default_route_expiry_s() -> u64 {
616 300
617}
618
619fn default_dns_servers() -> Vec<String> {
620 vec!["1.1.1.1".into(), "1.0.0.1".into(), "8.8.8.8".into()]
621}
622
623fn default_nat_interface() -> String {
624 "eth0".into()
625}
626
627fn default_max_connections() -> u32 {
628 200
629}
630
631fn default_key_file() -> PathBuf {
632 PathBuf::from("~/.pim/node.key")
633}
634
635fn default_require_encryption() -> bool {
636 true
637}
638
639fn default_trust_store_file() -> PathBuf {
640 PathBuf::from("~/.pim/trusted-peers.toml")
641}
642
643fn default_wfd_interface() -> String {
644 "wlan0".into()
645}
646
647fn default_wfd_go_intent() -> u8 {
648 7
649}
650
651fn default_wfd_listen_channel() -> u8 {
652 6
653}
654
655fn default_wfd_op_channel() -> u8 {
656 6
657}
658
659fn default_wfd_connect_method() -> String {
660 "pbc".into()
661}
662
663fn default_bluetooth_interface() -> String {
664 #[cfg(target_os = "macos")]
665 {
666 "bridge0".into()
667 }
668
669 #[cfg(not(target_os = "macos"))]
670 {
671 "auto".into()
672 }
673}
674
675fn default_bluetooth_radio_discovery_enabled() -> bool {
676 true
677}
678
679fn default_bluetooth_device_name_prefix() -> String {
680 "PIM-".into()
681}
682
683fn default_bluetooth_connect_pan() -> bool {
684 true
685}
686
687fn default_bluetooth_nap_bridge() -> String {
688 "br-bt".into()
689}
690
691fn default_bluetooth_nap_bridge_addr() -> String {
692 "192.168.44.1/24".into()
693}
694
695fn default_bluetooth_dhcp_enabled() -> bool {
696 true
697}
698
699fn default_bluetooth_dhcp_lease_time() -> String {
700 "12h".into()
701}
702
703fn default_bluetooth_request_dhcp() -> bool {
704 true
705}
706
707fn default_bluetooth_auto_discover_peers() -> bool {
708 true
709}
710
711fn default_bluetooth_poll_interval_ms() -> u64 {
712 2_000
713}
714
715fn default_bluetooth_scan_interval_ms() -> u64 {
716 5_000
717}
718
719fn default_bluetooth_peer_discovery_interval_ms() -> u64 {
720 2_000
721}
722
723fn default_bluetoothctl_timeout_s() -> u64 {
724 15
725}
726
727fn default_bluetooth_discoverable_timeout_s() -> u64 {
728 180
729}
730
731fn default_bluetooth_startup_timeout_ms() -> u64 {
732 15_000
733}
734
735fn default_bluetooth_rfcomm_channel() -> u8 {
736 22
737}
738
739fn default_bluetooth_rfcomm_outbound_enabled() -> bool {
740 true
741}
742
743fn default_bluetooth_rfcomm_poll_interval_ms() -> u64 {
744 30_000
745}
746
747fn default_bluetooth_rfcomm_bridge_to_tcp() -> bool {
748 true
749}
750
751fn default_bluetooth_rfcomm_discovery_enabled() -> bool {
752 true
753}
754
755fn default_bluetooth_rfcomm_inquiry_interval_ms() -> u64 {
756 60_000
757}
758
759impl Default for InterfaceConfig {
760 fn default() -> Self {
761 Self {
762 name: default_interface_name(),
763 mtu: default_mtu(),
764 mesh_ipv4_prefix: None,
765 mesh_ipv6_prefix: None,
766 }
767 }
768}
769
770impl Default for DiscoveryConfig {
771 fn default() -> Self {
772 Self {
773 enabled: default_discovery_enabled(),
774 port: default_discovery_port(),
775 broadcast_interval_ms: default_broadcast_interval_ms(),
776 peer_timeout_ms: default_peer_timeout_ms(),
777 connect_relays: default_connect_relays(),
778 connect_gateways: default_connect_gateways(),
779 }
780 }
781}
782
783impl Default for MeshKdfConfig {
784 fn default() -> Self {
785 Self {
786 m_cost_kib: default_mesh_kdf_m_cost_kib(),
787 t_cost: default_mesh_kdf_t_cost(),
788 p_cost: default_mesh_kdf_p_cost(),
789 }
790 }
791}
792
793fn default_mesh_kdf_m_cost_kib() -> u32 {
794 65536
795}
796
797fn default_mesh_kdf_t_cost() -> u32 {
798 3
799}
800
801fn default_mesh_kdf_p_cost() -> u32 {
802 1
803}
804
805impl Default for TransportConfig {
806 fn default() -> Self {
807 Self {
808 r#type: default_transport_type(),
809 listen_port: default_listen_port(),
810 max_reconnect_attempts: default_max_reconnect_attempts(),
811 connect_timeout_ms: default_connect_timeout_ms(),
812 peer_cleanup: PeerCleanupConfig::ephemeral_default(),
813 }
814 }
815}
816
817impl Default for RoutingConfig {
818 fn default() -> Self {
819 Self {
820 max_hops: default_max_hops(),
821 algorithm: default_route_algorithm(),
822 route_expiry_s: default_route_expiry_s(),
823 dns_servers: default_dns_servers(),
824 }
825 }
826}
827
828impl Default for GatewayConfig {
829 fn default() -> Self {
830 Self {
831 enabled: false,
832 nat_interface: default_nat_interface(),
833 max_connections: default_max_connections(),
834 }
835 }
836}
837
838impl Default for WifiDirectConfig {
839 fn default() -> Self {
840 Self {
841 enabled: false,
842 interface: default_wfd_interface(),
843 go_intent: default_wfd_go_intent(),
844 listen_channel: default_wfd_listen_channel(),
845 op_channel: default_wfd_op_channel(),
846 connect_method: default_wfd_connect_method(),
847 peer_cleanup: PeerCleanupConfig::bluetooth_default(),
848 }
849 }
850}
851
852impl Default for BluetoothConfig {
853 fn default() -> Self {
854 Self {
855 enabled: false,
856 interface: default_bluetooth_interface(),
857 radio_discovery_enabled: default_bluetooth_radio_discovery_enabled(),
858 device_name_prefix: default_bluetooth_device_name_prefix(),
859 local_alias: String::new(),
860 connect_pan: default_bluetooth_connect_pan(),
861 serve_nap: false,
862 nap_bridge: default_bluetooth_nap_bridge(),
863 nap_bridge_addr: default_bluetooth_nap_bridge_addr(),
864 dhcp_enabled: default_bluetooth_dhcp_enabled(),
865 dhcp_range: None,
866 dhcp_lease_time: default_bluetooth_dhcp_lease_time(),
867 dhcp_dns: None,
868 request_dhcp: default_bluetooth_request_dhcp(),
869 auto_discover_peers: default_bluetooth_auto_discover_peers(),
870 poll_interval_ms: default_bluetooth_poll_interval_ms(),
871 scan_interval_ms: default_bluetooth_scan_interval_ms(),
872 peer_discovery_interval_ms: default_bluetooth_peer_discovery_interval_ms(),
873 bluetoothctl_timeout_s: default_bluetoothctl_timeout_s(),
874 discoverable_timeout_s: default_bluetooth_discoverable_timeout_s(),
875 startup_timeout_ms: default_bluetooth_startup_timeout_ms(),
876 peer_cleanup: PeerCleanupConfig::bluetooth_default(),
877 }
878 }
879}
880
881impl Default for BluetoothRfcommConfig {
882 fn default() -> Self {
883 Self {
884 enabled: false,
885 channel: default_bluetooth_rfcomm_channel(),
886 device_name_prefix: default_bluetooth_device_name_prefix(),
887 outbound_enabled: default_bluetooth_rfcomm_outbound_enabled(),
888 poll_interval_ms: default_bluetooth_rfcomm_poll_interval_ms(),
889 bridge_to_tcp: default_bluetooth_rfcomm_bridge_to_tcp(),
890 discovery_enabled: default_bluetooth_rfcomm_discovery_enabled(),
891 inquiry_interval_ms: default_bluetooth_rfcomm_inquiry_interval_ms(),
892 peer_cleanup: PeerCleanupConfig::bluetooth_default(),
893 }
894 }
895}
896
897impl Default for SecurityConfig {
898 fn default() -> Self {
899 Self {
900 key_file: default_key_file(),
901 require_encryption: default_require_encryption(),
902 authorization_policy: AuthorizationPolicy::default(),
903 authorized_peers: Vec::new(),
904 trust_store_file: default_trust_store_file(),
905 }
906 }
907}