photoncloud-monorepo/specifications/configuration.md
centra 5c6eb04a46 T036: Add VM cluster deployment configs for nixos-anywhere
- netboot-base.nix with SSH key auth
- Launch scripts for node01/02/03
- Node configuration.nix and disko.nix
- Nix modules for first-boot automation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 09:59:19 +09:00

6.4 KiB

Unified Configuration Guidelines

This document outlines the standardized approach for managing configuration across all components of the Cloud project. Adhering to these guidelines ensures consistency, maintainability, and ease of operation for all services.

1. Layered Configuration with config-rs

All components MUST use the config-rs crate for managing application settings. This allows for a robust, layered configuration system with clear precedence:

  1. Defaults: Sensible, hard-coded default values provided by the application. These are the lowest precedence.
  2. Environment Variables: Values loaded from environment variables. These override defaults.
  3. Configuration Files: Values loaded from a TOML configuration file. These override environment variables and defaults.
  4. Command-Line Arguments: Values provided via command-line arguments. These are the highest precedence and always override all other sources.

Implementation Details:

  • ServerConfig::default() as base: Use toml::to_string(&MyServerConfig::default()) to provide the base default configuration to config-rs. This ensures Default implementations are the source of truth for base settings.
  • Workspace Dependency: The config crate should be a [workspace.dependencies] entry in the component's Cargo.toml, and member crates should reference it with config.workspace = true.

2. Configuration File Format: TOML

All configuration files MUST be in the TOML format.

  • config-rs should be configured to read TOML files (config::FileFormat::Toml).
  • Configuration structs MUST derive serde::Serialize and serde::Deserialize to enable config-rs to populate them.

3. Command-Line Argument Overrides with clap

All components MUST use clap::Parser for parsing command-line arguments.

  • Critical configuration parameters MUST be exposed as CLI arguments.
  • CLI arguments are applied after config-rs has loaded all other configuration sources, ensuring they have the highest precedence. This typically involves manually setting fields in the deserialized configuration struct if Option<T> arguments are provided.

4. Consistent Environment Variable Prefixes

All components MUST use a consistent naming convention for environment variables.

  • Prefix Format: UPPERCASE_COMPONENT_NAME_ (e.g., CHAINFIRE_, FLAREDB_, IAM_).
  • Nested Fields: Use double underscores (__) to represent nested fields in the configuration structure.
    • Example: For network.api_addr, the environment variable would be CHAINFIRE_NETWORK__API_ADDR.
  • Case Conversion: Configure config-rs to convert environment variable names to snake_case to match Rust struct field names (.convert_case(config::Case::Snake)).

5. Configuration Struct Guidelines

  • Top-Level ServerConfig: Each executable component (e.g., chainfire-server, flaredb-server) should have a single top-level ServerConfig (or similar name) struct that encapsulates all its settings.
  • Modularity: Break down ServerConfig into smaller, logical sub-structs (e.g., NodeConfig, NetworkConfig, StorageConfig, ClusterConfig) to improve readability and maintainability.
  • Default Implementation: All configuration structs and their sub-structs MUST implement Default to provide sensible starting values.
  • Doc Comments: Use clear doc comments for all configuration fields, explaining their purpose and acceptable values.

6. TLS/mTLS Configuration (Security)

All services MUST support optional TLS configuration for production deployments. The TLS configuration pattern ensures consistent security across all components.

Standard TLS Configuration Structure

use serde::{Deserialize, Serialize};

/// TLS configuration for gRPC servers
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
    /// Path to server certificate file (PEM format)
    pub cert_file: String,

    /// Path to server private key file (PEM format)
    pub key_file: String,

    /// Path to CA certificate file for client verification (optional, enables mTLS)
    pub ca_file: Option<String>,

    /// Require client certificates (mTLS mode)
    #[serde(default)]
    pub require_client_cert: bool,
}

Integration Pattern

Services should include tls: Option<TlsConfig> in their server configuration:

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerSettings {
    /// Listen address
    pub addr: SocketAddr,

    /// TLS configuration (optional)
    pub tls: Option<TlsConfig>,
}

Server Builder Pattern (tonic)

use tonic::transport::{Server, ServerTlsConfig, Identity, Certificate};

let mut server = Server::builder();

if let Some(tls_config) = &config.server.tls {
    let cert = tokio::fs::read(&tls_config.cert_file).await?;
    let key = tokio::fs::read(&tls_config.key_file).await?;
    let server_identity = Identity::from_pem(cert, key);

    let tls = if tls_config.require_client_cert {
        // mTLS: require and verify client certificates
        let ca_cert = tokio::fs::read(
            tls_config.ca_file.as_ref()
                .ok_or("ca_file required when require_client_cert=true")?
        ).await?;
        let ca = Certificate::from_pem(ca_cert);

        ServerTlsConfig::new()
            .identity(server_identity)
            .client_ca_root(ca)
    } else {
        // TLS only: no client certificate required
        ServerTlsConfig::new()
            .identity(server_identity)
    };

    server = server.tls_config(tls)?;
}

server.add_service(my_service).serve(config.server.addr).await?;

TOML Configuration Example

[server]
addr = "0.0.0.0:50051"

[server.tls]
cert_file = "/etc/centra-cloud/certs/iam/server.crt"
key_file = "/etc/centra-cloud/certs/iam/server.key"
ca_file = "/etc/centra-cloud/certs/ca.crt"
require_client_cert = true  # Enable mTLS

Certificate Management

  • Development: Use scripts/generate-dev-certs.sh to create self-signed CA and service certificates
  • Production: Integrate with external PKI or use cert-manager for automated rotation
  • Storage: Certificates stored in /etc/centra-cloud/certs/ (NixOS managed)
  • Permissions: Private keys must be readable only by service user (chmod 600)

By following these guidelines, we aim to achieve a unified, operator-friendly, and robust configuration system across the entire Cloud project.