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
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct HyphaConfig {
pub defaults: Defaults,
pub cache: CacheConfig,
}
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct Defaults {
/// Default synapse for queries (sense, lineage, search)
pub synapse: Option<String>,
/// Default domain for publishing (release)
pub domain: Option<String>,
/// Taste-specific overrides for auto-submission.
/// Separate because taste may use a different domain (hub subdomain)
/// and synapse (cmnhub.com) than general queries/publishing.
pub taste: TasteDefaults,
}
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct TasteDefaults {
/// Synapse to submit taste reports to
pub synapse: Option<String>,
/// Domain to sign taste reports with
pub domain: Option<String>,
}
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KeyTrustRefreshMode {
/// Refresh key trust only when trust cache is expired/missing.
#[default]
Expired,
/// Always refresh key trust from network sources.
Always,
/// Never refresh from network; rely on local trust cache only.
Offline,
}
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SynapseWitnessMode {
/// Allow Synapse key witness when domain confirmation is unavailable.
#[default]
Allow,
/// Require direct domain confirmation (or cached trust); do not use Synapse witness.
RequireDomain,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CacheConfig {
/// Custom cache directory path (default: $CMN_HOME/hypha/cache/)
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
/// cmn.json cache TTL in seconds (default: 300 = 5 minutes)
pub cmn_ttl_s: u64,
/// Key trust cache TTL in seconds (default: 604800 = 7 days)
pub key_trust_ttl_s: u64,
/// Key trust refresh strategy (default: expired)
pub key_trust_refresh_mode: KeyTrustRefreshMode,
/// Key trust fallback policy when domain is unreachable (default: allow)
pub key_trust_synapse_witness_mode: SynapseWitnessMode,
/// Maximum spore archive HTTP response body size in bytes (default: 1 GB)
pub spore_max_download_bytes: u64,
/// Maximum total bytes to extract from a spore archive (default: 512 MB)
pub spore_max_extract_bytes: u64,
/// Maximum number of files to extract from a spore archive (default: 100_000)
pub spore_max_extract_files: u64,
/// Maximum size of a single file in a spore archive in bytes (default: 256 MB)
pub spore_max_extract_file_bytes: u64,
/// Path components rejected when receiving spore content.
pub spore_reject_path_components: Vec<String>,
/// Clock skew tolerance in seconds for key trust TTL checks (default: 300 = 5 minutes).
/// Adds a grace period to prevent false "key_untrusted" errors caused by clock drift
/// between the local machine and the publishing domain.
pub clock_skew_tolerance_s: u64,
/// Whether the initial key for a domain must come from the domain itself (TOFU).
/// true = more secure, first contact requires domain to be online.
/// false = allows synapse to provide initial key (less secure).
pub require_domain_first_key: bool,
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
path: None,
cmn_ttl_s: 300,
key_trust_ttl_s: 604800,
key_trust_refresh_mode: KeyTrustRefreshMode::Expired,
key_trust_synapse_witness_mode: SynapseWitnessMode::Allow,
spore_max_download_bytes: 1024 * 1024 * 1024,
spore_max_extract_bytes: 512 * 1024 * 1024,
spore_max_extract_files: 100_000,
spore_max_extract_file_bytes: 256 * 1024 * 1024,
spore_reject_path_components: vec![".git".to_string(), ".cmn".to_string()],
clock_skew_tolerance_s: 300,
require_domain_first_key: true,
}
}
}