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
//! Governance and ban-list sharing configuration.
use serde::{Deserialize, Serialize};
/// Governance message relay configuration
#[cfg(feature = "governance")]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GovernanceConfig {
/// Enable governance message relay (default: false)
/// If false, governance messages are gossiped but not forwarded to blvm-commons
#[serde(default = "crate::config::default_false")]
pub enabled: bool,
/// URL for blvm-commons internal API (e.g., "http://10.0.0.2:8080")
/// Used for forwarding P2P governance messages via VPN
#[serde(default)]
pub commons_url: Option<String>,
/// API key for authenticating with blvm-commons internal API
/// Can also be set via COMMONS_API_KEY environment variable
#[serde(default)]
pub api_key: Option<String>,
}
#[cfg(feature = "governance")]
impl GovernanceConfig {
/// Configured key, or `COMMONS_API_KEY` when the config value is empty.
pub fn resolved_api_key(&self) -> Option<String> {
self.api_key
.clone()
.filter(|key| !key.is_empty())
.or_else(|| {
std::env::var("COMMONS_API_KEY")
.ok()
.filter(|key| !key.is_empty())
})
}
}
/// Ban list sharing configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BanListSharingConfig {
/// Enable ban list sharing
#[serde(default = "crate::config::default_true")]
pub enabled: bool,
/// Share mode: Immediate, Periodic, or Disabled
#[serde(default = "default_ban_share_mode")]
pub share_mode: BanShareMode,
/// Periodic sharing interval in seconds (only used if share_mode is Periodic)
#[serde(default = "default_periodic_interval")]
pub periodic_interval_seconds: u64,
/// Minimum ban duration to share (seconds, 0 = all)
#[serde(default = "default_min_ban_duration")]
pub min_ban_duration_to_share: u64,
/// Peers allowed to install bans on this node. Empty means this node
/// sends its own list and applies none. An entry is an IP or `ip:port`.
#[serde(default)]
pub trusted_peers: Vec<String>,
/// Longest peer-supplied ban this node will install, measured from now.
#[serde(default = "default_max_accepted_ban_seconds")]
pub max_accepted_ban_seconds: u64,
}
fn default_max_accepted_ban_seconds() -> u64 {
7 * 24 * 3600
}
/// Ban share mode
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BanShareMode {
/// Share immediately when auto-ban occurs
Immediate,
/// Share periodically (default)
Periodic,
/// Disabled
Disabled,
}
fn default_ban_share_mode() -> BanShareMode {
BanShareMode::Periodic
}
fn default_periodic_interval() -> u64 {
300 // 5 minutes
}
fn default_min_ban_duration() -> u64 {
3600 // 1 hour
}
impl Default for BanListSharingConfig {
fn default() -> Self {
Self {
enabled: true,
share_mode: BanShareMode::Periodic,
periodic_interval_seconds: 300,
min_ban_duration_to_share: 3600,
trusted_peers: Vec::new(),
max_accepted_ban_seconds: default_max_accepted_ban_seconds(),
}
}
}
impl BanListSharingConfig {
/// Sharing is on when the section is enabled and the mode is not `disabled`.
pub fn shares(&self) -> bool {
self.enabled && !matches!(self.share_mode, BanShareMode::Disabled)
}
/// True when `peer` is listed by IP or by full socket address.
pub fn trusts_peer(&self, peer: &std::net::SocketAddr) -> bool {
self.trusted_peers.iter().any(|entry| {
let entry = entry.trim();
if let Ok(addr) = entry.parse::<std::net::SocketAddr>() {
addr == *peer || (addr.port() == 0 && addr.ip() == peer.ip())
} else if let Ok(ip) = entry.parse::<std::net::IpAddr>() {
ip == peer.ip()
} else {
false
}
})
}
}