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

> Understand the Destructive Command Guard allowlist system's layered project, user, and system scopes. Learn how configurations override each other using rule IDs, commands, prefixes, and regex with strict safety.

- Repository: [Jeff Emanuel/destructive_command_guard](https://github.com/Dicklesworthstone/destructive_command_guard)
- Tags: internals
- Published: 2026-07-16

---

**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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/.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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main//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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/allowlist.rs)** – Core implementation containing `LayeredAllowlist`, `AllowEntry`, validity checks, and all match functions.
- **[`src/main.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/main.rs)** – Entry point that instantiates the `LayeredAllowlist` and coordinates command evaluation.
- **[`src/evaluator.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/evaluator.rs)** – Orchestrates calls to allowlist match functions before applying destructive pattern detection.
- **[`Cargo.toml`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/.dcg/allowlist.toml)), **user** (`~/.config/dcg/allowlist.toml`), and **system** ([`/etc/dcg/allowlist.toml`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main//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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/.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.