How Destructive Command Guard's Layered Configuration System Prioritizes Environment Variables vs Config Files

Environment variables prefixed with DCG_ override all configuration files, while settings cascade through project-level, user-level, system-level, and finally compiled defaults.

Destructive Command Guard (dcg) implements a deterministic layered configuration system that balances flexibility with predictability. Written in Rust, the tool loads settings from multiple sources according to strict precedence rules documented in the header of src/config.rs【L3-L9】, ensuring that environment variables always take precedence over file-based configurations.

The Five-Layer Configuration Hierarchy

The dcg configuration system builds settings from five distinct layers, applying a "higher-precedence wins" merge strategy:

Environment Variables (Highest Priority)

Any setting supplied via a DCG_… variable shadows values from all configuration files. The constant ENV_PREFIX is defined at line 44 in src/config.rs【L43-L45】, enabling per-run overrides without modifying persistent files.

Project-Level Configuration

The .dcg.toml file in the repository root provides project-specific settings. When running dcg inside a project directory, values here override user-level and system-level configurations but yield to environment variables.

User-Level Configuration

Settings specific to the current user are read from $XDG_CONFIG_HOME/dcg/config.toml (falling back to ~/.config/dcg/config.toml on most systems). This layer applies only when project-level files are absent or when specific fields remain unset.

System-Level Configuration

Global system defaults reside in /etc/dcg/config.toml on Linux/macOS or %ProgramData%\dcg\config.toml on Windows. These provide organization-wide defaults while allowing individual users and projects to override them.

Compiled Defaults (Lowest Priority)

When no configuration files exist and no environment variables are set, dcg uses the Rust Default implementations defined for each config struct (e.g., GeneralConfig::default() at line 46【L46-L48】).

The Merge Algorithm and ConfigLayer Structure

The merging logic centers on the ConfigLayer struct defined at line 84 in src/config.rs【L84-L90】. Each configuration field uses Option<T> to represent presence or absence:

  • Some(T) indicates an explicit value from that layer
  • None indicates the layer did not provide that setting

During initialization, dcg parses each file into a layer, then applies the merge algorithm: if a field is Some in a higher layer (e.g., environment variables), it shadows any Some value from lower layers. Only fields that remain None after evaluating all layers receive the compiled defaults.

Practical Override Examples

Overriding a Boolean via Environment Variable

The compiled default for verbose output is false. Setting the environment variable enables verbose logging for a single run regardless of file configurations:

DCG_VERBOSE=true dcg explain "git reset --hard"

Result: verbose is true regardless of values set in .dcg.toml, ~/.config/dcg/config.toml, or /etc/dcg/config.toml.

Selecting a Custom Config File

The special variable DCG_CONFIG selects an alternate TOML file, separate from per-setting overrides【L52-L56】:

DCG_CONFIG=/tmp/my-config.toml dcg --version

Result: dcg loads the specified file instead of the normal search order, then still applies any DCG_… overrides on top of that file.

Project-Level Precedence Over User Files

Assume the following scenario:

  • ~/.config/dcg/config.toml contains check_updates = false
  • The project's .dcg.toml contains check_updates = true

Running dcg inside the repository:

cd my-repo
dcg explain "rm -rf /"

Result: check_updates evaluates to true because the project file outranks the user file.

Core Implementation Files

  • src/config.rs – Contains the ConfigLayer struct, ENV_PREFIX constant, and the layered merging logic that implements the precedence rules.
  • src/main.rs – Parses command-line arguments and orchestrates the configuration loading sequence before applying environment variable overrides.
  • src/trace.rs – Emits diagnostic messages indicating when a setting was overridden by the environment (e.g., "BLOCK: config override").
  • src/cli.rs – Implements the dcg config show command, which respects the precedence rules to display active settings.

Summary

  • Environment variables with the DCG_ prefix take precedence over all configuration files.
  • Configuration files apply in descending order: project (.dcg.toml) → user (~/.config/dcg/config.toml) → system (/etc/dcg/config.toml).
  • Compiled defaults from Rust Default implementations serve as the final fallback.
  • The ConfigLayer struct uses Option<T> fields to implement the "higher-precedence wins" merge algorithm.
  • The DCG_CONFIG environment variable can specify a custom configuration file path, which is then subject to additional DCG_ overrides.

Frequently Asked Questions

What happens if I set both DCG_CONFIG and specific DCG_ variable overrides?

Dcg first loads the file specified by DCG_CONFIG, then applies any additional DCG_ environment variable overrides on top of that file. Environment variables always represent the final authority regardless of which file was loaded initially.

How does dcg handle configuration on Windows versus Linux?

On Linux and macOS, dcg searches /etc/dcg/config.toml for system-level settings and $XDG_CONFIG_HOME/dcg/config.toml (or ~/.config/dcg/config.toml) for user-level settings. On Windows, the system-level path becomes %ProgramData%\dcg\config.toml, while user-level configuration follows the same XDG pattern if available, or falls back to the standard config directory.

Can I see which configuration layer provided a specific setting?

Yes. When running dcg with verbose or trace output enabled, the system emits messages via src/trace.rs indicating when a configuration value was overridden by an environment variable. This helps debug which layer is active for specific settings like verbose or check_updates.

What occurs if no configuration files exist at any level?

If dcg finds no .dcg.toml, user config, or system config, it initializes the configuration using only the compiled defaults defined in the Default trait implementations for each struct (e.g., color = "auto" and check_updates = true). You can then selectively override these defaults using DCG_ environment variables as needed.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →