Skip to main content

cliban_server/
config.rs

1//! `cliband` daemon configuration, loaded from a TOML file.
2
3use std::path::{Path, PathBuf};
4
5use serde::Deserialize;
6
7use crate::ServerError;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum SignupPolicy {
12    Open,
13    Token,
14    Closed,
15}
16
17#[derive(Debug, Clone, PartialEq, Deserialize)]
18#[serde(default, deny_unknown_fields)]
19pub struct ServerConfig {
20    /// Address the SSH listener binds, e.g. "0.0.0.0:2222".
21    pub listen_addr: String,
22    /// Directory for the host key (and, in later tickets, tenant data).
23    pub data_dir: PathBuf,
24    /// Who may sign up; enforced by the signup control command.
25    pub signup_policy: SignupPolicy,
26    /// Shared token required when `signup_policy = "token"`. When the policy
27    /// is "token" and this is unset (the default), signup is denied entirely.
28    pub signup_token: Option<String>,
29    /// Max tenants a single public key may create. 0 = unlimited.
30    pub max_tenants_per_key: u32,
31    /// Global tenant cap. 0 = unlimited.
32    pub max_tenants: u32,
33}
34
35impl Default for ServerConfig {
36    fn default() -> Self {
37        Self {
38            listen_addr: "0.0.0.0:2222".into(),
39            data_dir: default_data_dir(),
40            signup_policy: SignupPolicy::Token,
41            signup_token: None,
42            max_tenants_per_key: 5,
43            max_tenants: 0,
44        }
45    }
46}
47
48impl ServerConfig {
49    pub fn parse(text: &str) -> Result<Self, ServerError> {
50        Ok(toml::from_str(text)?)
51    }
52
53    pub fn load(path: &Path) -> Result<Self, ServerError> {
54        Self::parse(&std::fs::read_to_string(path)?)
55    }
56}
57
58/// `$XDG_DATA_HOME/cliband`, falling back to `~/.local/share/cliband`.
59/// (Same shape as `cliban_core::paths::data_dir`, but the daemon keeps its
60/// own directory name.)
61fn default_data_dir() -> PathBuf {
62    match std::env::var("XDG_DATA_HOME") {
63        Ok(v) if !v.is_empty() => PathBuf::from(v).join("cliband"),
64        _ => {
65            let home = std::env::var("HOME").unwrap_or_else(|_| "/".into());
66            PathBuf::from(home)
67                .join(".local")
68                .join("share")
69                .join("cliband")
70        }
71    }
72}