zvault_server/config.rs
1//! Server configuration for `ZVault`.
2//!
3//! Loads configuration from environment variables with sensible defaults.
4//! All settings can be overridden via `ZVAULT_*` environment variables.
5
6use std::net::SocketAddr;
7
8/// Server configuration.
9#[derive(Debug, Clone)]
10pub struct ServerConfig {
11 /// Address to bind the HTTP listener to.
12 pub bind_addr: SocketAddr,
13 /// Storage backend type.
14 pub storage_backend: StorageBackendType,
15 /// Log level filter (e.g., `info`, `debug`, `warn`).
16 pub log_level: String,
17 /// Path to the audit log file (if file audit is enabled).
18 pub audit_file_path: Option<String>,
19 /// Whether to enable the default transit engine mount.
20 pub enable_transit: bool,
21 /// Lease expiry scan interval in seconds.
22 pub lease_scan_interval_secs: u64,
23 /// Whether to skip `mlock` (for development without root/`CAP_IPC_LOCK`).
24 pub disable_mlock: bool,
25 /// Spring OAuth configuration (optional — enables "Sign in with Spring").
26 pub spring_oauth: Option<SpringOAuthConfig>,
27}
28
29/// Configuration for Spring OAuth 2.0 / OIDC integration.
30#[derive(Debug, Clone)]
31pub struct SpringOAuthConfig {
32 /// Base URL of the Spring auth server (e.g., `https://auth.puddlesearch.in`).
33 pub auth_url: String,
34 /// OAuth client ID registered in Spring.
35 pub client_id: String,
36 /// OAuth client secret.
37 pub client_secret: String,
38 /// Redirect URI for the callback (auto-derived if not set).
39 pub redirect_uri: Option<String>,
40 /// Default vault policy to assign to Spring-authenticated users.
41 pub default_policy: String,
42 /// Vault policy to assign to Spring admin users.
43 pub admin_policy: String,
44}
45
46/// Supported storage backend types.
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum StorageBackendType {
49 /// In-memory (development only, data lost on restart).
50 Memory,
51 /// `RocksDB` persistent storage.
52 RocksDb { path: String },
53 /// Redb persistent storage.
54 Redb { path: String },
55 /// PostgreSQL persistent storage (recommended for Railway / cloud).
56 Postgres { url: String },
57}
58
59impl ServerConfig {
60 /// Load configuration from environment variables.
61 ///
62 /// Environment variables:
63 /// - `PORT` — port to bind on (Railway convention, binds to `0.0.0.0`)
64 /// - `ZVAULT_BIND_ADDR` — full bind address (overrides `PORT`, default: `127.0.0.1:8200`)
65 /// - `ZVAULT_STORAGE` — `memory`, `rocksdb`, `redb`, or `postgres` (default: `memory`)
66 /// - `ZVAULT_STORAGE_PATH` — path for persistent backends (default: `./data`)
67 /// - `DATABASE_URL` — PostgreSQL connection string (required when `ZVAULT_STORAGE=postgres`)
68 /// - `ZVAULT_STORAGE_PATH` — path for persistent backends (default: `./data`)
69 /// - `ZVAULT_LOG_LEVEL` — log filter (default: `info`)
70 /// - `ZVAULT_AUDIT_FILE` — path to audit log file (optional)
71 /// - `ZVAULT_ENABLE_TRANSIT` — enable transit engine (default: `true`)
72 /// - `ZVAULT_LEASE_SCAN_INTERVAL` — seconds between lease scans (default: `60`)
73 /// - `ZVAULT_DISABLE_MLOCK` — skip `mlockall` for dev environments (default: `false`)
74 #[must_use]
75 pub fn from_env() -> Self {
76 // Priority: ZVAULT_BIND_ADDR > PORT (Railway) > default 127.0.0.1:8200
77 let bind_addr = if let Ok(addr) = std::env::var("ZVAULT_BIND_ADDR") {
78 addr.parse()
79 .unwrap_or_else(|_| SocketAddr::from(([127, 0, 0, 1], 8200)))
80 } else if let Ok(port_str) = std::env::var("PORT") {
81 let port: u16 = port_str.parse().unwrap_or(8200);
82 SocketAddr::from(([0, 0, 0, 0], port))
83 } else {
84 SocketAddr::from(([127, 0, 0, 1], 8200))
85 };
86
87 let storage_path = std::env::var("ZVAULT_STORAGE_PATH")
88 .unwrap_or_else(|_| "./data".to_owned());
89
90 let storage_backend = match std::env::var("ZVAULT_STORAGE")
91 .unwrap_or_else(|_| "memory".to_owned())
92 .to_lowercase()
93 .as_str()
94 {
95 "rocksdb" => StorageBackendType::RocksDb { path: storage_path },
96 "redb" => StorageBackendType::Redb { path: storage_path },
97 "postgres" | "postgresql" => {
98 let url = std::env::var("DATABASE_URL")
99 .unwrap_or_else(|_| "postgres://localhost/zvault".to_owned());
100 StorageBackendType::Postgres { url }
101 }
102 _ => StorageBackendType::Memory,
103 };
104
105 let log_level = std::env::var("ZVAULT_LOG_LEVEL")
106 .unwrap_or_else(|_| "info".to_owned());
107
108 let audit_file_path = std::env::var("ZVAULT_AUDIT_FILE").ok();
109
110 let enable_transit = std::env::var("ZVAULT_ENABLE_TRANSIT")
111 .map(|v| v != "false" && v != "0")
112 .unwrap_or(true);
113
114 let lease_scan_interval_secs = std::env::var("ZVAULT_LEASE_SCAN_INTERVAL")
115 .ok()
116 .and_then(|v| v.parse().ok())
117 .unwrap_or(60);
118
119 let disable_mlock = std::env::var("ZVAULT_DISABLE_MLOCK")
120 .map(|v| v == "true" || v == "1")
121 .unwrap_or(false);
122
123 // Spring OAuth — enabled when SPRING_AUTH_URL is set.
124 let spring_oauth = std::env::var("SPRING_AUTH_URL").ok().map(|auth_url| {
125 SpringOAuthConfig {
126 auth_url,
127 client_id: std::env::var("SPRING_CLIENT_ID")
128 .unwrap_or_else(|_| "zvault-dashboard".to_owned()),
129 client_secret: std::env::var("SPRING_CLIENT_SECRET")
130 .unwrap_or_default(),
131 redirect_uri: std::env::var("SPRING_REDIRECT_URI").ok(),
132 default_policy: std::env::var("SPRING_DEFAULT_POLICY")
133 .unwrap_or_else(|_| "default".to_owned()),
134 admin_policy: std::env::var("SPRING_ADMIN_POLICY")
135 .unwrap_or_else(|_| "root".to_owned()),
136 }
137 });
138
139 Self {
140 bind_addr,
141 storage_backend,
142 log_level,
143 audit_file_path,
144 enable_transit,
145 lease_scan_interval_secs,
146 disable_mlock,
147 spring_oauth,
148 }
149 }
150}