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

> Learn about the dcg fail-open philosophy and when to choose fail-closed mode. Understand how Destructive Command Guard protects your systems in various security scenarios, preventing unintended command execution on error.

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

---

**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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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:

```bash

# 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:

```bash
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`:

```toml
[general]
fail_closed = true

```

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

```bash

# 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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/main.rs) (lines 60‑68 and 94‑104), with timeout handling in [`src/heredoc.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/heredoc.rs) and performance budgets defined in [`src/perf.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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.