Ingestion policy
Every environment has an ingestion policy: the rules Mobius applies on your Wazuh indexer to decide which alerts it fetches and triages. It is the first gate in the pipeline. An alert the policy filters out never leaves your Wazuh, is never analysed, and is never billed.
The policy runs as the query against your indexer, not as a filter inside Mobius. An alert that does not match stays in Wazuh only. If you narrow the policy and later widen it, the alerts that arrived in between are not back-filled. Test a change before saving it.
Open it from Management > Environments: each environment card shows the Ingestion policy summary, with View policy to open the editor and History for every saved version. Only users in the admins group can edit a policy; anyone can read it.
What the defaults are
A newly connected environment starts with the recommended policy:
| Setting | Default |
|---|---|
| Minimum severity | High+ (Wazuh 4.x rule.level >= 12, Wazuh 5.x high or critical) |
| Manager alerts | Ignored (alerts from an agent named wazuh-manager-* are dropped) |
| Include rules | none, so every alert at or above the floor is a candidate |
| Exclude rules | none |
| Advanced query | off |
| Rate limit | 5 events per second, hard limit 50 per second |
Environments connected before version 3.0.0 were migrated automatically. Their policy reproduces exactly what they ingested before: the floor those versions applied (Medium+ on 4.x, High+ on 5.x), manager alerts ignored, and any former ingestion scope rules translated one-for-one into include rules named Migrated scope rule N. Nothing changed in what those environments ingest; it is now visible and editable.
How the parts combine
The editor's Policy logic box shows the exact sentence, but the order is always the same. An alert is sent to Mobius only when all of these hold:
- Severity floor. It is at or above the minimum severity. Alerts below the floor are never fetched, whatever the rules say.
- Manager alerts. It is not a manager self-alert, unless you turned that filter off.
- Exclude rules. It matches no exclude rule. Exclusions always win, even over an include rule or an advanced query.
- Include rules or the advanced query. With no include rules and advanced mode off, every remaining alert is a candidate. With include rules, it must match at least one of them. With advanced mode on, it must match your query instead.
- Rate limit. Above the events-per-second cap, alerts are deferred to the next poll in order, never dropped, up to a six-hour backlog.
A narrow policy can stop most or all ingestion for an environment. Use Test policy to see the real effect on the last 24 hours before you save.
1. Minimum severity
Alerts below this level are never retrieved. The presets are defined once and mapped to each Wazuh version, so the same policy means the same thing on a 4.x and a 5.x environment. The editor shows the effective clause for both.
| Preset | Wazuh 4.x | Wazuh 5.x |
|---|---|---|
| Low+ | rule.level >= 1 (all alerts) | low, medium, high, critical |
| Medium+ | rule.level >= 7 | medium, high, critical |
| High+ (recommended) | rule.level >= 12 | high, critical |
| Critical | rule.level >= 15 | critical |
| Custom level | rule.level >= n, any value 1 to 15 | not available |
Wazuh 5.x reports severity as a keyword rather than a number, so Custom level is refused on a 5.x environment. Pick the nearest preset instead.
2. Manager alerts
Ignore manager self-alerts drops every alert whose agent name starts with
wazuh-manager-. Manager housekeeping rarely needs AI triage, so this is on by
default and recommended. Turn it off only if you want the manager's own
alerts triaged; they then pass through the same floor and rules as any other
agent.
3. Include rules: keep alerts matching any of these
Include rules are optional. With none, every alert at or above the floor is a candidate. Add them to narrow ingestion to what you care about.
- Rules are ORed: an alert is kept when it matches any rule.
- Conditions inside a rule are ANDed: every condition must match.
- A rule can be switched off with its toggle and kept for later. A disabled rule is stored but not applied.
- Name a rule so the history and the Check an alert tool can refer to it.
Limits: 20 rules across include and exclude combined, 6 conditions per rule, and 6 custom-field conditions per policy.
Fields and operators
Each condition is a field, an operator and a value. The operators offered depend on the field, and both are checked against the field catalogue for your Wazuh version before anything is saved.
| Field | Operators | Notes |
|---|---|---|
| Rule ID | is, is one of, is not, is not one of | Numbers such as 5710 |
| Rule groups | contains any, contains all, contains none | Case-sensitive, as Wazuh emits them |
| Description | contains, not contains, starts with, is | Free text |
| Agent ID | is, is one of, is not, is not one of | |
| Agent name | is, is one of, is not, is not one of, starts with, ends with, contains | |
| Agent groups | contains any, contains all, contains none | |
| MITRE tactic | contains any, contains all, contains none | Names such as Execution |
| MITRE technique | contains any, contains all, contains none | IDs such as T1059 |
| Source IP / Destination IP | is, is one of, in CIDR, not in CIDR | CIDR operators need Wazuh 5.x |
| Source port / Destination port | eq, gt, gte, lt, lte, between, is one of | Numeric range needs Wazuh 5.x |
| Protocol | is, is one of, is not | |
| Decoder name | is, is one of, is not, is not one of | |
| Field exists | exists, not exists | Names another catalogue field |
Fields that are lists in the alert, such as rule groups and MITRE tactics, use the contains operators; single-valued fields use is. An operator that does not fit the field is not offered.
Seen here. Click into a value box to see the values this environment's recent alerts actually carry, with counts, and pick from them instead of typing. This reads Mobius's own stored alerts for the last 30 days, so a brand-new environment shows nothing yet.
Custom field
For a field not in the catalogue, choose Custom field and give the literal
dotted path from the original Wazuh alert, for example
data.win.eventdata.image, plus a type hint. The hint picks the operators:
| Type | Operators |
|---|---|
| keyword | is, is one of, is not, starts with, ends with, contains |
| text | contains, starts with |
| number | eq, gt, gte, lt, lte, between |
| ip | is, is one of, in CIDR, not in CIDR |
| array | contains any, contains all, contains none |
Exists and not exists are always offered, because the hint is a guess.
A custom path is accepted by Validate but flagged unverified: Mobius
cannot check it against your index mapping. Run Test policy before relying
on one. Paths starting with _source, _index, _id, _score or script
are refused.
4. Exclude rules: then drop alerts matching any of these
Exclude rules use the same fields and operators as include rules and are the right place for known noise: a chatty rule ID, a lab agent, a scanner you run on purpose. An alert matching any exclude rule is dropped, even if it matched an include rule or the advanced query.
Excluded alerts never reach Mobius. For noise you still want recorded but not analysed, use a known behavior rule instead; that keeps the alert and skips the model.
Advanced query mode
For patterns the visual rules cannot express, switch on Advanced query
mode and write the query yourself as an OpenSearch bool clause in JSON.
- Advanced mode replaces the include rules only. The severity floor, the manager filter and your exclude rules still apply on top.
- Turning it on clears the include rules; turning it off restores the visual builder.
What the query may contain
Only this subset of OpenSearch is accepted, and it is checked before saving:
| Allowed | Detail |
|---|---|
bool keys | filter, must, should, must_not, minimum_should_match |
| Leaf queries | term, terms, range, prefix, wildcard, match_phrase, match_phrase_prefix, exists |
range keys | gt, gte, lt, lte |
wildcard | must not start with * or ?, at most 2 wildcard characters |
| Fields | must be catalogue fields for this environment's Wazuh version, by their real index path |
| Size | at most 40 leaf conditions, 6 levels of nesting, 4 wildcards per policy, 8 KB |
Anything else, including query_string, script, regexp, nested,
aggregations, or a field outside the catalogue, is rejected with the reason and
the JSON path of the offending clause.
Field paths by Wazuh version
The query uses real index paths, and they differ between versions. A query written for 4.x is rejected on a 5.x environment, and the other way round.
| Field | Wazuh 4.x | Wazuh 5.x |
|---|---|---|
| Rule ID | rule.id | wazuh.rule.id |
| Rule groups | rule.groups | wazuh.rule.tags or wazuh.integration.category |
| Description | rule.description | wazuh.rule.title |
| Agent ID | agent.id | wazuh.agent.id |
| Agent name | agent.name | wazuh.agent.name |
| Agent groups | agent.groups | wazuh.agent.groups |
| MITRE tactic | rule.mitre.tactic | wazuh.rule.mitre.tactic.name |
| MITRE technique | rule.mitre.id | wazuh.rule.mitre.technique.id |
| Source IP / port | data.srcip, data.srcport | source.ip, source.port |
| Destination IP / port | data.dstip, data.dstport | destination.ip, destination.port |
| Protocol | data.protocol | network.protocol |
| Decoder | decoder.name | wazuh.integration.name |
Severity is not a query field. rule.level and wazuh.rule.level are
rejected in the advanced query because the Minimum severity setting owns
the floor and is applied on top of your query.
Writing the query
The shape is one bool. Put what to keep in filter (all must match) or
in should with "minimum_should_match": 1 (any may match). Put what to
drop in must_not. This 4.x example keeps security-relevant alerts and
drops known noise:
{
"bool": {
"filter": [
{
"bool": {
"minimum_should_match": 1,
"should": [
{ "terms": { "rule.groups": ["authentication", "attack", "intrusion_detection", "sshd"] } },
{ "terms": { "rule.mitre.tactic": ["Initial Access", "Execution", "Credential Access", "Lateral Movement"] } },
{ "prefix": { "rule.mitre.id": "T1" } }
]
}
}
],
"must_not": [
{ "terms": { "rule.groups": ["vulnerability-detector"] } },
{ "prefix": { "agent.name": "test-" } }
]
}
}
The same policy on a 5.x environment uses the 5.x paths:
{
"bool": {
"filter": [
{
"bool": {
"minimum_should_match": 1,
"should": [
{ "terms": { "wazuh.rule.tags": ["authentication", "attack", "intrusion_detection", "sshd"] } },
{ "terms": { "wazuh.rule.mitre.tactic.name": ["Initial Access", "Execution", "Credential Access", "Lateral Movement"] } },
{ "prefix": { "wazuh.rule.mitre.technique.id": "T1" } }
]
}
}
],
"must_not": [
{ "terms": { "wazuh.rule.tags": ["vulnerability-detector"] } },
{ "prefix": { "wazuh.agent.name": "test-" } }
]
}
}
Exclusions written inside the query as must_not and exclusions added as
visual exclude rules both become must_not in the final query. Use whichever
reads more clearly; you can mix them.
Validate, then test
Validate checks the query against the rules above and against the field catalogue for your Wazuh version, and reports Query is valid with the clause count, or Query rejected with the reason and path. It runs automatically as you type and on demand with the button.
Validate is a static check: it proves the query is well-formed and
permitted. It cannot know your index mapping. A query can pass Validate and
still fail on the indexer, most often when a text-only operator such as
match_phrase_prefix is used on a field your mapping stores as keyword.
That is what Test policy is for: it runs the real query against your
environment and surfaces the indexer's own error if there is one.
View effective query shows the complete _search body Mobius will send
every poll, with the floor, the manager filter, your query and your exclude
rules combined, exactly as it is stored.
Test before you save
Test policy runs the candidate policy read-only against the environment's alerts from the last 24 hours (1 hour and 7 days are also available) and reports how many alerts would be sent to Mobius, what gets filtered out at each stage and why, and the estimated change in AI token use. Nothing is saved and nothing is ingested. The environment's connector must be online; if it is not, the test reports connector offline instead of a count.
One test runs at a time per organization. A test takes a few seconds to about a minute.
Check an alert answers "would this specific alert be ingested, and why?" for a candidate policy without touching the indexer. It walks the same order as the pipeline and names the rule that decided: Below the minimum severity, Manager self-alert (excluded), Matched exclude rule X, Matched include rule Y, or Matched no include rule.
The footer shows Not tested yet until you run a test on the current draft. You can still save without testing.
Save, versions and history
Every save creates an immutable version and makes it active immediately. Changes apply to alerts that arrive from then on. History lists every version with who saved it and a summary of what changed, and Restore creates a new version with an older version's content, so the trail is never rewritten. If someone else saved while you were editing, your save is refused with the current version number rather than silently overwriting theirs.
Rate limit and backpressure
Rate limit is the maximum number of alerts per second Mobius triages for the environment. It defaults to 5 per second, about 300 alerts per one-minute poll, and only billing admins can change it. Above the cap, alerts are deferred to the next poll, in order, and never dropped. If a storm keeps the backlog above six hours, the oldest part is shed deliberately and recorded as a gap on the environment page, so a storm can never build an unbounded queue. An Ingestion backlog building notification fires when a backlog forms and Ingestion backlog cleared when it drains; see notifications.
When a policy cannot run
If your indexer rejects the saved policy, for example after a Wazuh upgrade changes a mapping, Mobius does not stop ingesting. It falls back to the last version that worked, or, if there is none, to a conservative Critical-only policy with manager alerts ignored, and notifies you: Ingestion policy rejected by Wazuh, then Ingestion running a fallback policy, and Ingestion policy recovered once a save works again. The environment card's runtime banner shows which version is actually running and why.