// security workers
Write and enforce rules
Create, validate, assign, monitor, and enforce Runtime Guardrails rules.
Create a rule
Rules detect behavior in normalized coding-agent activity. They are evaluated locally on each client and distributed through client groups.
Default rules
The Rules table includes the full built-in catalog alongside custom rules. Default rows are labeled Default and apply to all clients.
Deleting a default rule creates a disabled organization override; it does not modify the client binary. The row remains visible with a Disabled status so administrators can restore it later. Select the restore action to remove the override and reactivate the embedded default.
Custom rules are labeled Custom and can be assigned to selected groups. Deleting a custom rule permanently removes it.
Generate a rule with the assistant
- Open Agents → Rules.
- Select New rule.
- Describe the behavior you want to detect in the rule generator terminal.
- Select Generate YAML.
- Review the generated YAML in the editor.
- Ask follow-up questions to refine it, or edit the YAML directly.
- Assign one or more client groups and select Validate and save.
For example:
Alert when an agent reads a .env file and then sends data to an external URL.
Make this high severity, but do not block it.After the first response, the YAML editor appears above the terminal. Follow-up terminal instructions use the current YAML as context:
Limit this to events in production projects and change the window to 30 events.The assistant is available only to organization owners. Generation is rate-limited and requires AI Gateway configuration on self-hosted deployments.
Write YAML manually
Open New rule and replace the commented example in the YAML editor. You do not need to send a prompt to the rule assistant.
| Field | Required | Description |
|---|---|---|
id |
Yes | Stable, dot-separated identifier using lowercase letters, numbers, dashes, or underscores |
version |
Yes | Quoted version copied into alerts |
enabled |
No | Whether the rule is active; defaults to true |
title |
Yes | Human-readable alert title |
description |
No | Precise explanation of what matches and what the finding proves |
severity |
Yes | info, low, medium, high, or critical |
tags |
No | Categories copied into findings, including relevant MITRE ATT&CK tags |
expr |
One of | CEL predicate evaluated against one event |
sequence |
One of | Ordered multi-event detection |
enforce |
No | Blocks matching actions when true; defaults to false (monitor only) |
deny_message |
No | Message returned when an enforceable action is denied |
Define exactly one of expr or sequence.
See the rule language reference for every normalized event field, CEL operator, parsed shell command, sequence window, and validation rule.
Single-event rules
Use expr when one event is enough to identify the behavior:
id: acme.secrets.env_read
version: "1.0"
enabled: true
title: Environment file read
description: Detects reads of environment files that commonly contain credentials.
severity: high
tags:
- secrets
- attack.t1552.001
expr: |-
event.event_type == "file.read" &&
event.file_path.matches("(^|/)\\.env$")Useful fields include:
event.event_typeevent.file_pathevent.commandevent.tool_nameevent.urlevent.content_previewevent.project_pathevent.source_agentevent.tags
Expressions support normal CEL operators and helpers such as ==, !=, &&, ||, contains(), startsWith(), endsWith(), and matches().
Sequence rules
Use sequence when events must occur in order:
id: acme.secrets.read_then_send
version: "1.0"
enabled: true
title: Secret read followed by external transfer
description: Detects an environment-file read followed by an outbound network indicator.
severity: critical
tags:
- secrets
- exfiltration
sequence:
within_events: 30
steps:
- expr: |-
event.event_type == "file.read" &&
event.file_path.matches("(^|/)\\.env$")
- expr: |-
event.event_type == "network.indicator" &&
event.url != ""A sequence requires two to eight steps and at least one window:
within— wall-clock duration such as30mwithin_events— event-count window, up to 4096
Monitoring and enforcement
Rules are monitor-only unless the YAML contains enforce: true (or Mode is set to Enforce in the UI). Installed hooks can deny matching actions for enforce rules; monitor rules only record findings.
Four high-confidence catastrophic-action defaults start in Enforce mode: recursive deletion of root or home, destructive disk operations, fork bombs, and forced termination of all accessible processes. Organization owners can change any of them to Monitor or Disabled. Other default rules start in Monitor mode to avoid blocking legitimate developer and administrator workflows.
enforce: true
deny_message: Contact your security team before accessing this file.Severity does not enable blocking. It only prioritizes the resulting alert.
Assign rules to groups
Use terminal commands to manage group assignments while editing:
/groups
/group add Production
/group remove Production
/group create New groupA client receives the union of rules assigned to all of its groups. Rule IDs are unique within an organization, so assigning one rule through multiple groups does not duplicate it.
Use /save to validate and save, /clear to reset the YAML, /close to close the panel, and /help to list commands.
Validation
Superagent validates the YAML structure and compiles every generated or edited rule with the same Numbat monitoring engine before accepting it. Compiler diagnostics are shown in the rule editor so unsupported fields, functions, and expressions can be fixed before saving.
Each endpoint validates downloaded rules again before activation. If a new catalog fails validation, the endpoint keeps its last known-good rules.
On self-hosted Node deployments, you can set AGENT_RULE_VALIDATOR_PATH to an executable Numbat v0.1.2 binary. When it is unset, the server downloads the pinned archive for macOS or Linux on arm64 or x64 during the first validation in each process, verifies the archive SHA-256 checksum, and keeps the validator only in that process's private temporary directory. Rule saves fail safely if the validator is unavailable.
Rule language reference
You can write Runtime Guardrails rules directly in the YAML editor. The rule assistant is optional.
- Open Agents → Rules.
- Select New rule.
- Replace the commented example in the editor with your rule.
- Choose the client groups that should receive it and set the rule to Monitor, Enforce, or Disabled.
- Select Save.
Superagent validates the YAML structure and compiles the CEL expression before saving. Each endpoint validates the rule again before activation.
Start with a complete rule
This rule detects reads of .env files while ignoring .env.example:
id: acme.secrets.env_read
version: "1.0"
enabled: true
title: Environment file read
description: |-
Detects a requested read of an environment file that may contain credentials.
It does not treat example environment files as secrets.
severity: high
tags:
- secrets
- attack.t1552.001
expr: |-
event.event_type == "file.read" &&
event.file_path.matches("(^|/)\\.env(\\.[^/]+)?$") &&
!event.file_path.endsWith(".env.example")
enforce: falseUse an organization-specific prefix such as acme. for custom rule IDs. Increment version whenever the rule logic changes so findings show which policy produced the match.
Top-level fields
A rule is one YAML object, up to 128 KiB of UTF-8 text. Do not use YAML aliases, wrap it in rules:, place multiple rules in one document, or add unknown top-level fields.
| Field | Required | Description |
|---|---|---|
id |
Yes | Stable, dot-separated identifier, up to 128 characters |
version |
Yes | Rule-owned version string copied into findings |
title |
Yes | Human-readable finding title |
severity |
Yes | info, low, medium, high, or critical |
expr |
One of | CEL predicate evaluated against one event |
sequence |
One of | Ordered multi-event detection |
description |
No | Operator-facing explanation of what a match proves |
tags |
No | Categories copied into findings |
enabled |
No | Enables the rule; defaults to true |
enforce |
No | Makes eligible matches blockable; defaults to false |
deny_message |
No | Control-free, single-line message returned for a denied action, up to 512 UTF-8 bytes |
Define exactly one of expr or sequence. Superagent recommends including description, tags, and an explicit enabled value even though the rule engine can supply their defaults.
Rule IDs use lowercase letters, digits, underscores, and dashes in dot-separated segments. Each segment must start and end with a letter or digit.
The Mode control in the editor writes enabled and enforce when you save. Use that control when changing a rule's operating mode.
Write CEL expressions
Every expr must return true or false. Start by selecting the event types the rule understands, then add the conditions that identify the behavior.
event.event_type == "file.write" &&
event.file_path.startsWith("/etc/")Common CEL operations include:
| Operation | Example |
|---|---|
| Boolean logic | a && b, `a |
| Equality | a == b, a != b |
| Membership | value in ["a", "b"] |
| String tests | contains(), startsWith(), endsWith(), matches() |
| List predicates | exists(), all(), exists_one() |
| List ranges | items.slice(start, end) |
| Integer indexes | lists.range(n).exists(i, ...) |
| Missing nullable value | event.exit_code == null |
matches() uses RE2 regular expressions. CEL strings require escaping, so a literal dot is written as "\\.env".
Use direct field access such as event.command. Unknown fields and computed field access are rejected.
Choose the right input
Rules can evaluate two inputs:
eventis the normalized action and context recorded across supported coding agents.shell_commandsis a parsed, rule-only view of commands found inevent.command.
Choose the simplest input that preserves the distinction you need:
| What you need to match | Input |
|---|---|
| File, URL, MCP, permission, model, or other normalized data | event |
| Literal text in the original command | event.command |
| Executable names, arguments, pipelines, redirects, or wrappers | shell_commands |
| Ordered activity across multiple events | sequence |
Raw command text can match comments, quoted examples, or here-document content. Prefer shell_commands when a rule depends on what the shell will execute, especially for enforcement rules.
Normalized events
Actions use the most specific available event type. A recognized shell request is command.exec, not both command.exec and tool.call. tool.call is the fallback when the action cannot be classified more specifically.
Common event types and their principal fields are:
| Event type | Meaning | Principal fields |
|---|---|---|
command.exec |
Requested shell or process action | command, tool_name, tool_call_id |
command.result |
Observed command result | command, exit_code, duration_ms, tool_call_id |
file.read |
Requested file access | file_path, tool_name, tool_call_id |
file.write |
Requested or observed file change | file_path, diff_sha256, diff_bytes |
file.delete |
Requested or observed file deletion | file_path, diff_sha256, diff_bytes |
network.indicator |
Known web or network target | url, mcp_server, mcp_tool |
tool.call |
Other or unknown tool request | tool_name, mcp_server, mcp_tool |
tool.result |
Result of a generic tool call | tool_name, tool_call_id |
permission.requested |
Permission prompt | approval_required, approval_reason |
permission.approved |
Approved permission request | approval_decision, approval_reason |
permission.denied |
Denied permission request | approval_decision, approval_reason |
config.agent |
Observed agent configuration | Context fields |
config.mcp |
Observed MCP configuration | mcp_server, mcp_tool |
session.start, session.end |
Session boundary | Context fields |
prompt.user |
User prompt | content_preview |
message.assistant, message.reasoning |
Agent output | content_preview |
Context fields are available on every event type:
event.actor
event.cli_version
event.confidence
event.content_preview
event.entrypoint
event.git_branch
event.model
event.model_provider
event.project_path
event.session_id
event.source_agent
event.source_type
event.sub_agent
event.tags
event.timestampAll event keys are present during evaluation. Missing strings are "", missing diff_bytes is 0, and missing tags are []. The nullable fields approval_required, duration_ms, and exit_code are null when unavailable.
Pre-action events describe requested behavior, not confirmed execution. A later result may be linked by tool_call_id, but some integrations record only one side.
For command activity, hooks and artifacts normally provide command.exec. Some OpenTelemetry sources expose only command.result, so a rule that needs both capture paths can use:
event.event_type == "command.exec" ||
(event.source_type == "otel" && event.event_type == "command.result")The source_type condition prevents the rule from matching both the request and result on sources that emit both.
Parse executable commands
Use shell_commands to reason about executable behavior instead of matching raw text:
id: acme.network.curl_upload
version: "1.0"
enabled: true
title: Curl file upload requested
description: Detects curl commands that request a local file upload.
severity: high
tags:
- exfiltration
expr: |-
event.event_type == "command.exec" &&
shell_commands.exists(command,
command.name == "curl" &&
command.argv.slice(1, command.argv.size()).exists(arg,
arg in ["--upload-file", "-T"]
)
)
enforce: falseEach parsed command provides:
| Field | Description |
|---|---|
name |
Lowercase executable basename with common Windows suffixes removed |
executable |
Statically parsed executable value |
argv |
Parsed arguments, including the executable at index 0 |
arguments |
Arguments with source, quoting, and expansion metadata |
assignments |
Command-scoped shell assignments |
redirects |
Input and output redirections |
dialect |
posix, powershell, or cmd |
wrappers |
Recognized launchers around the command |
statement_id |
Identifier for this command within the event |
pipeline_id |
Shared identifier for commands in one pipeline; 0 means none |
parent_statement_id |
Enclosing command identifier; 0 means none |
preview |
normal, preview, or uncertain |
function_call |
Whether this is a statically resolved shell function call |
recursive |
Whether this is a statically resolved recursive function call |
The richer collections expose these nested fields:
| Object | Fields |
|---|---|
argument |
value, source, quote, expands, subcommands |
assignment |
name, value, append |
redirect |
fd, op, target, target_source, target_quote, target_expands, subcommands |
wrapper |
name, executable, argv |
For example, detect a write or append redirect to an SSH authorization file:
shell_commands.exists(command,
command.redirects.exists(redirect,
redirect.op in ["write", "append"] &&
redirect.target.endsWith("/authorized_keys")
)
)The parser does not execute commands, expand variables, or read referenced scripts. If no safe static projection is available, rules that depend on shell_commands skip that event.
Correlate events with sequences
Use a sequence when one event is not enough to establish the behavior:
id: acme.secrets.read_then_send
version: "1.0"
enabled: true
title: Secret read followed by external transfer
description: Detects an environment file read followed by a curl command.
severity: critical
tags:
- secrets
- exfiltration
sequence:
within_events: 30
steps:
- expr: |-
event.event_type == "file.read" &&
event.file_path.matches("(^|/)\\.env$")
- expr: |-
event.event_type == "command.exec" &&
shell_commands.exists(command, command.name == "curl")
enforce: false| Sequence field | Description |
|---|---|
steps |
Two to eight ordered CEL predicates |
within |
Optional wall-clock window such as 30m |
within_events |
Optional event-count window, from the step count through 4096 |
max_matches |
Findings per rule and correlation partition; defaults to 1, maximum 16 |
Set within, within_events, or both. When both are present, both limits must hold.
Sequences correlate only within the same agent, source, session, and project. The final step is the only action an enforced sequence can block because earlier steps have already happened.
Monitor or enforce
Rules monitor by default. Set Mode → Enforce in the editor to make a matching pre-action eligible for blocking. The saved YAML contains:
enforce: true
deny_message: Contact your security team before accessing this file.severity only prioritizes the finding. It does not enable blocking.
Enforcement also depends on the coding agent exposing a supported synchronous pre-action hook. Post-action events, artifact scans, and sources without a blocking hook remain detection-only. For shell-derived matches, commands with dynamic values, substitutions, multiple statements, unsupported control flow, or parser diagnostics can be detected but are not blocked.
Use monitor mode first, review the resulting findings for false positives, and then enable enforcement for high-confidence rules.
Fix validation errors
Selecting Save runs both structural validation and CEL compilation. Common failures include:
- Defining both
exprandsequence, or neither - Using an unknown event or command field
- Returning a non-boolean CEL value
- Forgetting to escape a backslash inside a CEL string
- Using fewer than two or more than eight sequence steps
- Setting
within_eventsbelow the number of steps or above4096 - Using a duplicate rule ID
If a new catalog fails validation on an endpoint, that endpoint keeps its last known-good rules.