# dcg Fail-Open Philosophy and When to Enable Fail-Closed Mode

> Understand the dcg fail-open philosophy and learn when to enable fail-closed mode. This guide helps you balance command execution with safety checks.

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

---

**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.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/main.rs) allows the command rather than risk a false denial.
- **Heredoc extraction timeouts:** When parsing exceeds the time budget, [`src/heredoc.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/heredoc.rs) returns `None` and permits the command to run.
- **Oversized stdin:** Inputs exceeding `max_hook_input_bytes` trigger 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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/main.rs)

In [`src/main.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/heredoc.rs)

The file [`src/heredoc.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/perf.rs)

The 200 ms deadline for hook-mode operations is defined in [`src/perf.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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.

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

```toml

# ~/.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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs) exposes this setting to the rest of the application.

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

```bash

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

```bash

# 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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/main.rs), [`src/heredoc.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/heredoc.rs), and [`src/perf.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/perf.rs), with the 200 ms deadline and `max_hook_input_bytes` limit defining the boundaries.
- **Fail-closed mode** treats the same error conditions as denials and is configured via `DCG_FAIL_CLOSED=1` or `general.fail_closed = true` in 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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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.