How the Allowlist System Works with Layered Project, User, and System Scopes in Destructive Command Guard

The Destructive Command Guard (dcg) allowlist system uses a three-layer hierarchy where project-level configuration overrides user settings, which override system-wide defaults, with each layer supporting rule IDs, exact commands, prefixes, and regex patterns under strict safety validations.

The allowlist system in Destructive Command Guard (dcg)—an open-source safety tool from Dicklesworthstone/destructive_command_guard—implements a precedence-ordered mechanism for whitelisting commands across different administrative scopes. Written in Rust and located primarily in src/allowlist.rs, this system balances flexibility with security by allowing local project configurations to override broader user and system policies.

The Three-Layer Hierarchy

The allowlist system recognizes three distinct configuration scopes with explicit precedence rules. When the engine evaluates whether to permit a command, it consults these layers in order of decreasing priority.

Project Scope

The project layer holds the highest precedence. It reads from .dcg/allowlist.toml located at the repository root. This allows development teams to commit project-specific exceptions that travel with the codebase, ensuring that legitimate but potentially destructive operations—such as specific git reset patterns—are permitted only within that project's context.

User Scope

The user layer resides at ~/.config/dcg/allowlist.toml and applies to all commands executed by the current user across all projects. This middle layer allows individual developers to set personal preferences without modifying project repositories or requiring system administrator privileges.

System Scope

The system layer optionally loads from /etc/dcg/allowlist.toml and carries the lowest precedence. Administrators can deploy organization-wide defaults here, knowing that project and user configurations will override these settings when more specific policies exist.

Loading and Merging Configuration

All layer interaction is managed through the LayeredAllowlist struct in src/allowlist.rs. The system does not require all three files to exist; missing files simply create empty layers that contribute no entries.

LayeredAllowlist::load_from_paths

The constructor LayeredAllowlist::load_from_paths initializes the hierarchy by creating a LoadedAllowlistLayer for each existing file. According to the implementation, the insertion order is project → user → system, establishing the precedence used during lookups. This ordering ensures that when multiple layers contain entries for the same command, the project-level entry wins.

Agent-Profile Overrides

Before consulting file-based layers, prepend_agent_exact_commands can inject a temporary Agent layer with the absolute highest precedence (AllowlistLayer::Agent). This layer contains only exact-command entries derived from an agent's profile and exists only for the current session. These entries bypass file I/O but cannot use regex patterns or bypass risk acknowledgments.

Allowlist Entry Types

Each entry in the allowlist is represented by an AllowEntry structure that specifies what to match and under what conditions. The AllowSelector enum determines the matching strategy.

Rule ID Matching

Entries targeting specific rule IDs use AllowSelector::Rule and match the format pack_id:pattern_name. This allows fine-grained control over individual destructive patterns defined in dcg's rule packs.

Exact Command Matching

AllowSelector::ExactCommand performs literal string matching against the full command. This is the safest match type and is the only option available for agent-profile overrides.

Command Prefix Matching

AllowSelector::CommandPrefix validates that a command starts with a specific prefix, but includes safety checks in command_prefix_safely_matches to ensure the prefix ends at a token boundary and that the remaining command tail contains no shell-chain metacharacters that could enable injection attacks.

Regex Pattern Matching

AllowSelector::RegexPattern supports arbitrary regular expressions for complex matching scenarios. However, these entries require explicit risk_acknowledged = true metadata to prevent accidental broad regexes from creating security holes.

Validity Checks and Safety Guards

Before any entry can match a command, it must pass several validation checks implemented in is_entry_valid_at_path_with_session. These guards apply across all layers equally.

Expiration and TTL

Entries may specify expires_at (a specific timestamp) or ttl (time-to-live duration). The system checks these against the current time and rejects expired entries regardless of layer precedence.

Session Binding

When session = true, the entry binds to a specific DCG_SESSION_ID environment variable or a Linux system fingerprint. The entry only matches if the current session identifier equals the stored session_id, preventing allowlisted commands from persisting across terminal sessions unintendedly.

Environment Conditions

Entries can specify conditions as key-value pairs that must match the current environment variables. All specified conditions must evaluate to true for the entry to remain valid.

Risk Acknowledgment

Regex pattern entries require risk_acknowledged = true in their metadata. Without this flag, match_pattern_at_path rejects the match even if the regex technically matches the command string.

Path Restrictions

If paths is specified, the current working directory must match at least one glob pattern using glob::Pattern. When cwd is None, path filtering is skipped for backward compatibility, but when provided, the path must satisfy the restriction.

Matching Logic and Precedence

The matching API provides separate methods for each entry type, all following the same layer-traversal strategy.

How Matches Are Resolved

  • match_rule_at_path walks layers from project to system, returning the first entry whose AllowSelector::Rule matches the requested pack_id and pattern_name.
  • match_exact_command_at_path does the same for AllowSelector::ExactCommand.
  • match_command_prefix_at_path validates token boundaries and safe tails before accepting a prefix match.
  • match_pattern_at_path compiles regexes into a global pattern_cache and applies them only if risk_acknowledged is true.

Wildcard Support

Rule ID lookups support wildcard pattern names (*), allowing entries like core.git:* to match all patterns within a specific pack. However, wildcard pack IDs are explicitly rejected for safety to prevent overly broad exceptions.

Implementation in Source Code

The layered allowlist system spans several key files:

  • src/allowlist.rs – Core implementation containing LayeredAllowlist, AllowEntry, validity checks, and all match functions.
  • src/main.rs – Entry point that instantiates the LayeredAllowlist and coordinates command evaluation.
  • src/evaluator.rs – Orchestrates calls to allowlist match functions before applying destructive pattern detection.
  • Cargo.toml – Declares dependencies including glob for path pattern matching and fancy-regex for regex handling.

Summary

  • The allowlist system in dcg uses three precedence-ordered layers: project (.dcg/allowlist.toml), user (~/.config/dcg/allowlist.toml), and system (/etc/dcg/allowlist.toml), with project configurations always winning.
  • An optional Agent layer injected via prepend_agent_exact_commands takes temporary precedence above all file-based layers.
  • Entries support four match types—rule IDs, exact commands, command prefixes, and regex patterns—each with specific safety constraints.
  • All entries must pass temporal (expiration/TTL), contextual (session, environment), and spatial (path glob) validity checks before matching.
  • Regex patterns require explicit risk_acknowledged = true and are cached globally for performance.
  • The implementation in src/allowlist.rs uses LayeredAllowlist::load_from_paths to initialize the hierarchy and provides specific match_* functions that traverse layers from highest to lowest precedence.

Frequently Asked Questions

What file takes precedence if the same command is allowlisted in multiple scopes?

The project scope always wins. According to the loading logic in LayeredAllowlist::load_from_paths, layers are inserted in the order project → user → system, and match functions traverse this list from top to bottom, returning the first valid entry they find. If .dcg/allowlist.toml contains an entry for git reset --hard, it overrides any git reset --hard entries in the user or system configurations.

How does the agent layer interact with the three standard scopes?

The Agent layer sits above all file-based scopes when active. Calling prepend_agent_exact_commands injects a temporary AllowlistLayer::Agent containing only exact-command entries at the head of the layer stack. These entries are checked first during evaluation, but they cannot use regex patterns and are not persisted to disk—they exist only for the current session.

What safety measures prevent regex patterns from bypassing security?

Two controls apply to AllowSelector::RegexPattern entries. First, the entry must include risk_acknowledged = true in its metadata; otherwise match_pattern_at_path rejects the match. Second, even when acknowledged, the regex must pass all other validity checks including expiration, session binding, environment conditions, and path restrictions. Wildcard pack IDs are also rejected for rule-based entries.

Can allowlist entries be restricted to specific directories?

Yes, through the paths field which accepts glob patterns. When match_rule_at_path or other match functions receive a cwd parameter, they validate the current working directory against glob::Pattern instances stored in the entry. If the directory doesn't match any specified pattern, the entry is skipped even if the command otherwise matches. When cwd is None, path filtering is bypassed for backward compatibility.

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 →