diff options
| author | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-10-31 07:39:18 -0500 |
|---|---|---|
| committer | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-10-31 07:39:18 -0500 |
| commit | d809d86182ab802472ae7d5e74cab66ba7bfa397 (patch) | |
| tree | 36718e8d91b13b2714cbfe7877353ac7625f2f44 /crates/config | |
| parent | dd1d5863ef865ebe29dc93d03ca6abc0aaf48bee (diff) | |
Package private chat for HTTPS and verify portable encrypted recovery
Diffstat (limited to 'crates/config')
| -rw-r--r-- | crates/config/src/lib.rs | 45 |
1 files changed, 43 insertions, 2 deletions
diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 0a648a2..ce509b6 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -32,6 +32,7 @@ const HOSTS: &[&str] = &[ "SEARCH_HOST", "WKD_HOST", "CHAT_HOST", + "CHAT_LOCAL_HOST", ]; const PUBLIC: &[&str] = &[ "BASE_DOMAIN", @@ -40,6 +41,9 @@ const PUBLIC: &[&str] = &[ "SEARCH_HOST", "WKD_HOST", "CHAT_HOST", + "CHAT_LOCAL_HOST", + "WEB_BIND", + "CHAT_UPSTREAM", "HTTP_PORT", "HTTPS_PORT", "LOCAL_HTTP_PORT", @@ -103,6 +107,32 @@ impl Config { } else { BTreeMap::new() }; + Self::from_values(explicit) + } + + #[doc = "Loads only declared public settings and the chat token from a container environment."] + #[doc = "# Errors"] + #[doc = "Rejects invalid configuration without importing unrelated host secrets."] + pub fn from_environment() -> Result<Self> { + let mut explicit = BTreeMap::new(); + for key in PUBLIC + .iter() + .copied() + .chain(std::iter::once("CHAT_SETUP_TOKEN")) + { + if let Some(value) = std::env::var_os(key) { + explicit.insert( + key.to_owned(), + value + .into_string() + .map_err(|_| anyhow::anyhow!("Invalid {key} environment value"))?, + ); + } + } + Self::from_values(explicit) + } + + fn from_values(explicit: BTreeMap<String, String>) -> Result<Self> { let mut values = defaults()?; values.retain(|key, _| PUBLIC.contains(&key.as_str())); values.extend(explicit.clone()); @@ -160,9 +190,16 @@ impl Config { (1024..=65_534).contains(&self.port("LOCAL_HTTP_PORT")?), "LOCAL_HTTP_PORT must be between 1024 and 65534" ); - for key in ["CGIT_UPSTREAM", "SEARXNG_UPSTREAM"] { + for key in ["CGIT_UPSTREAM", "SEARXNG_UPSTREAM", "CHAT_UPSTREAM"] { ensure!(endpoint(self.value(key)?), "Invalid {key} in root .env"); } + self.value("WEB_BIND")? + .parse::<std::net::IpAddr>() + .context("Invalid WEB_BIND")?; + ensure!( + self.value("CHAT_LOCAL_HOST")?.ends_with(".localhost"), + "CHAT_LOCAL_HOST must be a dedicated .localhost name" + ); self.chat_bind()?; let origin = self.value("CHAT_ORIGIN")?; ensure!( @@ -222,9 +259,10 @@ impl Config { values.insert("GIT_ORIGIN".into(), "/git".into()); values.insert("SEARCH_ORIGIN".into(), "/search".into()); values.insert("WKD_ORIGIN".into(), String::new()); + values.insert("LOCAL_HTTP_PORT".into(), port.to_string()); values.insert( "CHAT_PUBLIC_ORIGIN".into(), - self.value("CHAT_ORIGIN")?.into(), + format!("http://{}:{port}", self.value("CHAT_LOCAL_HOST")?), ); } Ok(values) @@ -271,6 +309,9 @@ pub fn initialize_public(root: &Path) -> Result<()> { writeln!(appended, "{key}={value}")?; } } + if path.exists() { + fs::set_permissions(&path, fs::Permissions::from_mode(0o600))?; + } if appended.is_empty() { return Ok(()); } |
