dcg Fail-Open Philosophy and When to Enable Fail-Closed Mode
dcg defaults to a fail-open philosophy that allows shell commands to execute when internal errors prevent reliable safety checks, and you should enable fail-closed mode only in environments where blocking legitimate commands is less risky than allowing potentially destructive ones.
The destructive_command_guard repository implements a safety-first hook architecture written in Rust. Understanding the fail-open philosophy and fail-closed mode in dcg is essential for operators who need to balance availability against strict security guarantees.
What Is the dcg Fail-Open Philosophy?
Fail-open is the default safety stance of dcg. Whenever the tool encounters an internal problem that prevents it from reliably evaluating a command—such as malformed JSON, a heredoc timeout, an oversized stdin payload, or a regex compilation error—it does not block the command. Instead, it logs a warning and allows execution to proceed.
This behavior guarantees that dcg never unintentionally stops a legitimate user action because of its own failure. The tool explicitly errs on the side of availability to avoid becoming a denial-of-service vector.
Error Conditions That Trigger Fail-Open
- Malformed hook input: Invalid JSON payloads cannot be parsed, so
src/main.rsallows the command rather than risk a false denial. - Heredoc extraction timeouts: When parsing exceeds the time budget,
src/heredoc.rsreturnsNoneand permits the command to run. - Oversized stdin: Inputs exceeding
max_hook_input_bytestrigger a warning but are not blocked. - Regex compilation failures: Patterns that fail to compile return no match and are treated as safe.
Fail-Open Implementation in the dcg Source Code
The fail-open paths are implemented across several core files in the dcg repository. Each file specializes in a different failure domain, from JSON parsing to heredoc extraction.
Hook Input Parsing in src/main.rs
In src/main.rs, lines 60–68 handle parser failures by emitting a warning and choosing the fail-open path. When the hook input cannot be parsed, the code prints a message such as "... could not parse hook input; allowing command (fail-open) ..." and exits cleanly without denying the command.
Heredoc Timeouts in src/heredoc.rs
The file src/heredoc.rs enforces a performance limit during Tier 2 extraction. If the operation exceeds its deadline, the function returns None, which signals the fail-open condition rather than crashing or blocking. This logic appears around lines 906–960.
Performance Deadlines in src/perf.rs
The 200 ms deadline for hook-mode operations is defined in src/perf.rs, lines 30–40. When this budget is exceeded, dcg falls back to fail-open behavior to preserve shell responsiveness.
Design rationale is further documented in docs/security.md. That file explains the trade-offs behind fail-open timeouts, payload limits, and regex safety.
When to Enable Fail-Closed Mode in dcg
Fail-closed mode inverts the default posture. When enabled, dcg treats parser errors, oversized inputs marked as InputTooLarge, and other ambiguous conditions as denials rather than allowances.
Enable fail-closed only when the cost of a false positive—blocking a legitimate command—is lower than the cost of a false negative—allowing a destructive command to slip through due to an internal failure. This mode is appropriate for high-trust environments where any ambiguity is treated as a security risk.
Typical Use Cases for Fail-Closed
- CI pipelines: Automated jobs must not silently pass malformed hook payloads that could corrupt build artifacts.
- Critical production systems: Environments where an undetected destructive command would cause irreversible data loss.
- Security-review zones: Settings that require zero-tolerance for malformed or attacker-influenceable payloads.
How to Configure Fail-Closed Mode in dcg
The underlying logic that decides whether to block resides in handle_unparseable_hook_input inside src/main.rs, lines 94–104. In that function, the code computes block = blockable && config.is_fail_closed(), which means a denial only occurs when the condition is blockable and the operator has explicitly enabled the stricter mode.
Environment Variable
Set DCG_FAIL_CLOSED=1 before invoking dcg to force denials for any parse error or oversized input. This is the fastest way to toggle strict security without editing configuration files.
export DCG_FAIL_CLOSED=1
echo '{ "tool_name": "Bash", "tool_input": { "command": "git reset --hard" }' | dcg
# → Denial JSON is emitted; command is blocked
TOML Configuration File
Add the flag to ~/.config/dcg/config.toml for a persistent, version-controllable setting. This method is ideal for production deployments where environment variables may change across sessions.
# ~/.config/dcg/config.toml
[general]
fail_closed = true
After editing the config, dcg will deny malformed payloads even without the environment variable. The helper method is_fail_closed() in src/config.rs exposes this setting to the rest of the application.
dcg < malformed_input.json
# → Denied with JSON output
Code Examples: Fail-Open in Practice
The following examples demonstrate dcg’s default fail-open behavior under error conditions. You can run these in a terminal to observe the tool’s safety-first responses.
Default Behavior with Malformed JSON
Under default settings, a malformed payload is allowed to run because the parser cannot prove the command is dangerous. The tool prioritizes availability by returning exit code 0 and omitting any denial JSON.
# Hook mode – malformed JSON is allowed to run
echo '{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /" }' | dcg
# → No denial JSON; command proceeds (fail-open)
Oversized Stdin Warning
Generating a payload larger than the default 1 MiB limit produces a warning without blocking execution. This demonstrates how dcg handles potential DoS vectors by logging the issue while keeping the shell responsive.
# Generate a large payload (exceeds default 1 MiB limit)
head -c 2M /dev/urandom | base64 | dcg
# → Prints warning:
# [dcg] Warning: stdin input (2097152 bytes) exceeds limit (1048576 bytes); allowing command (fail-open)
Summary
- Fail-open is the dcg default. Parser errors, timeouts, oversized input, and regex failures all result in the command being allowed to run with a warning logged.
- The implementation spans
src/main.rs,src/heredoc.rs, andsrc/perf.rs, with the 200 ms deadline andmax_hook_input_byteslimit defining the boundaries. - Fail-closed mode treats the same error conditions as denials and is configured via
DCG_FAIL_CLOSED=1orgeneral.fail_closed = truein the TOML config. - Enable fail-closed only when availability is less important than absolute security, such as in automated CI pipelines or critical production systems.
Frequently Asked Questions
What does fail-open mean in dcg?
Fail-open means that if dcg cannot parse hook input, exceeds a time budget, or encounters any internal evaluation error, it allows the underlying shell command to execute. According to the destructive_command_guard source code in src/main.rs, this prevents the tool from accidentally denying legitimate operations due to its own failures.
When should I use fail-closed mode instead of fail-open?
You should enable fail-closed mode in environments where any ambiguity is a security risk, such as CI pipelines, automated deployment scripts, or high-trust production zones. In these contexts, the risk of allowing a destructive command is greater than the risk of blocking a benign one.
How do I enable fail-closed mode in dcg?
You can enable fail-closed mode by setting the environment variable DCG_FAIL_CLOSED=1 or by adding general.fail_closed = true to your ~/.config/dcg/config.toml file. The blocking logic in src/main.rs checks config.is_fail_closed() to decide whether to deny malformed or oversized payloads.
What happens if a regex pattern fails to compile in dcg?
If a regex compilation fails, dcg treats the result as no match and considers the command safe, which is a fail-open behavior. This ensures that a broken pattern does not accidentally cause a denial-of-service by blocking all commands.
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 →