Agent Threat Rules (ATR)
Broad, explainable regex coverage from the MIT-licensed ATR corpus
Agent Threat Rules (ATR)
ATR screens text against the Agent Threat Rules corpus — 825 community rules in 10 categories, evaluated in-process by a vendored engine (with a ReDoS guard). Every finding names the rule that matched, which makes ATR the most explainable provider: you can always point at the exact rule behind a block or a false positive.
ATR ships with a pinned corpus bundled in the backend — a row with an
empty corpus_path scans it out of the box. The engine reads the corpus once
and reloads when files change (checked at most every 60 s), so you can tune a
mounted corpus without a restart.
1. Use the corpus (bundled by default)
Every row scans the bundled pinned corpus (revision
60dd057bf5946fe44d638f7678d7bd4bb6502f9f, v4.1.2-4) unless you point
corpus_path somewhere else. To supply your own wider or curated corpus,
check out the ATR rules repository at that revision and mount it:
services:
backend:
volumes:
- /opt/atr-rules:/app/atr-corpus:roAny path readable inside the backend container works — a directory of rule
*.yaml files or a single compact JSON corpus. An explicit path replaces the
bundled corpus entirely; if it is unreadable the row records an error event.
2. Understand a rule
Each corpus file is one rule; this is what the engine consumes:
id: ATR-2026-0001
title: Direct instruction override
maturity: stable # stable | test | experimental | draft
severity: high # low | medium | high | critical
category: prompt-injection
detection:
condition: any
conditions:
- field: user_input # user_input | content | tool_response | tool_name | agent_output
operator: regex
value: "(?i)ignore (all )?previous instructions"
response:
actions: [block_input, alert]Maturity matters: only stable rules can block. Findings from wider tiers
are always recorded as observations — so you can preview the broader corpus on
real traffic without any risk.
3. Create the guardrail
| Field | Meaning | Example |
|---|---|---|
corpus_path | Corpus path inside the backend container. Empty = the bundled pinned corpus. | /app/atr-corpus |
min_maturity | Tiers to run: stable; stable+test; or everything. Only stable rules can block. | stable |
min_severity | Skip rules below this severity. | low |
categories | Include only these categories (comma-separated). Empty = all 10. | prompt-injection, tool-abuse |
exclude_categories | Skip these categories (applied after categories). | jailbreak |
scan_targets | Surfaces this row serves; rules carrying a scan_target filter run only where the sets intersect. | mcp_tool_result |
condition_timeout_ms | Per-regex budget (ReDoS guard). | 50 |
max_text_chars | Longer texts are scanned as head+tail windows of this size. | 20000 |
{
"corpus_path": "/app/atr-corpus",
"min_maturity": "stable",
"categories": ["prompt-injection", "tool-abuse"]
}Recommended rollout
- Start with
min_maturity: "test"and Enforce = Off — everything the wider corpus flags lands in the events log: a free preview on your traffic. - Review findings (the excerpt names the rule id), exclude noisy categories,
then tighten to
min_maturity: "stable". - Flip Enforce = On. Because test-tier findings can never block, mistakenly
leaving
testselected only means more observations — never more blocks.