Writing Rules (Detections & Case Rules)
Detections and Case Rules are written in the same compact rule language. A Detection turns a log or telemetry pattern into an event. A Case Rule watches the stream of events and groups correlated noise (flaps, storms) into a single Case. This page is the reference for both.
The editor’s Test button validates a rule and shows what it compiled to — use it as you write.
Anatomy of a rule
Section titled “Anatomy of a rule”rule my_rule { meta: # settings (key = value) events: # what to match ($e.field op value) match: # OPTIONAL: group key + time window condition: # OPTIONAL: how many matches before it fires close: # OPTIONAL (Detections): close earlier events on recovery}meta settings
Section titled “meta settings”| Key | Values | Meaning |
|---|---|---|
type |
detection (default) · case |
case makes it a Case Rule |
source |
log (default) · telemetry |
Detections: which stream to watch |
severity |
critical high medium low info (or 1–5) |
|
description |
text | shown in the UI |
tag |
text | the event’s Tag (defaults to the rule name) |
status |
open (default) · closed |
closed = a recovery/audit record |
window |
e.g. 5m |
default window when there’s no match: |
autoclose |
e.g. 3h |
Case Rules: close the case after this much quiet |
Time units: s m h d — e.g. 30s, 15m, 3h.
Operators: = != > < >= <= contains.
Group placeholders: $e.field = $x binds field as a group key you then use in match:.
Detections
Section titled “Detections”Log fields (source = "log", the default)
Section titled “Log fields (source = "log", the default)”sourcetype, source, host, severity_id, facility, sender_ip, class_uid, category_uid, activity_id, type_uid, src_endpoint_ip, src_endpoint_hostname, src_endpoint_port, actor_user_name, dst_endpoint_ip, dst_endpoint_hostname, protocol_name, action_id, status_id, device_id, collector_id, site_id, transport, subsystem, category, process, tag, signature — plus _raw (use
only with contains) and fields.<name> for any field you’ve extracted.
class_uid/category_uid/activity_id/type_uid together classify what kind
of event it is (for example, class_uid = 3002 + activity_id = 1 is an
authentication logon). status_id is the outcome: 1 = success, 2 =
failure, 99 = other. severity_id is the log’s own reported severity, on a
0 (unknown) to 6 (fatal) scale where a higher number is more severe — this
is separate from the meta.severity you set for the event the rule raises.
Telemetry fields (source = "telemetry")
Section titled “Telemetry fields (source = "telemetry")”kind (reachability or metric), alive ("true"/"false"), via, rtt,
metric, value, if_index, if_name, units, device_id.
Example 1 — a single log event
Section titled “Example 1 — a single log event”No match: means it fires on every match.
rule root_ssh_login { meta: severity = "high" description = "Direct SSH login as root" events: $e.class_uid = 3002 # Authentication $e.activity_id = 1 # Logon $e.status_id = 1 # Success $e.actor_user_name = "root"}Example 2 — a windowed count (brute force)
Section titled “Example 2 — a windowed count (brute force)”Group by source IP, count failures in a window. This is the built-in SSH brute force detection GridNMS ships with.
rule ssh_brute_force { meta: severity = "high" description = "10+ failed logins from one source IP within 5 minutes" events: $e.class_uid = 3002 # Authentication $e.activity_id = 1 # Logon $e.status_id = 2 # Failure $e.src_endpoint_ip != "" $e.src_endpoint_ip = $ip $e.host = $host match: $ip, $host over 5m condition: #e >= 10}Example 3 — a telemetry threshold
Section titled “Example 3 — a telemetry threshold”This is what the interface Thresholds panel generates for you.
rule eth0_in_high { meta: source = "telemetry" severity = "minor" tag = "THRESHOLD:eth0:in_bps" events: $e.kind = "metric" $e.device_id = 4 $e.if_index = 12 # or: $e.if_name = "eth0" $e.metric = "in_bps" $e.value > 100000}Example 4 — auto-clear (recovery)
Section titled “Example 4 — auto-clear (recovery)”A recovery rule fires its own event when the condition clears, marked as the recovery for the matching breach — events themselves are permanent records, never edited or deleted, so this raises a paired recovery event rather than changing the original. This is what the Auto-clear checkbox builds for you automatically.
rule eth0_in_high_clear { meta: source = "telemetry" status = "closed" events: $e.kind = "metric" $e.device_id = 4 $e.if_index = 12 $e.metric = "in_bps" $e.value <= 100000 close: tag = "THRESHOLD:eth0:in_bps"}close: also accepts a pattern: tag matches "IF-DOWN:%".
Case Rules (type = "case")
Section titled “Case Rules (type = "case")”A Case Rule runs over the events stream. events: selects which events to watch,
match: is the group key + window, and condition: is the count threshold.
When a group trips, GridNMS opens one Case, attaches the events, sends a single
notification, and suppresses the per-event alerts.
Event fields
Section titled “Event fields”detection (the rule that produced the event), device_id, device_class, tag,
severity, source, collector_id, message.
Example 1 — flapping (built in)
Section titled “Example 1 — flapping (built in)”A detection recurring on the same device.
rule flapping_signal { meta: type = "case" title = "Flapping signal" severity = "minor" autoclose = "3h" events: $e.source = "detector" $e.severity <= 4 $e.detection = $det $e.device_id = $dev match: $det, $dev over 15m condition: #e >= 2}Example 2 — a storm on one device
Section titled “Example 2 — a storm on one device”E.g. a switch reboot dumping many interface-down events.
rule device_event_storm { meta: type = "case" title = "Event storm on a device" severity = "major" autoclose = "1h" events: $e.device_id = $dev match: $dev over 5m condition: #e >= 15}When rules overlap — priority
Section titled “When rules overlap — priority”Case Rules evaluate like firewall rules: the highest-priority matching rule wins an event, and an event belongs to exactly one rule’s case. Each rule’s priority is derived automatically from how specific it is (a rule that names a detection outranks a broad catch-all, and more group-by fields and filters add weight), or you can set an explicit Priority (0–32000) on the rule’s edit sheet to pin the order. When a higher-priority rule opens a case, it also claims matching recent events out of any lower-priority rule’s case, so the outcome is the same no matter which rule fired first. Cases you open by hand are never touched — no rule can pull events out of them. Rules with equal priority split events the same deterministic way every time.
Streaming and telemetry detections work the same way. When a log line or a device reading matches more than one streaming/telemetry detection, only the highest-priority one fires — it raises a single event instead of one per matching rule. Leave Priority blank to have it derived from how specific the detection is, or set an explicit value (0–32000) to pin the order. Recovery (“auto-clear”) always tracks its own detection, so turning a detection’s priority up never leaves a “device down” event stuck open. Scheduled (timer-based) log detections are unaffected — each still runs on its own and raises its own events.
Example 3 — own a specific detection’s cases
Section titled “Example 3 — own a specific detection’s cases”Naming a detection makes this rule responsible for its cases (and turns off that detection’s own “Open a Case on match”).
rule ssh_bruteforce_cases { meta: type = "case" title = "SSH brute-force incidents" autoclose = "6h" events: $e.detection = "ssh_brute_force" $e.device_id = $dev match: $dev over 30m condition: #e >= 1}Good to know
Section titled “Good to know”- The first match still alerts you — Case grouping only kicks in once the threshold trips, so you never miss the initial signal.
- Notifications are turned on per detection (the Notify section). A Case sends its single alert to the producing detection’s recipients.
- Auto-clear vs Case Rules: auto-clear closes one event when it recovers; a Case Rule groups a pattern of events into an incident. They complement each other.
- Severity words map to numbers: critical = 1, high = 2, medium/minor = 3, low/warning = 4, info = 5.
docs built 2026-09-26 · 195c6d00