Field names are the ones the feature engine emits. Gates are the thresholds each detector compares against. Live state comes from the detector registry; a detector with no reading is named, not assumed healthy.
c2_beacon
C2 beaconingStatisticalUnavailableFinds a command-and-control callback: one source talking to one destination on a regular interval, with a stable payload, to somewhere it has never been before.
Requires: Regular timing on its own reports nothing, because NTP, software updaters and monitoring agents are all near-perfect beacons. At least two independent corroborating signals must hold before this detector emits anything.
c2_beacon.periodicity_score>= 0.7Regularity of the inter-arrival series, scaled by how many intervals were actually observed, so three lucky connections cannot score high.
c2_beacon.interarrival_cv<= 0.25Coefficient of variation of the gaps between check-ins. A beacon is tight; a human is not.
c2_beacon.interarrival_meanThe average gap. Check-ins outside a plausible range are not treated as beacons at all.
c2_beacon.interarrival_stdev<= 30 sAbsolute jitter. Regular in relative terms but hours of absolute jitter is not a beacon.
c2_beacon.autocorrelation_periodicityAlternative path for a beacon whose intervals follow a repeating pattern and therefore scores poorly on plain variability.
c2_beacon.spectral_peak_ratio>= 0.45 over >= 12 intervalsThe same alternative, measured as energy concentration in the spectrum. Needs a long enough series to mean anything.
c2_beacon.byte_stability_cv<= 0.3Payload size stability across check-ins: fixed-size check-ins are the signature.
c2_beacon.destination_rarity>= 0.5 to corroborateHow unusual this destination is for this source.
c2_beacon.destination_port_rarity>= 0.5 to corroborateHow unusual this destination port is for this source.
c2_beacon.is_internal_destinationA beacon leaves the estate. An internal destination is not a callback.
c2_beacon.corroborating_signalsCount of the independent signals that held. Below two, nothing is reported.
c2_ml
C2 beaconingSupervisedUnavailableA classifier over the same pair-window features, for beacons whose timing is regular but not textbook. It runs alongside c2_beacon rather than replacing it.
Requires: A champion model is registered and its feature contract matches. Probability must clear the model's own decision threshold, which is recorded with the model, not hard-coded here.
c2_ml.c2_probability>= the model's decision_thresholdCalibrated probability that this pair is a beacon.
c2_ml.interarrival_countHow many check-ins the classifier was given. Too few and it abstains.
dga
Domain generationSupervisedUnavailableRecognises algorithmically generated domains: malware walking a list it generates, so the names look random but are not random to the model.
Requires: A champion DGA model must be registered. Below the calibrated probability threshold the query is treated as ordinary name resolution.
dga.dga_probability>= 0.85 unless the champion records its own thresholdModel probability that the queried domain is generated.
dga.dns_char_entropyShannon entropy of the domain. Generated names sit high; dictionary names sit low.
dga.dns_payload_length>= 8Length of the queried name. Very short names are not worth a verdict.
dga.dns_label_countNumber of labels in the queried name.
dga.dns_nxdomain_ratio_60sShare of this host's lookups that returned NXDOMAIN. A walking list mostly misses.
dns_tunnel
DNS tunnellingHybridUnavailableFinds data being carried out inside DNS queries: long, unique, high-entropy subdomains under one zone, which is what an exfiltration channel over port 53 looks like from the outside.
Requires: Lexical and behavioural evidence together. A single long name is not a tunnel; a tunnel is a pattern across many of them.
dns_tunnel.dns_domain_length>= 60Length of the full queried name. Tunnels pad the subdomain to carry payload.
dns_tunnel.dns_char_entropy>= 3.6 bits/charEntropy over the subdomain characters. Encoded payload is high entropy; a real zone name is not.
dns_tunnel.dns_unique_name_ratio_60s>= 0.8Share of this host's queries that are unique names. A tunnel almost never reuses a name.
dns_tunnel.dns_query_count_60s>= 20Query rate from this host. A tunnel sustains volume.
dns_tunnel.dns_txt_ratio_60s>= 0.3Share of queries that are TXT, the record type most used to smuggle a response back.
dns_tunnel.dns_hex_ratioShare of the subdomain that is hex digits, the cheapest encoding to use.
dns_tunnel.dns_subdomain_depthLabel depth below the registrable domain. Tunnels nest to create space.
tls_malware
TLS malwareRuleUnavailableIdentifies malware by the shape of its TLS handshake rather than its contents. The payload is encrypted, but the fingerprint of the client stack is not.
Requires: A curated fingerprint list, or a champion model over TLS metadata. Confidence is capped so a single one-sided sighting never reads as certainty.
tls_malware.ja4_list_countlistings above 3 are not rare on their ownHow many independent rows in the curated list carry this JA4. A repeat is stronger evidence than a one-off.
tls_malware.tls_malware_confidencecapped at 0.9Evidence-based confidence: 0.6 for a match, +0.2 when both handshake sides agree, +0.15 when the fingerprint repeats.
tls_malware.tls_malware_probabilityModel probability from TLS metadata, when a champion model is registered.
tls_malware.tls_sni_presentWhether a server name was offered. Malware frequently omits it.
tls_malware.fingerprint_samplesTotal sightings across both sides of the handshake, capped for the evidence count.
exfiltration
Data exfiltrationStatisticalUnavailableFinds sustained outbound bulk transfer to a destination the host does not normally use. Judged over the widest window, because transfers take time.
Requires: Volume alone never fires. The transfer must be outbound from inside the estate, and at least one non-corroborating signal must hold: a rare destination, a baseline deviation, or a flow-size deviation.
exfiltration.bytes_out_total_300s>= 10 MBTotal bytes sent by this host over five minutes.
exfiltration.orig_resp_byte_ratio_300s>= 20How lopsided the transfer is. Only measurable when the sensor sees both directions.
exfiltration.bytes_out_per_sec_60s_zscore>= 4.0Outbound rate against this host's own history.
exfiltration.destination_rarity>= 0.9How new this destination is for this host.
exfiltration.destination_port_rarity>= 0.9How new this destination port is for this host.
exfiltration.outbound_flow_bytes_zscore>= 4.0Per-flow size against this host's flow-size baseline.
exfiltration.bytes_out_per_sec_60s_baseline_warmWhether enough history exists to compare against. A cold baseline is reported as cold, never as clean.
exfiltration.mean_duration_300s>= 60 sMean connection duration. A sustained transfer runs long.
recon
ReconnaissanceRuleUnavailableSeparates a scanner from a busy legitimate client. Fan-out alone describes a proxy, an updater or a backup agent, so a second, independent gate must also hold.
Requires: Behind a one-way tap the response side is never observed, so failures cannot be measured. The detector then falls back to an origin-only discriminator: a scan opens roughly one flow per target, and each probe is small.
recon.unique_dst_ports_60s>= 20Distinct destination ports touched, which is what a vertical scan is.
recon.unique_dst_ips_60s>= 25Distinct destination hosts touched, which is what a horizontal scan is.
recon.flow_count_60s>= 20Flows from this host in the window. Too few and there is no pattern to read.
recon.failed_ratio_60s>= 0.8Share of connections that failed, when the sensor can observe the response side.
recon.syn_ratio_60sShare that were SYN without establishment. Same caveat as above.
recon.mean_duration_60s<= 1.0 sMean connection lifetime. A probe is short; a working session is not.
recon.dst_port_entropy_60sSpread of the destination port choices, which separates a sweep from a client reusing a few services.
ddos
DDoS floodHybridUnavailableFinds a flood toward one victim. The alert is attributed to the destination, because a flood has many sources and attributing it to one of them would scatter the incident.
Requires: Rate alone cannot tell a flood from a scan, so the traffic must be concentrated on few destination ports. A baseline comparison is reported as cold when there is no history, and a cold baseline is not evidence of an attack.
ddos.syn_rate_1s>= 500SYN flows per second toward the victim.
ddos.flows_per_sec_1s>= 1000Total flows per second toward the victim.
ddos.packets_per_sec_1s>= 20,000Packet rate toward the victim, which catches a flood made of small packets.
ddos.unique_dst_ports_1s<= 10How many ports the flood spreads across. A flood concentrates.
ddos.unique_src_ips_5s>= 50 to corroborateDistinct sources in five seconds.
ddos.src_ip_entropy_5s>= 4.0 bitsSpread of the source addresses. High entropy means distributed or spoofed.
ddos.amplification_ratio_5s>= 5.0Bytes received against bytes sent, for reflection attacks. Only measurable with both directions.
ddos.flows_per_sec_60s_zscore>= 10x the baseline meanCurrent rate against this destination's own history.
anomaly
Benign trafficStatisticalUnavailableFinds hosts whose overall behaviour is unlike themselves. It never raises a threat on its own: it supplies corroborating context to the detectors that do.
Requires: At least ten events for this host, and a champion model whose feature contract matches.
anomaly.anomaly_score>= the model's score_thresholdHow far this host's current behaviour sits from its trained profile.
anomaly.anomaly_thresholdThe threshold recorded with the champion model that produced the score.
anomaly.flows_per_sec_60sCurrent flow rate for this host.
anomaly.bytes_out_per_sec_60sCurrent outbound rate for this host.
anomaly.unique_dst_ports_60sCurrent fan-out for this host.
anomaly.unique_dst_ips_60sCurrent distinct-destination count for this host.
anomaly.dns_queries_per_sec_60sCurrent resolver load for this host.