Skip to content

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.

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
}
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:.


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.

kind (reachability or metric), alive ("true"/"false"), via, rtt, metric, value, if_index, if_name, units, device_id.

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
}

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
}

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:%".


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.

detection (the rule that produced the event), device_id, device_class, tag, severity, source, collector_id, message.

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
}

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
}

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
}

  • 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