Understanding dcg's Fail-Open Philosophy and When to Use Fail-Closed Mode

By default, dcg (Destructive Command Guard) operates in fail-open mode, allowing commands to run when internal errors prevent safety evaluation, while fail-closed mode blocks commands under the same conditions for high-security environments.

dcg's fail-open philosophy prioritizes availability over absolute security by default. When the tool encounters parsing errors, timeouts, or oversized inputs, it logs warnings but permits the command to execute rather than risking a denial-of-service. This article explains the implementation in the Dicklesworthstone/destructive_command_guard repository and when operators should switch to fail-closed mode.

What Is dcg's Fail-Open Philosophy?

Fail-open is the default safety stance embedded throughout the dcg codebase. When dcg cannot reliably determine whether a command is destructive due to internal limitations, it treats the command as safe and allows execution. This prevents the tool itself from becoming a single point of failure that accidentally blocks legitimate operations.

The philosophy applies to several specific failure scenarios:

  • Malformed JSON input: When the hook input parser cannot validate the command structure, dcg returns exit code 0 and allows the command rather than emitting a denial JSON
  • Hedoc extraction timeouts: If parsing heredoc syntax exceeds the 200 ms time budget, dcg returns None and permits the command to proceed
  • Oversized payloads: Inputs exceeding max_hook_input_bytes (default 1 MiB) trigger a warning but do not block execution
  • Regex compilation failures: Complex or invalid patterns fail safely by returning "no match" rather than crashing or denying the command

In src/main.rs at lines 60‑68, the parser's error path explicitly implements this behavior with the message: "could not parse hook input; allowing command (fail‑open)".

Implementation: Where Fail-Open Lives in the Code

Parse Error Handling in src/main.rs

The primary decision logic for fail-open versus fail-closed resides in the main entry point. When handle_unparseable_hook_input detects an unparseable payload, it checks the configuration before deciding whether to block.

At lines 94‑104 in src/main.rs, the code calculates block = blockable && config.is_fail_closed(). This means blocking only occurs when the condition is blockable and fail-closed mode is explicitly enabled. Otherwise, the command proceeds with a logged warning.

Heredoc Timeouts and Performance Budgets

The heredoc extraction system enforces strict performance boundaries to prevent parsing from hanging indefinitely. In src/heredoc.rs at lines 906‑960, the Tier 2 extraction logic implements a deadline-based timeout. If parsing exceeds the limit defined in src/perf.rs (lines 30‑40 specify a 200 ms budget), the function returns None rather than an error, effectively choosing the fail-open path.

This ensures that dcg never degrades shell responsiveness due to pathological input patterns.

Input Size and Regex Safety Valves

Size limits and pattern matching incorporate similar safety valves. When stdin exceeds max_hook_input_bytes (1,048,576 bytes by default), dgc prints [dcg] Warning: stdin input exceeds limit but returns exit code 0. Similarly, regex compilation errors in pattern matching return "no match" status, treating the command as safe rather than risking a crash.

When to Use Fail-Closed Mode

Fail-closed mode inverts the default philosophy: any internal error that prevents reliable evaluation results in a denial rather than permission. Enable this mode by setting the environment variable DCG_FAIL_CLOSED=1 or configuring general.fail_closed = true in your dcg.toml file.

Critical Production Systems

Use fail-closed when running dcg in critical production environments where data loss costs exceed inconvenience costs. If undetected destructive commands could corrupt databases or destroy infrastructure, the fail-closed posture ensures that any ambiguity in parsing results in immediate blocking.

CI/CD and Automated Pipelines

Automated build systems benefit from fail-closed mode because silent failures in the guard tool could propagate corrupted artifacts. When DCG_FAIL_CLOSED=1 is set, malformed JSON inputs produce explicit denial JSON payloads and exit code 1, causing CI jobs to fail safely rather than proceeding with potentially dangerous commands.

High-Trust Security Zones

In security-review environments requiring zero-tolerance for malformed hook payloads, fail-closed mode treats parser failures as potential evasion attempts. The tool denies commands experiencing InputTooLarge errors or JSON parse failures, forcing operators to investigate anomalies rather than allowing potentially manipulated inputs through.

Configuration Examples

Default Fail-Open Behavior

Without configuration, malformed inputs proceed with warnings:


# Malformed JSON missing closing brace

echo '{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /" }' | dcg

# Command proceeds; warning logged internally

Enabling Fail-Closed via Environment Variable

Set the variable to block ambiguous inputs immediately:

export DCG_FAIL_CLOSED=1
echo '{ "invalid": "json' | dcg

# Returns denial JSON and blocks command

Enabling Fail-Closed via Configuration File

Create or edit ~/.config/dcg/config.toml:

[general]
fail_closed = true

With this setting, oversized inputs that would normally trigger a warning now block execution:


# Generate 2MB of random data exceeding default limit

head -c 2M /dev/urandom | base64 | dcg

# Denied: InputTooLarge error returned

Summary

  • Fail-open is dcg's default: parser errors, timeouts, and oversized inputs log warnings but allow command execution to prevent accidental denial-of-service
  • Fail-closed mode (DCG_FAIL_CLOSED=1 or general.fail_closed = true) blocks commands when dcg cannot parse or process inputs, prioritizing security over availability
  • The decision logic resides in src/main.rs (lines 60‑68 and 94‑104), with timeout handling in src/heredoc.rs and performance budgets defined in src/perf.rs
  • Use fail-open for general shell interaction where availability matters; use fail-closed for CI pipelines, production systems, and high-security environments where any ambiguity must be treated as malicious

Frequently Asked Questions

What happens if dcg crashes during command evaluation?

If dcg encounters an internal panic or unhandled exception, the shell hook architecture typically falls back to allowing the command, preserving the fail-open philosophy at the infrastructure level. However, within the application logic, panic handling converts to graceful exits with appropriate logging.

Does fail-open mode reduce security significantly?

Fail-open maintains security for evaluated commands while accepting risk on unevaluated commands. Malformed inputs that bypass parsing could theoretically hide destructive commands, which is why production systems with strict security requirements should enable fail-closed mode.

Can I configure different size limits for fail-open versus fail-closed?

The max_hook_input_bytes setting (in src/config.rs) applies universally, but its enforcement differs by mode. In fail-open, exceeding the limit produces a warning and allows the command; in fail-closed, the same limit triggers an InputTooLarge denial. There is no separate size limit configuration per mode.

Why does dcg use a 200 ms timeout for heredoc parsing?

The 200 ms deadline defined in src/perf.rs prevents pathological regular expressions or deeply nested heredoc structures from freezing the shell. This limit balances thoroughness with responsiveness; if extraction cannot complete quickly, dcg assumes the input is complex but not necessarily dangerous, choosing availability over perfect analysis.

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 →