WAKE

WAKE enriches, matches, and scores entities (IPs, users, hostnames, sessions) inside the rsyslog pipeline, then propagates verdicts across a tenant-scoped mesh of edges. The hot path is C, and so is gossip (immesh, inside rsyslog); analytics are Python. It is meant for collectors that cannot ship every line, and for sites where a central correlator is not available.

The C hot path

On each source ruleset the line is parsed, then enriched, then scored, before it is written. mmsigma runs once after that, in the shared logstore ruleset. None of these actions blocks the rsyslog queue.

mmenrich - CTI enrichment (C)
IOC lookup against a compiled .thrt database (IPs, domains, hashes, emails) plus MaxMind GeoIP City and ASN. The lookup path is bloom filter, then an accel table and binary search, with the CRC32C instruction from SSE 4.2 and ARMv8-CRC used for hashing; the vector code paths belong to the offline IOC miner rather than the lookup. Writes $!threat, $!labels and $!tags, with the emit target configurable. Pure enrichment: no state, no scoring.
mmwake - entity scoring & pheromone deposit (C)
Per-entity behavioral counters with thresholds fed from a .score_baseline sidecar, consuming $!threat and $!labels and folding a composite score into a shared pheromone mmap. Requires the SIMD TurboVM parser. immesh is not the next stage of this line: it is the C input module, in the same rsyslog process, that gossips and runs quorum on that table.
mmsigma - Sigma detection & correlation (C)
Multi-pattern Sigma rule matching with a Teddy/SIMD matcher, carrying each rule’s MITRE ATT&CK technique into the verdict. The same action runs the correlation engine (event_count, value_count, temporal, sum/avg/percentile, beaconing, temporal_ordered). Commutative partials hash onto one owner in a configured ring as $!sigma_contribution; the module does not open sockets. Percentile, median, ordered and beaconing types stay local. This plane does not merge with the pheromone table. See the mmsigma mesh pattern.

Mesh, engine, and console

libwake and immesh - pheromone gossip (C)
Edges broadcast signals about scored entities to their tenant peers over SWIM and PlumTree, run by the immesh rsyslog module and enabled per node. Receivers add the confidence into a shared per-entity counter, and an alert requires at least two distinct sources, so local observations become fleet-wide awareness without a central correlator.
waked - behavioral engine (Python)
Sixteen behavioral detectors in three groups: twelve per-entity (profile, first-seen, temporal, velocity, lateral-movement, data-staging, geo-travel, DNS and ICMP tunneling, cross-category, credential-pivot, slow-scan), three correlating over the signals those produce (role-incongruence, attack-chain, cross-window sequence), and one keyed on destinations rather than sources. Most carry a MITRE ATT&CK mapping; a few emit without one. Beaconing sits outside this layer: the C mmsigma correlation detects it at line rate and projects it into a beaconing signal. See behavioral detectors for the full list, thresholds and gaps. An EMA volume model over hour-of-week slots (which also feeds mmwake's baseline sidecar), a LogStore (DuckDB) for hot data, and an EntityStore (SQLite) for metadata. Detector thresholds themselves are configured constants with per-profile overrides, not EMA-derived. Exposes /ocsf for OCSF 1.8.0 projection (Detection 2004, Network 4001, Auth 3002, HTTP 4002). Also profiles its own Sigma layer on a timer and reports noisy and silent rules: see detection health.
wakeweb - SOC console (Python / NiceGUI)
Delegates authentication to fleet over the local fleetd broker socket, so operator accounts are managed in one place. Authorization is tenant scoping rather than a read/write split: a viewer role exists but is not yet enforced, so any authenticated session in scope can mutate. See the console.
Entity profile198.51.100.1 · tenant: north
TCP baseline UDP baseline ICMP deviation
Weekly traffic baseline00h - 24h
Mon
Tue
Wed
Thu
Fri
Sat
Sun
low high  ·  deviation
Per-protocol EMA classification with a weekly traffic baseline. The amber cell marks a deviation from the rolling profile. Illustrative - representative data, RFC 5737 documentation IPs.

Pipeline

  log sources    syslog, journald, file tail, MQTT, ...
       |
       v
  [ rsyslog ]                     input modules accept raw events
       |
       +-- mmnormalize + TurboVM   structured field extraction (SIMD)
       +-- mmenrich                CTI / IOC / GeoIP  -> $!threat, $!labels
       +-- mmwake                  entity scoring + pheromone deposit
       +-- immesh                  C input, same process: gossip + quorum
       |                             on the mmwake table, not on the log line
       +-- mmsigma                 Sigma match + correlation -> $!sigma
       |                             hashed contribs -> $!sigma_contribution
       |                             other plane: do not merge with pheromone
       |
       v
  [ enriched.jsonl ]    LogStore ingest path
       |
       v
  [ waked ]             16 behavioral detectors, Python
       +-- /ocsf endpoint         OCSF 1.8.0 projection to downstream SIEM
       +-- wakeweb                operator console

Two meshes, one engine

A central correlator assumes a reliable path back to one box. That does not hold for offshore racing, DMZ collectors, or air-gapped floors. WAKE uses two planes that must not be collapsed into one gossip topic.

two planes one destination vs every peer
Sigma reduce compared with pheromone gossip On the left, the Sigma reduce plane: three peers send their commutative partials to exactly one owner chosen by hashing the correlation identifier and group onto a configured ring. On the right, the pheromone plane: every peer broadcasts signals about scored entities to every other peer, accumulating into a shared counter. plane 01 / sigma reduce Exactly one owner. Commutative partials merge on a single owner. 1 emit 2 emit 3 emit 0 edge-00 OWNER owner = djb2(corr_id TAB group) % peercount One configured ring per site. Carried by ordinary rsyslog omfwd; the module opens no socket. plane 02 / pheromone Everyone tells everyone. Signals about scored entities, tenant-wide. 1 2 3 4 accumulated, then decayed on a timer Liveness and entity scoring. Never Sigma partials: collapsing the two planes would double-count fires.
Same engine, two shapes. Sigma reduce is modulo hashing onto one owner in an operator-configured ring, so a partial is applied exactly once; changing the ring size reshuffles every group. Pheromone is gossip to every tenant peer, where the same signal arriving from several directions is the point.
Sigma reduce (hashed owner)
Commutative correlation windows (event_count, value_count, temporal, sum/avg) merge among the peers named in the overlay's ring. Owner = djb2(corr_id TAB group) % peercount. Non-owners emit $!sigma_contribution JSON; rsyslog omfwd moves it. temporal_ordered and raw beaconing stay on the node that saw the events. mmsigma never opens a socket. See the vector diagram.
Pheromone (SWIM / PlumTree)
Each edge broadcasts signals about scored entities to tenant peers. Receivers add the confidence into a shared counter, which decays globally on a timer rather than being weighted per source; over a quorum the entity’s composite score crosses a detection threshold without any single edge having seen the whole picture. Injected wake_signal JSON has no TurboVM arena, so that ruleset skips mmsigma. This plane is liveness and scoring gossip, not Sigma partials.
Threat activitycomposite score · across the mesh
EntityScoreHitsSources
203.0.113.66
8.7
1425
198.51.100.23
6.4
883
user:svc-backup
4.9
512
192.0.2.140
3.2
272
host:edge-04
1.8
121
Entities ranked by composite score across the mesh; more edges reporting the same activity raises confidence. HITS = observations, SOURCES = reporting edges. Illustrative - representative data, RFC 5737 documentation IPs.

Campaign, then TAC shadow

The campaign matcher that runs is attack-chain: fixed patterns over signals already emitted, on one entity. Sequence adds order, absence, and a handoff to another entity. Neither is a learned graph, and neither is the pheromone plane.

TAC (Transient Associative Coupling) is a local memory of which techniques co-fire on the same entity. The table decays. It is off by default. Nothing in the verdict, the alert, or the score reads it, and a coupling is not a reason to isolate a host. It sits beside attack-chain. It does not replace it.

Integration

WAKE doesn’t talk to fleetserver directly. It uses the local fleetd as a broker (Unix socket /var/run/fleet/app.sock, peer-UID gated). That removes per-app service tokens, makes auth delegation consistent, and survives fleetserver being unreachable.

Edge-portable
The WAKE collector runs on FreeBSD (jails), Linux (host), and macOS (dev). Only the platform integration differs; detectors are the same.
OCSF projection
Declarative mapping from internal signal schema to OCSF 1.8.0. GET /ocsf on waked exposes the projection for downstream SIEMs.
Storage
LogStore is DuckDB over per-day Parquet parts, with retention as removal of expired parts and periodic consolidation of the small ones. EntityStore is SQLite.
Parsing
The fast path is TurboVM, a 64-byte bytecode VM with SSE 4.2 / NEON code paths integrated into liblognorm.