C5Store for Rust
C5Store is a Rust library providing a unified store for configuration and secrets. It aims to be a single point of access for your application's configuration needs, consolidating values from various sources (like YAML and TOML files or directories), handling environment variable overrides, managing secrets securely via built-in decryption, and allowing dynamic loading through providers.
The core idea is to simplify configuration management in complex applications by offering a hierarchical, type-aware, extensible, and environment-aware configuration layer.
Key Features
- Unified Access: Retrieve configuration values using simple dot-notation key paths (e.g.,
database.connection.pool_size). - Multiple Sources & Merging: Load configuration from YAML and TOML files, or entire directories containing such files. Configuration is intelligently merged based on load order.
- Environment Variable Overrides: Seamlessly override any configuration value using environment variables (e.g.,
C5_DATABASE__HOST=...). Values are intelligently parsed (bool, int, float, string). - Type-Safe Retrieval: Get values converted directly into expected Rust types using
get_into::<T>(), returning aResultfor robust error handling. - Flexible Struct Deserialization: Deserialize configuration sections directly into your custom Rust structs using
get_into_struct::<T>(). Supports both nested maps (from files) and flattened key structures (e.g., from environment variables). - Integrated Secrets Management (Optional Feature):
- Transparently decrypt secrets defined within configuration files using the
.c5encvalkey. - Supports pluggable decryption algorithms (includes
base64andecies_x25519). - Securely load decryption keys from files (including
.pem) or environment variables.
- Transparently decrypt secrets defined within configuration files using the
- Value Providers: Defer loading of specific configuration sections to external sources (e.g., files) using a provider system. Includes a built-in
C5FileValueProvider. - Periodic Refresh: Value providers can be configured to automatically refresh their data at specified intervals.
- Change Notifications: Subscribe to changes in configuration values at specific key paths or their ancestors using
subscribe(basic) orsubscribe_detailed(includes old value). Notifications are debounced. - Hierarchical Structure: Access nested configuration values easily and create "branches" for context-specific views of the configuration using
branch(). - Source Tracking: Identify the origin of any configuration value (File, Env Var, Provider) using
get_source(). .envFile Support (Optional Feature): Load environment variables from.envfiles at startup.- Extensible: Designed with traits for custom value providers and secret decryptors.
- Telemetry Hooks: Basic interfaces for integrating custom logging and statistics recording.
- Optional Feature Flags: Fine-tune dependencies (
dotenv,toml,secrets).
Getting Started
-
Add Dependency: Add
c5storeto yourCargo.toml. Enable optional features as needed:[] # Use the latest version = "0.3.1" # Example enabling .env file support (optional) # c5store = { version = "0.3.1", features = ["dotenv"] } # Example disabling default secrets support (optional, smaller binary) # c5store = { version = "0.3.1", default-features = false } # Other necessary dependencies = { = "1", = ["derive"] } -
Basic Usage:
use ; // Import types use PathBuf; use Deserialize; // Needed for get_into_struct // Example struct for deserialization
Configuration Files & Directories
C5Store loads configuration from specified paths in the create_c5store call. These paths can be:
- YAML files (
.yaml,.yml) - TOML files (
.toml) - Requirestomlfeature. - Directories: All files within the directory with supported extensions (
.yaml,.yml,.toml) will be loaded and merged alphabetically.
Configuration sources are merged in the order they are processed (files listed explicitly first, then files within directories alphabetically). Values from later sources override values from earlier sources for the same key path. Maps (objects/tables) are merged recursively; other types are replaced entirely.
Example (config/common.yaml):
service:
name: MyAwesomeApp
port: 8080
database:
host: prod-db.example.com
pool_size: 50
Example (config/local.toml):
# Overrides common.yaml values
# Assumes local.toml is processed after common.yaml
= 9090 # Overrides port 8080
[]
= "localhost" # Overrides prod host
= "dev_user" # Adds a new key
# service.name and database.pool_size are inherited from common.yaml
Environment Variables & Loading Priority
C5Store supports overriding configuration values using environment variables after all files have been loaded and merged.
- Prefix: Variables starting with
C5_(by default) are processed. - Separator: Double underscore (
__) is used to denote nesting levels (e.g.,C5_DATABASE__HOSTmaps todatabase.host). - Case: The key derived from the environment variable is converted to lowercase (e.g.,
C5_SERVICE__NAMEbecomesservice.name). - Value Parsing: Environment variable values are parsed into the most appropriate
C5DataValuetype (Boolean, Integer, Float, String).
Loading Priority (Highest to Lowest):
- Environment Variables (e.g.,
C5_...) - Configuration Files/Directories (processed in the order specified/discovered, with later files/directories overriding earlier ones).
Optional Features (dotenv, toml, secrets)
C5Store uses Cargo features to enable optional functionality:
dotenv:- Enables loading environment variables from a
.envfile at startup usingC5StoreOptions::dotenv_path. - Requires the
dotenvycrate. .envfiles are loaded before process environment variables are read, allowing process variables to override.envvariables.
- Enables loading environment variables from a
toml:- Enables parsing of
.tomlconfiguration files. - Requires the
tomlcrate.
- Enables parsing of
secrets:- Enables all secrets management functionality (loading
.c5encval,SecretOptions,SecretKeyStore, decryptors). - Requires crypto dependencies (
ecies_25519,curve25519-parser,sha2). - Enabled by default. Disable using
default-features = falseif secrets are not needed.
- Enables all secrets management functionality (loading
full:- Convenience feature to enable
dotenv,toml, andsecrets.
- Convenience feature to enable
[]
# Minimal - no .env, no secrets, no toml
# c5store = { version = "0.3.1", default-features = false }
# Default - secrets and yaml enabled
# c5store = "0.3.1"
# Enable all common features
= { = "0.3.1", = ["full"] }
# Just enable .env support
# c5store = { version = "0.3.1", default-features = false, features = ["dotenv"] }
Secrets Management (secrets feature)
(Requires the secrets feature, enabled by default).
Secrets are defined using a special .c5encval key (configurable via SecretOptions::secret_key_path_segment) within your configuration.
Structure:
# YAML Example
some_secret_key:
.c5encval:
# TOML Example
# [some_secret_key]
# ".c5encval" = ["<algorithm>", "<key_name>", "<base64_encrypted_data>"]
<algorithm>: Name of registeredSecretDecryptor(e.g.,"base64","ecies_x25519").<key_name>: Name used to look up the decryption key in theSecretKeyStore.<base64_encrypted_data>: The secret value, encrypted and then Base64 encoded.
Configuration (SecretOptions):
Configure secrets via the secret_opts field in C5StoreOptions.
use ;
// Only if using secrets explicitly
use ;
use EciesX25519;
use PathBuf;
// ... inside setup code ...
let mut options = default;
// Gate configuration if secrets might be disabled
let config_paths = vec!;
let = create_c5store?;
// Retrieving the secret automatically attempts decryption
match store.
Value Providers
Value providers allow parts of your configuration to be loaded dynamically from external sources (like files, databases, or remote services). Mark a section in YAML/TOML with a .provider key specifying the provider's name. Register providers using C5StoreMgr::set_value_provider. C5Store includes a C5FileValueProvider for loading content from files specified in the configuration.
Example (config/providers.yaml):
files:
large_config:
.provider: resource # Name matches registered provider
path: large_data.json # Path relative to provider base or absolute
format: json # Instruct provider to parse as JSON
raw_template:
.provider: resource
path: template.txt
# format: raw (default)
# encoding: utf8 (default)
Registration:
use C5FileValueProvider;
// ... inside main after create_c5store ...
// Create provider, setting its base path for relative 'path' values
let file_provider = default; // Use built-in JSON/YAML deserializers
// Register with the store manager, optionally enable refresh
store_mgr.set_value_provider;
// Now access values loaded by the provider
match store.
Change Notifications
Subscribe to configuration changes using subscribe (new value only) or subscribe_detailed (new and old value). Listeners are called after a configurable debounce period (C5StoreOptions::change_delay_period).
// Subscribe to changes under the 'database' prefix
store.subscribe_detailed;
// Programmatic changes (or provider refreshes) will trigger notifications later
// store.set("database.pool_size", 100.into()); // Example change
License
This project is licensed under the Mozilla Public License Version 2.0 (MPL-2.0). See LICENSE file for details.
Contributing
Contributions welcome! Please open issues or PRs on the project repository.
Changelog
See CHANGELOG.md for a history of notable changes.