How dcg's Layered Configuration System Prioritizes Settings: A Technical Deep Dive
Environment variables override project-specific settings, which override user-level settings, which override system-level settings, with built-in defaults serving as the final fallback.
Destructive Command Guard (dcg) implements a deterministic configuration hierarchy that merges settings from multiple sources at startup. Understanding how dcg's layered configuration system prioritizes settings is essential for managing security policies across different environments. The system uses a strict precedence order where the most specific source always wins, ensuring that local overrides take precedence over global defaults.
The Five Precedence Layers
The configuration loader applies settings in a strict hierarchy from most specific to least specific. When conflicts occur, the higher-precedence layer overwrites values from lower layers.
1. Environment Variable Overrides
Individual settings supplied via DCG_<SECTION>_<KEY> variables take highest precedence. For example, DCG_GENERAL_VERBOSE=false disables verbose output even if configuration files enable it. These variables are parsed into a temporary ConfigLayer and merged on top of all other layers.
2. Project-Specific Configuration
The file located at <repo-root>/.dcg/config.toml defines settings for the specific repository where the command executes. Alternatively, the DCG_CONFIG environment variable can specify a custom path to a project-specific file.
3. User-Level Configuration
Settings in $HOME/.config/dcg/config.toml apply to all dcg invocations by the current user account. This layer loads once per user and provides account-specific defaults.
4. System-Level Configuration
Global defaults for every user on the host reside in /etc/dcg/config.toml. Administrators use this layer to enforce baseline security policies across the entire system.
5. Built-In Defaults
Hard-coded Default::default() implementations for each sub-struct (such as GeneralConfig and PacksConfig) provide fallback values when no other source defines a setting. These ensure the application always has valid configuration values.
How the Merge Algorithm Works
The merging process preserves whether a value was explicitly present or absent in each configuration file using Rust's Option<T> type.
Each TOML file deserializes into a partial representation called ConfigLayer. All scalar fields in these layer structs are Option<T>, which tracks explicitly set values versus omitted fields. This distinction prevents lower-precedence layers from overwriting higher-precedence explicit settings.
The merging routine walks the hierarchy sequentially—system → user → project → environment. For every configuration field, the algorithm applies the value from the first layer containing Some(value). Because later layers visit after earlier ones, a value from a higher-precedence layer overwrites any lower-precedence setting.
Finally, the system builds the complete Config struct by taking the merged ConfigLayer and filling any remaining None fields with hard-coded defaults from Config::default().
Configuration File Locations and Environment Variables
The environment variable prefix DCG_ and the special DCG_CONFIG variable are defined at the top of src/config.rs. When DCG_CONFIG is set, dcg loads that specific file instead of performing the standard layered search.
The standard resolution order searches:
- The path specified by
DCG_CONFIG(if set) ./.dcg/config.toml(project-specific)$HOME/.config/dcg/config.toml(user-level)/etc/dcg/config.toml(system-level)
Practical Implementation Examples
Override a single setting via environment variable:
export DCG_GENERAL_VERBOSE=false
dcg scan ./ # Verbose output disabled regardless of file settings
Define project-level settings in .dcg/config.toml:
[general]
verbose = true
[packs]
enabled = ["cloud.aws", "git"]
Use a custom configuration file path:
export DCG_CONFIG=/path/to/custom-dcg.toml
dcg scan . # Loads custom file instead of default hierarchy
Access the merged configuration programmatically:
let cfg = dcg::config::load(); // Returns fully merged `Config`
println!("Verbose? {}", cfg.general.verbose);
Key Source Files
Understanding the configuration system requires examining these specific files in the Dicklesworthstone/destructive_command_guard repository:
src/config.rs— Defines theConfigandConfigLayerstructs, environment variable constants, and the full merge implementation including theload_configfunction with the "Config file layering" logic.src/cli.rs— Parses command-line flags such as--config <path>that affect which configuration file loads.src/main.rs— Entrypoint that callsconfig::load()before any other subsystem initializes.src/allowlist.rsandsrc/packs/mod.rs— Consume the mergedConfigto determine active allowlist files and pattern packs.
Summary
- dcg's layered configuration system uses five precedence levels: environment variables, project-specific, user-level, system-level, and built-in defaults.
- The merge algorithm uses
Option<T>fields inConfigLayerstructs to distinguish between explicitly set values and omissions. - Higher-precedence layers overwrite lower-precedence layers in a sequential merge (system → user → project → environment).
- The
DCG_CONFIGenvironment variable bypasses the standard hierarchy to load a specific file directly. - All configuration logic resides in
src/config.rs, with theConfigLayerstruct enabling presence-aware merging.
Frequently Asked Questions
How does dcg handle conflicting settings between different configuration files?
The system applies a strict precedence order where the most specific source wins. When dcg::config::load() executes, it merges layers sequentially starting with system-level settings and ending with environment variables. Any field defined in a higher-precedence layer overwrites the same field from lower layers, ensuring environment variables always override file-based settings.
Can I use environment variables to override only specific fields without creating a full configuration file?
Yes. Individual fields support override via DCG_<SECTION>_<KEY> format variables. These variables parse into a temporary ConfigLayer that merges on top of all file-based configurations. This approach allows targeted overrides—such as disabling verbose mode with DCG_GENERAL_VERBOSE=false—without modifying any TOML files.
What happens if a configuration file is missing or has syntax errors?
The ConfigLayer deserialization uses Option<T> for all fields, meaning missing files simply result in empty layers that contribute no values. The merge continues with lower-precedence layers until reaching built-in defaults. However, syntax errors in TOML files would typically cause the deserialization to fail, which the implementation in src/config.rs handles during the load_config process.
Where does dcg look for configuration files by default?
The loader searches four standard locations in order of increasing precedence: /etc/dcg/config.toml (system), $HOME/.config/dcg/config.toml (user), ./.dcg/config.toml (project), and any path specified by the DCG_CONFIG environment variable. If DCG_CONFIG is set, the system loads that specific file instead of performing the standard layered search.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →