Performance Budgets in dcg: How the 200ms Timeout Enforces Low Latency
The Destructive Command Guard (dcg) enforces a hard 200ms timeout on its PreToolUse hook to guarantee that command validation never introduces perceptible lag, failing open (allowing the command) if the deadline is exceeded.
The dcg (destructive_command_guard) repository provides a synchronous safety layer that inspects shell commands before execution in AI coding agents. Because this check blocks the command from running, the project defines strict performance budgets that each processing stage must respect to maintain a snappy user experience. These budgets culminate in a 200ms hard deadline that ensures the tool never becomes a bottleneck in the development workflow.
What Are Performance Budgets in dcg?
Performance budgets are maximum latency allocations assigned to specific stages of the command evaluation pipeline. Each budget targets a sub-millisecond or low-millisecond threshold to ensure the total processing time remains imperceptible to users.
The budgets are enforced across these key stages:
- Quick-reject filter (< 50 µs): Implemented in
src/evaluator.rs, this stage usesmemchrandRegexSetto eliminate over 99% of harmless commands before expensive processing begins. - Fast-path normalization (< 500 µs): The
src/normalize.rsmodule handles path stripping, alias expansion, and basic tokenization. - Pattern matching (< 1 ms): Also in
src/evaluator.rs, compiled static regexes (vialazy_static!) classify commands as safe or destructive. - Heredoc extraction (< 2 ms): The
src/heredoc.rsparser processes complex multi-line inputs only when necessary. - Full pipeline with AST matching (< 20 ms): The
src/ast_matcher.rsmodule provides deep analysis for edge cases, validated by the benchmark suite inbenches/heredoc_perf.rsandbenches/hook_latency.rs.
How dcg Enforces the 200ms Timeout
The 200ms deadline represents the total time budget for the entire PreToolUse hook execution. If processing exceeds this limit, dcg implements a fail-open policy to prevent workflow disruption.
The enforcement mechanism follows four steps:
- Start timer: As soon as
dcgreads the JSON hook input insrc/hook.rs::run, it recordsInstant::now(). - Execute pipeline: The command flows through quick-reject, normalization, pattern matching, optional heredoc parsing, and suggestion scoring.
- Check elapsed time: Immediately before writing the final JSON denial in
output/denial.rs, the code verifies:
if start.elapsed().as_millis() > HOOK_TIMEOUT_MS {
// Fail-open: silently allow the command
std::process::exit(0);
}
- Emit result: If under budget, the normal allow/deny decision outputs as JSON; otherwise, the process exits with code 0, allowing the command to run.
The constant HOOK_TIMEOUT_MS is defined in src/perf.rs as const HOOK_TIMEOUT_MS: u64 = 200;, centralizing the deadline configuration.
Why a 200ms Fail-Open Policy?
A defensive security tool that stalls the developer's workflow would quickly be disabled. The 200ms figure was chosen after profiling real-world workloads on Linux and Windows, sitting comfortably below the typical "thinking" latency of code-assistant UIs.
By failing open—allowing the command rather than blocking indefinitely—dcg balances safety (catching most dangerous commands quickly) with usability (never introducing noticeable lag). This guarantee ensures that even pathological inputs, such as deeply nested heredocs, cannot freeze the agent interface.
Validating Performance with Benchmarks
The project maintains rigorous performance validation through dedicated benchmark suites. The benches/hook_latency.rs file measures end-to-end hook latency to verify the < 200ms target, while benches/heredoc_perf.rs focuses on heredoc extraction and AST matching performance.
Developers can observe the internal timer in debug builds by running with RUST_LOG=debug to verify budget compliance:
// In src/hook.rs (debug builds only)
log::debug!("Hook elapsed: {} ms", start.elapsed().as_millis());
You can also test the timeout behavior directly:
# Normal safe command (fast path)
echo '{"tool_name":"Bash","tool_input":{"command":"git status"}}' \
| cargo run --release
# A deliberately complex heredoc that approaches the budget
cat <<'EOF' > /tmp/large_script.sh
$(yes "echo hello" | head -n 50000)
EOF
echo '{"tool_name":"Bash","tool_input":{"command":"bash /tmp/large_script.sh"}}' \
| cargo run --release
# If parsing the heredoc would exceed 200ms, dcg silently allows the command.
Summary
- Performance budgets in
dcgallocate specific latency limits (< 50 µs to < 20 ms) to each processing stage insrc/evaluator.rs,src/normalize.rs, andsrc/heredoc.rs. - The 200ms timeout is enforced in
src/hook.rsby checkingInstant::elapsed()againstHOOK_TIMEOUT_MSdefined insrc/perf.rs. - If the deadline is exceeded,
dcgfails open by exiting with code 0, ensuring user workflow never stalls. - The benchmark suite in
benches/hook_latency.rsvalidates that real-world processing stays well under the 200ms budget.
Frequently Asked Questions
What happens if a command takes longer than 200ms to evaluate?
If processing exceeds the 200ms limit defined in src/perf.rs, dcg immediately exits with code 0 and empty stdout, allowing the command to run. This fail-open policy ensures that complex heredocs or edge-case inputs never block the developer's terminal.
Why does dcg use a fail-open policy instead of blocking slow commands?
Blocking indefinitely would create a poor user experience and incentivize developers to disable the tool entirely. By failing open, dcg guarantees that its safety checks never introduce perceptible lag, maintaining a balance between security and workflow velocity.
Which file defines the 200ms timeout constant?
The timeout value is defined as const HOOK_TIMEOUT_MS: u64 = 200; in src/perf.rs. This constant is imported and checked in src/hook.rs right before the evaluation result is emitted.
How does the quick-reject filter achieve sub-50-microsecond performance?
The filter in src/evaluator.rs uses memchr and pre-compiled RegexSet patterns via lazy_static! to eliminate over 99% of harmless commands without invoking heavier normalization or AST parsing stages.
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 →