@agenttrail/guardrails 0.2.2 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
  </picture>
10
10
  </a>
11
11
 
12
- # AgentTrail Guardrails: open-source rules for AI coding agents
12
+ # AgentTrail Guardrails: an open-source guardrail library for AI coding agents
13
13
 
14
- **A library of rules for catching dangerous commands before an AI coding agent runs them.**<br />
14
+ **A library of guardrails for catching dangerous commands before an AI coding agent runs them.**<br />
15
15
  Plain data you can read, test, and change. Enforced in **Claude Code**, **Cursor**, and **Codex CLI**.
16
16
 
17
17
  [![CI](https://github.com/agenttrailhq/guardrails/actions/workflows/ci.yml/badge.svg)](https://github.com/agenttrailhq/guardrails/actions/workflows/ci.yml)
@@ -22,13 +22,13 @@ Plain data you can read, test, and change. Enforced in **Claude Code**, **Cursor
22
22
  [Quick start](#quick-start) ·
23
23
  [The packs](#the-eleven-packs) ·
24
24
  [OWASP coverage](#owasp-coverage) ·
25
- [Rule format](#a-rule-start-to-finish) ·
26
- [Contribute a rule](#contributing-a-rule) ·
25
+ [Guardrail format](#a-guardrail-start-to-finish) ·
26
+ [Contribute a guardrail](#contributing-a-guardrail) ·
27
27
  [Changelog](CHANGELOG.md)
28
28
 
29
29
  </div>
30
30
 
31
- [74 rules](https://www.agenttrail.sh/guardrails), grouped into 11 packs. Apache-2.0. Published
31
+ [74 guardrails](https://www.agenttrail.sh/guardrails), grouped into 11 packs. Apache-2.0. Published
32
32
  as `@agenttrail/guardrails` and enforced by [AgentTrail Guard](https://github.com/agenttrailhq/guard).
33
33
 
34
34
  ![A Claude Code session: asked to split a name column and push the schema, the agent edits the Prisma schema and seed, then runs npx prisma db push --force-reset to wipe the database. AgentTrail Guard blocks it with the guardrail dd.accept-data-loss, and the agent stops](.github/assets/guard-blocks-force-reset.gif)
@@ -48,28 +48,28 @@ for, how serious it is, and what should happen: allow it, ask a human first, or
48
48
  **That is all this package does.** It contains no code that watches your machine and nothing that
49
49
  talks to the network. Something else has to read the list and act on it: normally
50
50
  [`agenttrail-guard`](https://github.com/agenttrailhq/guard), which runs on your laptop and checks each
51
- command an agent proposes against these rules.
51
+ command an agent proposes against these guardrails.
52
52
 
53
- ## Why these rules?
53
+ ## Why these guardrails?
54
54
 
55
- - **Readable.** Every rule is plain data with a title, a severity, a default action, and a
55
+ - **Readable.** Every guardrail is plain data with a title, a severity, a default action, and a
56
56
  description that says what it catches **and what it misses**.
57
- - **Proven in both directions.** Every rule ships commands it must catch and near-misses it must
58
- leave alone, and a shared corpus of everyday commands no rule may match. A rule that fails
57
+ - **Proven in both directions.** Every guardrail ships commands it must catch and near-misses it must
58
+ leave alone, and a shared corpus of everyday commands no guardrail may match. A guardrail that fails
59
59
  either side does not build.
60
60
  - **Quiet on purpose.** A commit message or a `grep` that only *mentions* `rm -rf /` is not
61
61
  treated as running it. See [Talking about a command is not running it](#talking-about-a-command-is-not-running-it).
62
62
  - **Mapped to known risks.** The packs line up with the
63
63
  [OWASP Top 10s for agentic and LLM applications](#owasp-coverage), and the gaps are stated, not
64
64
  hidden.
65
- - **Separate from the enforcer.** You can read, disagree with, and change any rule without
65
+ - **Separate from the enforcer.** You can read, disagree with, and change any guardrail without
66
66
  trusting anything about the tool that enforces it.
67
67
 
68
68
  ## Quick start
69
69
 
70
70
  **You need:** Node.js 22.13 or newer.
71
71
 
72
- ### Enforce the rules in your agent
72
+ ### Enforce the guardrails in your agent
73
73
 
74
74
  Most people never install this package directly. [AgentTrail Guard](https://github.com/agenttrailhq/guard)
75
75
  bundles it, so installing Guard is all it takes:
@@ -77,18 +77,18 @@ bundles it, so installing Guard is all it takes:
77
77
  ```bash
78
78
  npm install -g @agenttrail/guard
79
79
  agenttrail-guard init --agent claude # or: --agent cursor, --agent codex
80
- agenttrail-guard guardrails list # every rule and its current action
81
- agenttrail-guard guardrails show wt.reset-hard # one rule in full
82
- agenttrail-guard guardrails set-action <id> warn # change what a rule does
83
- agenttrail-guard guardrails allow <id> <pattern> # silence one rule for one command shape
80
+ agenttrail-guard guardrails list # every guardrail and its current action
81
+ agenttrail-guard guardrails show wt.reset-hard # one guardrail in full
82
+ agenttrail-guard guardrails set-action <id> warn # change what a guardrail does
83
+ agenttrail-guard guardrails allow <id> <pattern> # silence one guardrail for one command shape
84
84
  ```
85
85
 
86
86
  See the [AgentTrail Guard README](https://github.com/agenttrailhq/guard#quick-start) for setup
87
87
  and everything else Guard can do.
88
88
 
89
- ### Use the rules in your own code
89
+ ### Use the guardrails in your own code
90
90
 
91
- Install the package if you are writing rules, or building your own tool on top of the list:
91
+ Install the package if you are writing guardrails, or building your own tool on top of the list:
92
92
 
93
93
  ```bash
94
94
  npm install @agenttrail/guardrails
@@ -99,24 +99,24 @@ import { RULES, getRule, rulesForPack } from "@agenttrail/guardrails/guardrails"
99
99
 
100
100
  // Everything that refuses outright, rather than asking or warning.
101
101
  const blocking = RULES.filter((rule) => rule.defaultAction === "block");
102
- console.log(`${RULES.length} rules, ${blocking.length} block by default`);
102
+ console.log(`${RULES.length} guardrails, ${blocking.length} block by default`);
103
103
 
104
- // One pack, with each rule's severity and default action.
104
+ // One pack, with each guardrail's severity and default action.
105
105
  for (const rule of rulesForPack("secret-exposure")) {
106
106
  console.log(rule.id, rule.severity, rule.defaultAction);
107
107
  }
108
108
 
109
- // One rule, including the limits its description admits to.
109
+ // One guardrail, including the limits its description admits to.
110
110
  console.log(getRule("wt.reset-hard")?.description);
111
111
  ```
112
112
 
113
- This package has no evaluator: it tells you what each rule looks for, not whether a given
113
+ This package has no evaluator: it tells you what each guardrail looks for, not whether a given
114
114
  command matches. Guard does the matching. [Two ways to import it](#two-ways-to-import-it)
115
115
  explains the two entry points.
116
116
 
117
- ## A rule, start to finish
117
+ ## A guardrail, start to finish
118
118
 
119
- Here is a complete rule. Nothing is hidden; this is the actual shape.
119
+ Here is a complete guardrail. Nothing is hidden; this is the actual shape.
120
120
 
121
121
  ```jsonc
122
122
  {
@@ -126,6 +126,7 @@ Here is a complete rule. Nothing is hidden; this is the actual shape.
126
126
  "defaultAction": "block",
127
127
  "title": "git reset --hard discards uncommitted work",
128
128
  "description": "Discards all uncommitted changes. Does not match `git restore` (see wt.restore-path).",
129
+ "owasp": ["ASI02", "LLM03"],
129
130
 
130
131
  // What to look for. This one matches a Bash command against a regular expression.
131
132
  "match": {
@@ -134,7 +135,7 @@ Here is a complete rule. Nothing is hidden; this is the actual shape.
134
135
  ]
135
136
  },
136
137
 
137
- // Proof it works, in both directions (see "Every rule proves both directions" below).
138
+ // Proof it works, in both directions (see "Every guardrail proves both directions" below).
138
139
  "fixtures": {
139
140
  "block": ["git reset --hard"],
140
141
  "allow": ["git reset src/api.ts"]
@@ -142,32 +143,33 @@ Here is a complete rule. Nothing is hidden; this is the actual shape.
142
143
  }
143
144
  ```
144
145
 
145
- The real rule is in [`wt-reset-hard.ts`](src/packs/working-tree/wt-reset-hard.ts); this is a
146
+ The real guardrail is in [`wt-reset-hard.ts`](src/packs/working-tree/wt-reset-hard.ts); this is a
146
147
  trimmed copy. With it enforced by Guard, an agent that proposes `git reset --hard` is refused
147
- and told which rule stopped it, while `git reset src/api.ts`, which unstages one file, goes
148
+ and told which guardrail stopped it, while `git reset src/api.ts`, which unstages one file, goes
148
149
  ahead as normal.
149
150
 
150
151
  Reading the fields:
151
152
 
152
153
  | Field | What it means |
153
154
  |---|---|
154
- | `id` | A stable name. Users type it to disable or change a rule, so it never changes. |
155
+ | `id` | A stable name. Users type it to disable or change a guardrail, so it never changes. |
155
156
  | `severity` | How bad the thing being caught is: `critical`, `high`, `medium`, `low`, `info`. **It is not a price**: this package ships no mapping from severity to money. |
156
157
  | `defaultAction` | What should happen: `block` (refuse), `require_approval` (ask a human), or `warn` (allow, but say so). A user can override it. |
157
- | `description` | What the rule catches **and what it misses**. The honest limits are part of the rule, not a footnote. |
158
+ | `description` | What the guardrail catches **and what it misses**. The honest limits are part of the guardrail, not a footnote. |
159
+ | `owasp` | Optional. The [OWASP risks](#owasp-coverage) the guardrail addresses, as ids such as `ASI02` or `LLM06`, in list order. Every guardrail in this package has at least one. Versions up to and including 0.2.2 reject the field, so validate a guardrail that carries it with 0.2.3 or later. |
158
160
  | `match` | The condition. `any_of` means "any one of these is enough". |
159
161
  | `fixtures` | Examples that must match, and examples that must not. |
160
162
 
161
163
  ## The eleven packs
162
164
 
163
- A rule is filed by **the harm it prevents**, never by the technique it uses to spot it.
165
+ A guardrail is filed by **the harm it prevents**, never by the technique it uses to spot it.
164
166
 
165
- That sounds like a detail and is not. The three rules about production config, `.env` files and API
167
+ That sounds like a detail and is not. The three guardrails about production config, `.env` files and API
166
168
  endpoints all work by matching file paths, but they are *not* in `file-scope`. Someone who turned
167
169
  that pack off to stop path noise would otherwise silently lose their production and secret
168
170
  protection, which they never asked to turn off and would not know they had.
169
171
 
170
- | Pack | Rules | What it is about |
172
+ | Pack | Guardrails | What it is about |
171
173
  |---|---:|---|
172
174
  | [`working-tree`](src/packs/working-tree/) | 9 | Destroying uncommitted work or published history: `git reset --hard`, `git clean -fd`, force-push, `rm -rf`. |
173
175
  | [`destructive-data`](src/packs/destructive-data/) | 8 | Data git cannot bring back: a dropped volume, a dropped database, destructive DDL, a deleted shadow copy. |
@@ -183,10 +185,10 @@ protection, which they never asked to turn off and would not know they had.
183
185
 
184
186
  Pack names appear in user config files, so renaming one is a breaking change, not a tidy-up.
185
187
 
186
- Each pack name links to its source folder: one TypeScript file per rule, so you can read any rule
187
- end to end. Every rule, with its full description and tested examples, is also in the
188
+ Each pack name links to its source folder: one TypeScript file per guardrail, so you can read any guardrail
189
+ end to end. Every guardrail, with its full description and tested examples, is also in the
188
190
  [guardrail library](https://www.agenttrail.sh/guardrails) on the AgentTrail website, where you
189
- can paste a command to see which rules match it.
191
+ can paste a command to see which guardrails match it.
190
192
 
191
193
  ## OWASP coverage
192
194
 
@@ -194,28 +196,39 @@ Several packs exist because of risks named in OWASP's two lists for AI systems:
194
196
  [Top 10 for Agentic Applications](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/)
195
197
  (ASI01 to ASI10, 2026 edition) and the
196
198
  [Top 10 for LLM Applications](https://genai.owasp.org/resource/owasp-genai-llm-top-10-2026/)
197
- (LLM01 to LLM10, 2026 edition). A rule cannot see a prompt or a model's reasoning, only the
198
- action that comes out of it, so these rules address a risk at the point where it turns into a
199
+ (LLM01 to LLM10, 2026 edition). A guardrail cannot see a prompt or a model's reasoning, only the
200
+ action that comes out of it, so these guardrails address a risk at the point where it turns into a
199
201
  command or a file change.
200
202
 
201
- | Pack | Agentic risks | LLM risks |
202
- |---|---|---|
203
- | `working-tree`, `destructive-data`, `prod-infra`, `file-scope` | ASI02 Tool Misuse and Exploitation | LLM03 Excessive Agency |
204
- | `rce-supply-chain` | ASI05 Unexpected Code Execution, ASI04 Agentic Supply Chain | LLM04 Supply Chain |
205
- | `privilege-supply-chain` | ASI03 Identity and Privilege Abuse, ASI04 Agentic Supply Chain | LLM04 Supply Chain |
206
- | `secret-exposure`, `exfiltration` | ASI02 Tool Misuse and Exploitation, ASI03 Identity and Privilege Abuse | LLM02 Sensitive Information Disclosure |
207
- | `safety-bypass` | ASI09 Human-Agent Trust Exploitation | LLM10 Improper Output Handling (`gb.ansi-terminal-forgery`) |
208
- | `agent-context` | ASI06 Memory and Context Poisoning (the write, not its contents), ASI10 Rogue Agents | LLM03 Excessive Agency |
209
- | `test-integrity` | ASI10 Rogue Agents, ASI09 Human-Agent Trust Exploitation | |
210
-
211
- What the rules cannot address, by design:
212
-
213
- - **Prompt injection and goal hijack (LLM01, ASI01).** The rules never see the input that
203
+ | Pack | Agentic risks | LLM risks | Default action |
204
+ |---|---|---|---|
205
+ | `working-tree`, `destructive-data`, `prod-infra`, `file-scope` | ASI02 Tool Misuse and Exploitation | LLM03 Excessive Agency, LLM10 Improper Output Handling (`block-destructive-sql`) | `block` or `require_approval` |
206
+ | `rce-supply-chain` | ASI04 Agentic Supply Chain, ASI05 Unexpected Code Execution | LLM04 Supply Chain, LLM07 Misinformation (`rce.unverified-package`) | `block` or `require_approval` |
207
+ | `privilege-supply-chain` | ASI03 Identity and Privilege Abuse, ASI04 Agentic Supply Chain | LLM03 Excessive Agency (`ps.iam-grant`), LLM04 Supply Chain, LLM07 Misinformation (`flag-dependency-install`) | `require_approval`, except `flag-dependency-install`, which is `warn` |
208
+ | `secret-exposure`, `exfiltration` | ASI02 Tool Misuse and Exploitation, ASI03 Identity and Privilege Abuse | LLM02 Sensitive Information Disclosure | `secret-exposure` is mostly `warn`; `exfiltration` is `block` or `require_approval` |
209
+ | `safety-bypass` | ASI09 Human-Agent Trust Exploitation | LLM10 Improper Output Handling (`gb.ansi-terminal-forgery`) | `require_approval`, except `gb.ansi-terminal-forgery`, which is `warn` |
210
+ | `agent-context` | ASI03 Identity and Privilege Abuse (`ac.agent-autonomy-flag`), ASI04 Agentic Supply Chain (`ac.mcp-server-add`), ASI06 Memory and Context Poisoning (the write, not its contents), ASI10 Rogue Agents | LLM03 Excessive Agency | `require_approval` |
211
+ | `test-integrity` | ASI10 Rogue Agents, ASI09 Human-Agent Trust Exploitation | | Mostly `warn`; deleting a test file or editing a test runner's configuration is `require_approval` |
212
+
213
+ Each guardrail lists the ids it addresses in its own `owasp` field, so
214
+ `RULES.filter((rule) => rule.owasp?.includes("ASI06"))` gives the guardrails behind one risk. Each row
215
+ above is the union of its packs' ids, with the guardrail named where only one guardrail in the pack carries
216
+ an id. A test keeps the table and the guardrails' ids equal.
217
+
218
+ Read the default action before relying on a risk. A `warn` guardrail lets the action proceed and records
219
+ it; only `require_approval` and `block` stop it. Reading a secret is a normal part of a normal day,
220
+ so most secret-exposure guardrails warn, and a dependency install is flagged, not held. A file tool
221
+ carries a path rather than content, so `ac.memory-store-edit` cannot see what was written to a
222
+ memory file. You can change any guardrail's action in your own configuration.
223
+
224
+ What the guardrails cannot address, by design:
225
+
226
+ - **Prompt injection and goal hijack (LLM01, ASI01).** The guardrails never see the input that
214
227
  redirected the agent. They can only stop the action it led to.
215
- - **Anything that needs memory across commands (LLM06, ASI08).** A rule sees one command at a
228
+ - **Anything that needs memory across commands (LLM06, ASI08).** A guardrail sees one command at a
216
229
  time, so it cannot count, rate-limit, or spot a runaway loop.
217
230
  - **Model, training and retrieval risks (LLM05, LLM08, LLM09), and traffic between agents
218
- (ASI07).** These happen somewhere a command rule never reaches.
231
+ (ASI07).** These happen somewhere a command guardrail never reaches.
219
232
 
220
233
  For the item-by-item matrix, including how the rest of AgentTrail covers each risk, see the
221
234
  [AgentTrail OWASP coverage page](https://www.agenttrail.sh/security/owasp). AgentTrail is an
@@ -223,10 +236,10 @@ independent project, not affiliated with or endorsed by OWASP.
223
236
 
224
237
  ## Talking about a command is not running it
225
238
 
226
- This is the single most important thing to understand about how these rules behave.
239
+ This is the single most important thing to understand about how these guardrails behave.
227
240
 
228
- A rule sees the command as one line of text. Nothing in that text distinguishes a command that
229
- **runs** something from one that merely **mentions** it. Left alone, that makes the rule set unusable
241
+ A guardrail sees the command as one line of text. Nothing in that text distinguishes a command that
242
+ **runs** something from one that merely **mentions** it. Left alone, that makes the guardrail set unusable
230
243
  by exactly the people most likely to install it:
231
244
 
232
245
  ```bash
@@ -234,7 +247,7 @@ git commit -m "fix: document rm -rf / risk" # would have been a hard refusal
234
247
  grep -rn "rm -rf /" docs/ # so would this
235
248
  ```
236
249
 
237
- So every command rule ignores four **carriers**, verbs that handle their arguments as text and never
250
+ So every command guardrail ignores four **carriers**, verbs that handle their arguments as text and never
238
251
  execute them:
239
252
 
240
253
  | Carrier | Example |
@@ -258,16 +271,16 @@ echo "rm -rf /" | bash # NOT exempt: the pipe runs it
258
271
  echo "$(rm -rf /var)" # NOT exempt: the shell expands `$( )`
259
272
  ```
260
273
 
261
- Five rules deliberately keep firing on one carrier each, because that carrier *is* their trigger:
262
- `gb.git-no-verify` on a `git commit`, `se.token-print` on an `echo`, the `curl`/`wget` rules on an
263
- HTTP body. Each rule's `description` says which, and why.
274
+ Five guardrails deliberately keep firing on one carrier each, because that carrier *is* their trigger:
275
+ `gb.git-no-verify` on a `git commit`, `se.token-print` on an `echo`, the `curl`/`wget` guardrails on an
276
+ HTTP body. Each guardrail's `description` says which, and why.
264
277
 
265
- ### The one rule where a mention IS the danger
278
+ ### The one guardrail where a mention IS the danger
266
279
 
267
280
  `block-hardcoded-secrets` is the exception, and it is worth understanding.
268
281
 
269
282
  Everywhere else a carrier is genuinely harmless: a commit message naming `rm -rf /` deletes nothing.
270
- But that rule's subject is a **string**, not an action, so two of the four carriers are not mentions
283
+ But that guardrail's subject is a **string**, not an action, so two of the four carriers are not mentions
271
284
  at all. They are the exposure itself:
272
285
 
273
286
  | Carrier | What happens to the key | Exempt? |
@@ -280,43 +293,43 @@ at all. They are the exposure itself:
280
293
  The cost, stated in the other direction: documenting a real-looking key in a commit message is still
281
294
  blocked. Redact the body of the key, or use a placeholder short enough to fail the length check.
282
295
 
283
- The finer limits of the carrier logic, and how command rules apply to MCP tools, are in the
296
+ The finer limits of the carrier logic, and how command guardrails apply to MCP tools, are in the
284
297
  [Reference](#reference).
285
298
 
286
- ## What these rules deliberately do not catch
299
+ ## What these guardrails deliberately do not catch
287
300
 
288
301
  Stated here rather than discovered later. Every one is a real limit of the format, not something
289
302
  somebody forgot.
290
303
 
291
- - **Nothing about the web.** Pages an agent fetches are not checked, and there are no URL rules.
304
+ - **Nothing about the web.** Pages an agent fetches are not checked, and there are no URL guardrails.
292
305
  - **Nothing inside a file.** The checker sees a file's *path*, never its contents. A secret typed into
293
306
  a source file, SQL built by string concatenation, a missing auth check: none of it is visible.
294
- Rules that would need it are absent rather than approximated.
307
+ Guardrails that would need it are absent rather than approximated.
295
308
  - **Nothing about where you are.** No working directory, no project root, no git branch, no cloud
296
309
  profile reaches the checker; it gets one command and nothing else. So "the agent wrote outside the
297
- project" **cannot be written as a rule**, and `file-scope` is limited to well-known absolute paths
298
- for good. For the same reason, a rule cannot tell a scratch database from a production one.
310
+ project" **cannot be written as a guardrail**, and `file-scope` is limited to well-known absolute paths
311
+ for good. For the same reason, a guardrail cannot tell a scratch database from a production one.
299
312
  - **Nothing hidden inside a quoted payload.** Where the danger is inside a quoted argument
300
- (`psql -c "<sql>"`, `python -c "<code>"`), a text rule can only guess. In a long script, a match
313
+ (`psql -c "<sql>"`, `python -c "<code>"`), a text guardrail can only guess. In a long script, a match
301
314
  says very little about what the script actually does.
302
315
  - **Nothing a wrapper hides.** `./deploy.sh` that runs `terraform apply -auto-approve` inside it is
303
316
  just a shell script from the outside.
304
317
  - **Nothing recurring.** There is no counting. "The same mistake three times this week" needs memory
305
318
  across commands, and a single command has none.
306
319
 
307
- Each rule's own `description` names its specific misses. Read those before trusting a rule to cover a
320
+ Each guardrail's own `description` names its specific misses. Read those before trusting a guardrail to cover a
308
321
  case; they are written to be believed, not to sell.
309
322
 
310
- ## Every rule proves both directions
323
+ ## Every guardrail proves both directions
311
324
 
312
- Every rule ships at least one **`block`** example and at least one **`allow`** example. A rule missing
325
+ Every guardrail ships at least one **`block`** example and at least one **`allow`** example. A guardrail missing
313
326
  either does not build.
314
327
 
315
- - **`block` means "this rule must match."** It does *not* mean the agent is refused; most packs
328
+ - **`block` means "this guardrail must match."** It does *not* mean the agent is refused; most packs
316
329
  default to asking or warning.
317
- - **`allow` means "this rule must NOT match."** *This is the half that matters.* Anyone can write a
318
- rule that catches `rm -rf /`. The hard part is not firing on `rm -rf ./node_modules` forty times a
319
- day, and a rule with no negative example has not shown it can tell them apart.
330
+ - **`allow` means "this guardrail must NOT match."** *This is the half that matters.* Anyone can write a
331
+ guardrail that catches `rm -rf /`. The hard part is not firing on `rm -rf ./node_modules` forty times a
332
+ day, and a guardrail with no negative example has not shown it can tell them apart.
320
333
 
321
334
  Examples come in two kinds, and each must use the right one:
322
335
 
@@ -325,13 +338,13 @@ Examples come in two kinds, and each must use the right one:
325
338
  | a command | `Bash`, `PowerShell`, a search query, an MCP tool's input |
326
339
  | a file path | `Edit`, `Write`, `Read`, `MultiEdit`, `NotebookEdit` |
327
340
 
328
- Giving a path-matching rule a command example makes it pass **without testing anything**: it matches
329
- nothing, which reads as proof of quietness and proves only that the path never reached the rule. CI
341
+ Giving a path-matching guardrail a command example makes it pass **without testing anything**: it matches
342
+ nothing, which reads as proof of quietness and proves only that the path never reached the guardrail. CI
330
343
  rejects that.
331
344
 
332
- ## Contributing a rule
345
+ ## Contributing a guardrail
333
346
 
334
- Rules are meant to be contributed. The bar is not "clever regex"; it is **does it fire on the real
347
+ Guardrails are meant to be contributed. The bar is not "clever regex"; it is **does it fire on the real
335
348
  thing, and stay quiet on the near-miss**.
336
349
 
337
350
  **1. Write it**, following the shape above. Give it a `description` that says what it misses.
@@ -348,10 +361,10 @@ if (!result.success) console.error(result.error.issues);
348
361
  Or, if you have the guard installed:
349
362
 
350
363
  ```bash
351
- agenttrail-guard guardrails validate ./my-rule.json
364
+ agenttrail-guard guardrails validate ./my-guardrail.json
352
365
  ```
353
366
 
354
- **3. Understand what that does and does not tell you.** It answers *"is this a well-formed rule?"* It
367
+ **3. Understand what that does and does not tell you.** It answers *"is this a well-formed guardrail?"* It
355
368
  does **not** answer *"does it actually fire on the command I think it does?"* That needs the real
356
369
  checker, which is not part of this package.
357
370
 
@@ -359,18 +372,18 @@ checker, which is not part of this package.
359
372
  everyone. You get the shape check instantly here and the real answer there, which is where it has to
360
373
  run to be trusted anyway.
361
374
 
362
- Your rule is also run against a **quiet corpus**: hundreds of everyday commands and paths that no
363
- rule may match at all. Your own negative example only proves your rule is quiet on the near-miss
364
- *you* thought of. The quiet corpus is what catches a Terraform rule firing on `pnpm test`.
375
+ Your guardrail is also run against a **quiet corpus**: hundreds of everyday commands and paths that no
376
+ guardrail may match at all. Your own negative example only proves your guardrail is quiet on the near-miss
377
+ *you* thought of. The quiet corpus is what catches a Terraform guardrail firing on `pnpm test`.
365
378
 
366
379
  ### Shapes that will not validate
367
380
 
368
- Three are rejected outright, each because it produces a rule that *looks* enforced and is not: the
381
+ Three are rejected outright, each because it produces a guardrail that *looks* enforced and is not: the
369
382
  worst failure a security tool can have.
370
383
 
371
384
  | Rejected | Why |
372
385
  |---|---|
373
- | `scope` | It compares against ids that are always UUIDs, never a vendor name, so a scoped rule matches nothing, forever, silently. |
386
+ | `scope` | It compares against ids that are always UUIDs, never a vendor name, so a scoped guardrail matches nothing, forever, silently. |
374
387
  | Numeric conditions | Token counts and durations are all zero *before* a command runs. "Greater than" can never fire; "less than" fires on everything. |
375
388
  | A command matcher and a file matcher in one condition | No real command carries both, so the condition can never be true. Split it into two under `any_of`. |
376
389
 
@@ -380,7 +393,7 @@ command through.
380
393
 
381
394
  One more is caught in CI rather than by the shape check, because it cannot be caught earlier: **a
382
395
  single-item brace list in a tool name.** `"{Bash}"` is a glob pattern, and it does not match `Bash`,
383
- so the rule matches nothing, forever, with no error anywhere. Write a single tool plainly as
396
+ so the guardrail matches nothing, forever, with no error anywhere. Write a single tool plainly as
384
397
  `"Bash"`; braces are for real alternatives, `"{Bash,PowerShell}"`.
385
398
 
386
399
  ### Ready to open a pull request?
@@ -391,32 +404,32 @@ and report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
391
404
 
392
405
  ## Stability and versioning
393
406
 
394
- Rule ids and pack names end up in people's config files, so they are treated as public API:
407
+ Guardrail ids and pack names end up in people's config files, so they are treated as public API:
395
408
 
396
- - **A rule id never changes.** Renaming or removing a rule would silently undo an override
409
+ - **A guardrail id never changes.** Renaming or removing a guardrail would silently undo an override
397
410
  someone set on it, so either one is a breaking change.
398
411
  - **A pack name never changes** either, for the same reason: a renamed pack silently
399
412
  re-enables a pack someone had turned off.
400
- - **A new rule or pack is a feature.** Guard turns every pack on unless you turned it off, so a
401
- rule added in a release you install starts enforcing with no step of your own.
413
+ - **A new guardrail or pack is a feature.** Guard turns every pack on unless you turned it off, so a
414
+ guardrail added in a release you install starts enforcing with no step of your own.
402
415
 
403
416
  Releases are cut automatically from [Conventional Commits](https://www.conventionalcommits.org/).
404
- Before 1.0, a breaking change ships as a minor release and a new rule as a patch release, so
417
+ Before 1.0, a breaking change ships as a minor release and a new guardrail as a patch release, so
405
418
  skim the [changelog](CHANGELOG.md) before you upgrade.
406
419
 
407
420
  ## FAQ
408
421
 
409
- **Can I use these rules without Guard?** Yes. They are plain data under Apache-2.0. You need your
422
+ **Can I use these guardrails without Guard?** Yes. They are plain data under Apache-2.0. You need your
410
423
  own matcher to apply them, because this package does not contain one.
411
424
 
412
- **A rule fires on something legitimate. What do I do?** In Guard, `agenttrail-guard guardrails
413
- allow <id> <pattern>` silences that one rule for that one command shape, without turning the rule
425
+ **A guardrail fires on something legitimate. What do I do?** In Guard, `agenttrail-guard guardrails
426
+ allow <id> <pattern>` silences that one guardrail for that one command shape, without turning the guardrail
414
427
  off. Then please [open an issue](https://github.com/agenttrailhq/guardrails/issues) with a
415
- sanitized example, so the rule can learn the near-miss.
428
+ sanitized example, so the guardrail can learn the near-miss.
416
429
 
417
- **Why didn't a rule fire on my command?** Start with the rule's `description`: it lists what the
418
- rule misses. Then check [what these rules deliberately do not catch](#what-these-rules-deliberately-do-not-catch).
419
- To test a command against every rule, paste it into the [online checker](https://www.agenttrail.sh/guardrails#check).
430
+ **Why didn't a guardrail fire on my command?** Start with the guardrail's `description`: it lists what the
431
+ guardrail misses. Then check [what these guardrails deliberately do not catch](#what-these-guardrails-deliberately-do-not-catch).
432
+ To test a command against every guardrail, paste it into the [online checker](https://www.agenttrail.sh/guardrails#check).
420
433
 
421
434
  **Why is `secret-exposure` mostly `warn`?** Reading a secret is a normal part of a normal day.
422
435
  Refusing every `.env` read would be turned off within a week. Moving a secret off the machine is
@@ -430,16 +443,19 @@ held for approval instead.
430
443
  ### Two ways to import it
431
444
 
432
445
  ```ts
433
- import { RULES, getRule } from "@agenttrail/guardrails/guardrails"; // just the rules
434
- import { parseRule } from "@agenttrail/guardrails"; // rules + the shape checker
446
+ import { RULES, getRule } from "@agenttrail/guardrails/guardrails"; // just the guardrails
447
+ import { parseRule } from "@agenttrail/guardrails"; // guardrails + the shape checker
435
448
  ```
436
449
 
437
- Use the first when you want to *apply* rules, and the second when you want to *validate* one you are
450
+ The exports keep their original names (`RULES`, `getRule`, `parseRule`, `defineRule`, `Rule`); each
451
+ entry is a guardrail.
452
+
453
+ Use the first when you want to *apply* guardrails, and the second when you want to *validate* one you are
438
454
  writing.
439
455
 
440
456
  They are separate because the guard starts a fresh process on **every single command** an agent runs,
441
457
  under a ten-second ceiling. It cannot afford to load a validator it never calls, or to re-check 74
442
- rules that were already checked before release.
458
+ guardrails that were already checked before release.
443
459
 
444
460
  ### Known limits of the carrier logic
445
461
 
@@ -457,38 +473,38 @@ In the same spirit as the rest of this file:
457
473
 
458
474
  ### MCP coverage
459
475
 
460
- A command rule fires on `Bash`, `PowerShell` *and* any `mcp__*` tool: the guard hands the checker an
461
- MCP call's serialized `tool_input` as the same command text every command rule reads, so a command
476
+ A command guardrail fires on `Bash`, `PowerShell` *and* any `mcp__*` tool: the guard hands the checker an
477
+ MCP call's serialized `tool_input` as the same command text every command guardrail reads, so a command
462
478
  shape run through an MCP server (`{"command":"rm -rf /"}`) is caught, not ignored. Two honest limits
463
479
  follow from that:
464
480
 
465
- 1. **Start-anchored patterns may miss.** A rule whose pattern is anchored to the start of the command
481
+ 1. **Start-anchored patterns may miss.** A guardrail whose pattern is anchored to the start of the command
466
482
  (`^…` or a command-position class) may not fire inside the JSON, where the shape sits after a `"`
467
- rather than at a command boundary; the `\b`-anchored rules (most of the corpus) do fire.
483
+ rather than at a command boundary; the `\b`-anchored guardrails (most of the corpus) do fire.
468
484
  2. **Mentions are not exempt.** The quoted-mention exemptions are shell-only, so an MCP payload that
469
485
  merely *names* a command in a text field (`{"title":"fix the rm -rf / bug"}`) is matched the same
470
486
  as one that runs it; a JSON blob cannot be told apart from a shell-running MCP server.
471
487
 
472
- File rules match by path on whichever file tool a client uses. No rule is shell-only by design; a
473
- rule that does not reach the MCP channel does so because its pattern, not its label, does not match
488
+ File guardrails match by path on whichever file tool a client uses. No guardrail is shell-only by design; a
489
+ guardrail that does not reach the MCP channel does so because its pattern, not its label, does not match
474
490
  the serialized shape.
475
491
 
476
- ### Where these rules came from
492
+ ### Where these guardrails came from
477
493
 
478
494
  Independently authored. No block list, pattern or wording is copied from any other project.
479
495
 
480
- Where a rule's shape follows an obvious convention (an `rm -rf` pattern looks like an `rm -rf`
496
+ Where a guardrail's shape follows an obvious convention (an `rm -rf` pattern looks like an `rm -rf`
481
497
  pattern), that is two people meeting the same shell, not one copying the other.
482
498
 
483
499
  ## Part of AgentTrail
484
500
 
485
- These rules are part of [AgentTrail](https://www.agenttrail.sh), which builds guardrails and
501
+ This library is part of [AgentTrail](https://www.agenttrail.sh), which builds guardrails and
486
502
  observability for AI coding agents:
487
503
 
488
- - **[AgentTrail Guard](https://www.agenttrail.sh/agenttrail-guard)** enforces them on every
504
+ - **[AgentTrail Guard](https://www.agenttrail.sh/agenttrail-guard)** enforces these guardrails on every
489
505
  tool call that reaches its hooks, on your machine. It is free, open source, and needs no
490
506
  account. Its code is in [agenttrailhq/guard](https://github.com/agenttrailhq/guard).
491
- - **[AgentTrail OS](https://www.agenttrail.sh/agenttrail-os)** uses the same open rule library,
507
+ - **[AgentTrail OS](https://www.agenttrail.sh/agenttrail-os)** uses the same open guardrail library,
492
508
  and adds searchable session history and team workflows. See
493
509
  [AgentTrail Guard vs AgentTrail OS](https://www.agenttrail.sh/guard-vs-os).
494
510
 
@@ -496,7 +512,7 @@ observability for AI coding agents:
496
512
 
497
513
  Apache-2.0. See [LICENSE](./LICENSE).
498
514
 
499
- Rule ideas and reports go to [Guardrails issues](https://github.com/agenttrailhq/guardrails/issues).
500
- If you rely on these rules, a star helps other developers find them.
515
+ Guardrail ideas and reports go to [Guardrails issues](https://github.com/agenttrailhq/guardrails/issues).
516
+ If you rely on these guardrails, a star helps other developers find them.
501
517
 
502
518
  Made with ❤️ by [agenttrail.sh](https://www.agenttrail.sh)