Passive events
Active polling asks a device how it is doing. Passive events are the opposite direction: the device speaks first — a syslog line, an SNMP trap, a webhook from another system — and Yagra listens, correlates the message to the node that sent it, and decides whether it warrants an alert.
Overview
Section titled “Overview”Three intake paths feed one event pipeline:
| Path | How it arrives | Received by |
|---|---|---|
| syslog | UDP datagrams (host port 514 by default) |
a poller |
| SNMP traps | UDP datagrams (host port 162 by default) — v1/v2c traps and informs |
a poller |
| Webhooks | POST /api/v1/ingest/webhook/<source-id> with a per-source bearer token |
core, on the API port |
Pollers receive syslog and traps close to the devices and forward them to core over the bus, so passive intake works in the same distributed topology as polling — a branch-site poller receives the branch’s events locally and they ride the poller’s existing bus connection home. Webhooks skip the poller entirely and land on the core API.
Core matches every received event against your event rules (below). A matching event gets a severity and can raise an alert; a non-matching event is still stored and browsable, so you can see what your fleet is saying before you have written a rule for it. Alerts raised from events join the same pipeline as threshold alerts — the same notification channels, including PagerDuty (Events API v2) and Jira Service Management (Alerts API) with a native fire/resolve lifecycle.
Enabling the listeners
Section titled “Enabling the listeners”The syslog and trap listeners are opt-in: each exists only while its bind variable is set. The compose files enable both by default.
| Listener | Enable with | Container bind | Host port (compose) |
|---|---|---|---|
| syslog | YAGRA_SYSLOG_BIND (e.g. 0.0.0.0:1514) |
1514/udp |
514 (YAGRA_SYSLOG_PORT) |
| SNMP traps | YAGRA_TRAP_BIND (e.g. 0.0.0.0:1162) |
1162/udp |
162 (YAGRA_TRAP_PORT) |
# .env — these are the compose defaults; set a bind empty to disable that listenerYAGRA_SYSLOG_BIND=0.0.0.0:1514YAGRA_TRAP_BIND=0.0.0.0:1162YAGRA_SYSLOG_PORT=514 # host port devices send toYAGRA_TRAP_PORT=162To disable one under compose, set its bind variable to an empty string in .env; outside
compose, leaving the variable unset means the listener never starts.
Two deployment details matter more than anything else on this page:
- Event→node correlation uses the datagram’s source IP. An event is attributed to the node
whose address sent it. If Docker’s bridge networking rewrites source addresses on your host,
run the poller with
network_mode: hostso the device’s real address survives — the remote-site poller composition already does. Anything else that rewrites the source address (NAT between device and poller) breaks attribution too, which is one more reason to place pollers close to their devices. If the host already runs a syslog daemon on port514, remap the published port. - The poller cannot bind privileged ports. It runs as a non-root container, so it binds the
unprivileged ports
1514and1162; compose maps the standard low ports on the host for it. With host networking or a native install there is no port mapping — either point devices at the high ports directly or redirect514/162to them in the firewall. The exactiptablesREDIRECT rules are in Ports & firewall.
Syslog
Section titled “Syslog”The syslog listener accepts both RFC 5424 (modern, structured) and RFC 3164 (legacy BSD) messages, so routers, switches, firewalls, and Unix hosts can all point their existing syslog configuration at it unchanged. Parsing extracts the facility, severity, hostname, application name, and message text — the fields event rules match on, and the same fields forwarding filters can select on later.
There is nothing to configure per device on the Yagra side: any device whose source address matches a monitored node is correlated automatically, and messages from addresses Yagra does not monitor are still received and browsable.
To confirm the listener is reachable, send a test line from any Linux host and look for it under Alerts ▸ Events:
logger --server <yagra-host> --port 514 --udp "test: hello from yagra docs"SNMP traps
Section titled “SNMP traps”The trap listener speaks SNMP v1 and v2c, and handles both notification forms:
- Traps — fire-and-forget notifications, received and parsed.
- Informs — confirmed notifications: Yagra sends the acknowledgement the sender is waiting for, so devices configured for informs do not sit in retransmit loops.
Incoming traps resolve their trap OID to a human-readable name, so a trap arrives as a named event rather than a raw OID, with a trap badge in the event log. A set of built-in trap event rules ships with Yagra, so common traps are classified out of the box.
An optional community filter drops traps whose community string does not match:
YAGRA_TRAP_COMMUNITY=mycommunity # unset = accept all communitiesThe configured value is never logged. SNMPv3 traps are not supported yet — the trap listener is v1/v2c only.
To confirm trap reception end to end, send a standard linkDown trap from any host with
Net-SNMP installed and watch it arrive as a named event:
snmptrap -v 2c -c public <yagra-host>:162 '' 1.3.6.1.6.3.1.1.5.3 1.3.6.1.2.1.2.2.1.1 i 2Webhook sources
Section titled “Webhook sources”The third intake path lets other systems push events over HTTPS — a backup job, a CI pipeline, a cloud service’s alerting hook. Create webhook sources under Alerts ▸ Event sources: each source names the node its events belong to and gets its own bearer token.
Senders then post to the source’s endpoint on the core API port:
curl -X POST "http://<yagra-host>:8080/api/v1/ingest/webhook/<source-id>" \ -H "Authorization: Bearer <source-token>" \ -H "Content-Type: application/json" \ -d '{"message": "nightly backup failed on db-01"}'The payload is deliberately permissive: if the body is a JSON object, its message, text, or
summary field becomes the event text; any other body is taken as-is. The response codes are
deliberately distinct, so an automated sender can tell what went wrong: 202 accepted (with the
event id), 401 for a wrong token, 404 for an unknown or disabled source, 413 for an
oversized body, and 429 when the source is over its rate limit. A 503 means event ingestion
is not available on this core — either it is not configured, or in a high-availability pair the
request landed on the standby.
Event rules
Section titled “Event rules”Rules live under Alerts ▸ Event rules and turn raw events into classified, alertable signals. A rule is built from:
- A match — substring or regex against the event text, optionally scoped to a single stream (syslog, traps, or webhooks) so a pattern written for one source cannot fire on another.
- A severity — the matched event is classified, which drives the event log, the dashboard widgets, and any alert the rule raises.
- An alert with auto-clear — a rule can raise a real alert on the node the event came from. Event alerts carry a TTL and clear automatically when it lapses; the TTL is how an event alert closes.
- A clear pattern — an optional second pattern that clears the alert early, so a link up message closes the alert its link down counterpart raised, without waiting out the TTL.
- A fire threshold — “N events in M seconds”, so a pattern that is noise in isolation (an occasional authentication failure) alerts only when it storms.
Two behaviors worth knowing:
- Event-raised alerts skip dependency suppression on purpose — a device that just emitted an event is demonstrably reachable, so there is no upstream outage to roll it into.
- A rule scoped to a stream kind this core does not recognize (possible mid-upgrade, when a newer WebUI writes a rule an older core has not learned) is left out of the matching engine and logged — it will not silently widen to every stream.
Rate limiting
Section titled “Rate limiting”Event floods are bounded at the edge, on the poller, before anything reaches the bus or the database. Syslog and trap intake share a token-bucket limiter keyed by source IP:
| Limit | Default | Variable |
|---|---|---|
| Per source IP | 200 events/s |
YAGRA_EVENT_RATE_PER_SOURCE |
| Global (all sources) | 5000 events/s |
YAGRA_EVENT_RATE_GLOBAL |
Both allow bursts of up to 2× the sustained rate, so a device that batches a few seconds of messages is not clipped, while a genuinely looping device is. A single misbehaving source hits its own 200/s ceiling long before it can crowd out the rest of the fleet’s 5000/s budget.
Webhook sources are rate-limited separately, per source, at the API — an over-rate sender gets
429 and can back off. Flow datagrams have their own, separate budget (see
Traffic flow).
Both event limits are tunable; see the configuration reference.
Browsing and searching events
Section titled “Browsing and searching events”Alerts ▸ Events shows everything received — matched or not. Filter by stream kind (syslog / trap / webhook), by matched vs. unmatched, by node, and by time range, with a text search that has a regex toggle. Unmatched events are worth a periodic look: they are the messages your fleet sends that no rule classifies yet.
The same event stream also appears where you triage:
- Every node’s detail page has an Events tab scoped to that node.
- The dashboard has a passive-events widget section — event feed, volume, kind mix, top traps, triage, noisy sources, and rule coverage.
By default events are stored and searched in PostgreSQL, which is fine for moderate volumes
and needs nothing extra. For serious event volumes, point YAGRA_LOGS_URL at a VictoriaLogs
instance and event storage and search move onto a dedicated log store built for full-text
queries over large histories. The single-node compose stack already ships a VictoriaLogs
service with the URL set, so it is the default experience there; on a hand-rolled deployment it
is opt-in. The UI and API are the same either way.
AI clients see the same stream: the MCP tools search_events and event_stats expose event
search and triage statistics (noisiest nodes, severity mix, unmatched signatures) to an
MCP-capable assistant.
Search, sort, and paging are all based on when an event happened, not when Yagra ingested it. One consequence is inherent to time-ordered logs: when a remote poller reconnects after an outage and replays what it buffered (store-and-forward), those older events insert at their original position — which can be behind a page you have already scrolled past.
Forwarding events onward
Section titled “Forwarding events onward”Everything the listeners receive can also be relayed to a SIEM or another collector — byte-for-byte where the original datagram exists — or streamed into BigQuery as normalized rows for long-term querying. Forwarding is a tee, not a diversion: Yagra keeps matching, alerting, and storing regardless of where else the data goes. See Forwarding.
See also
Section titled “See also”- Traffic flow — the other passive intake: NetFlow, IPFIX, and sFlow collection.
- Ports & firewall — listener ports, the REDIRECT rules for unprivileged binds, and what must stay internal.
- Configuration reference — every
YAGRA_*variable named above.