Skip to main content

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}