1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6#[serde(default)]
7pub struct Config {
8 pub storage: StorageConfig,
9 pub search: SearchConfig,
10 pub validation: ValidationConfig,
11 pub privacy: PrivacyConfig,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(default)]
16pub struct StorageConfig {
17 pub retention_days: u32,
18 pub vacuum_interval_secs: u64,
19 pub max_db_size_mb: u64,
20 pub busy_timeout_ms: u32,
21 pub cache_size_kb: u32,
22 pub dedup_window_secs: u64,
23 pub encryption_enabled: bool,
24 pub entropy_threshold: f64,
27}
28
29impl Default for StorageConfig {
30 fn default() -> Self {
31 Self {
32 retention_days: 90,
33 vacuum_interval_secs: 604800,
34 max_db_size_mb: 500,
35 busy_timeout_ms: 5000,
36 cache_size_kb: 2048,
37 dedup_window_secs: 900,
38 encryption_enabled: false,
39 entropy_threshold: 0.35,
40 }
41 }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45#[serde(default)]
46pub struct SearchConfig {
47 pub default_limit: u32,
48 pub max_limit: u32,
49 pub min_relevance_score: Option<f64>,
54 pub preview_max_chars: usize,
56 pub preview_min_chars: usize,
58 pub column_weights: BTreeMap<String, f64>,
61}
62
63fn default_column_weights() -> BTreeMap<String, f64> {
64 [
65 ("key".to_string(), 10.0),
66 ("value".to_string(), 1.0),
67 ("tags".to_string(), 5.0),
68 ("source_type".to_string(), 0.5),
69 ("scope".to_string(), 0.5),
70 ]
71 .into_iter()
72 .collect()
73}
74
75impl Default for SearchConfig {
76 fn default() -> Self {
77 Self {
78 default_limit: 10,
79 max_limit: 50,
80 min_relevance_score: None,
81 preview_max_chars: 400,
82 preview_min_chars: 80,
83 column_weights: default_column_weights(),
84 }
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89#[serde(default)]
90pub struct ValidationConfig {
91 pub max_key_length: usize,
92 pub max_value_length: usize,
93 pub max_tags: usize,
94 pub max_tag_length: usize,
95}
96
97impl Default for ValidationConfig {
98 fn default() -> Self {
99 Self {
100 max_key_length: 256,
101 max_value_length: 2000,
102 max_tags: 20,
103 max_tag_length: 64,
104 }
105 }
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(default)]
110pub struct PrivacyConfig {
111 pub secret_patterns: Vec<String>,
112 pub extra_patterns: Vec<String>,
113 pub replace_defaults: bool,
114 pub file_deny_list: Vec<String>,
115}
116
117impl Default for PrivacyConfig {
118 fn default() -> Self {
119 Self {
120 secret_patterns: default_secret_patterns(),
121 extra_patterns: Vec::new(),
122 replace_defaults: false,
123 file_deny_list: vec![
124 ".env".into(),
125 ".env.*".into(),
126 "*.pem".into(),
127 "*.key".into(),
128 "*.p12".into(),
129 "*.pfx".into(),
130 "id_rsa".into(),
131 "id_ed25519".into(),
132 "id_ecdsa".into(),
133 "*.secret".into(),
134 "credentials.json".into(),
135 ],
136 }
137 }
138}
139
140pub fn default_secret_patterns() -> Vec<String> {
141 vec![
142 r"AKIA[0-9A-Z]{16}".into(),
143 r"-----BEGIN [A-Z ]*PRIVATE KEY-----".into(),
144 r"(?i)(api[_-]?key|token|secret|password)\s*[:=]\s*\S+".into(),
145 r"(?i)mongodb(\+srv)?://[^\s]+".into(),
146 r"(?i)postgres(ql)?://[^\s]+".into(),
147 r"(?i)mysql://[^\s]+".into(),
148 r"(?i)redis://[^\s]+".into(),
149 r"ghp_[a-zA-Z0-9]{36}".into(),
150 r"sk-[a-zA-Z0-9]{48}".into(),
151 r"xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]+".into(),
152 ]
153}