Generated banner for Kibana Anomaly Detection
Generated logo for Kibana Anomaly Detection

Kibana Anomaly Detection

ElasticOfficialCreated by elastic

  • Skill
  • Tool integration
  • Data and AI
  • Apache-2.0
  • No issues found

From the author

Elastic ML anomaly detection — investigation/RCA, score explanation, job lifecycle troubleshooting, and job operations. Use when answering "what broke?"/"which entity?"/RCA, "why is score high/low?"/renormalization, "datafeed stopped"/"memory limit"/hard_limit, or configuring ML anomaly detection jobs. Reads results from `.ml-anomalies-*` and job state from ML REST APIs.
— the description by elastic

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured, expert-led workflow for managing and troubleshooting Elastic ML anomaly detection jobs. It covers root cause analysis (RCA) by attributing incidents to specific entities, explains model scoring behavior including renormalization, and provides precise lifecycle management for job configuration and troubleshooting, such as resolving hard memory limits.

What it does

  • Performs root cause analysis by identifying anomalous entities using influencer scores.
  • Explains anomaly scores by comparing initial and current scores to identify renormalization.
  • Diagnoses and resolves job lifecycle issues like hard memory limits and stopped datafeeds.
  • Provides a standardized sequence for updating job configurations and memory limits.
  • Facilitates cross-job correlation to identify systemic issues versus isolated incidents.
  • Validates findings by querying raw source evidence from datafeed indices.

When to use it

  • When investigating the root cause of an anomaly or identifying which entity is responsible.
  • When users need to understand why an anomaly score is high, low, or has changed over time.
  • When troubleshooting job failures, such as datafeeds stopping or memory limit errors.
  • When configuring new anomaly detection jobs or updating existing ones.
  • When performing cross-job analysis to determine the blast radius of an incident.

Advantages

  • Standardizes RCA by prioritizing influencer scores over aggregate bucket scores.
  • Prevents common misdiagnoses by correctly identifying renormalization versus model failure.
  • Provides a safe, step-by-step lifecycle protocol for updating job configurations.
  • Ensures troubleshooting is grounded in actual job stats like memory status.
  • Reduces false alarms by distinguishing between transient spikes and sustained behavioral shifts.

Usage examples

“Why did the anomaly score for my job drop from 90 to 50 overnight?”
The agent will compare the initial_record_score with the current record_score to determine if renormalization occurred, explaining that this is expected behavior when the model encounters more extreme anomalies later.
“My anomaly detection job stopped and shows a hard_limit error. How do I fix it?”
The agent will diagnose the memory_status, advise against simply restarting the datafeed, and provide the correct sequence to stop the job, increase the memory limit, and restart.
“Which entity caused the spike in checkout latency detected by the ML job?”
The agent will query the influencer results for the relevant time window and rank entities by influencer_score to identify the primary suspect.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/elastic/kibana-anomaly-detection/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip kibana-anomaly-detection.zip -d .agents/skills

Documentation by elastic

Elastic ML Anomaly Detection

Expert process for ML anomaly detection: attribute incidents to entities, explain scores and model behavior, diagnose job lifecycle failures, and manage jobs. Read anomaly results from POST /.ml-anomalies-*/_search (Serverless-safe) and job/datafeed state from ML REST APIs. When the user embeds fixture evidence (influencer rows, job stats) in the prompt, apply the judgment below directly — do not re-fetch fields already supplied.

<!-- begin-partial: preamble -->

Environment Configuration

This skill executes Elasticsearch operations through the elastic CLI. If the elastic CLI is not installed, tell the user what it is needed for. Do not guess credentials, call the HTTP API directly, or attempt other workarounds.

This skill references operations in HTTP-shorthand form (e.g., GET /, GET /_cat/indices, GET /{index}/_mapping, GET /{index}/_settings/index.mode, POST /_query). The Operations table at the end of this document maps each shorthand to the equivalent elastic CLI command — always use the CLI rather than calling the HTTP API directly.

<!-- end-partial: preamble -->

Mode selector

User intentMode
"What broke?" / RCA / cross-job / blast radius / influencers / log categoriesInvestigate
"Why score high/low?" / renormalization / model bounds / forecastsExplain
Missing docs / memory limit / datafeed stopped / lifecycle / calendarsTroubleshoot
Create a job / configure a datafeed / start analysis / retrieve resultsManage
Security framing (attack chains, MITRE, exfil)Investigate + references/security-anomaly-expert.md
Observability/SRE framing (degradation, capacity, deployment regression)Investigate + references/observability-anomaly-expert.md

When a question spans modes: Investigate → Explain → Troubleshoot. Finish one mode before blending logic.

Serverless note: Legacy /_ml/anomaly_detectors/{job_id}/results/* endpoints return HTTP 410 in Serverless. Always query .ml-anomalies-* via POST /.ml-anomalies-*/_search with result_type filters.

Score quick reference

  • record_score bands: >75 critical · 50–75 warning · 25–50 minor · <25 informational

  • multi_bucket_impact ≥ 3 → sustained shift (not a transient spike)

  • initial_record_score >> record_score → renormalization (model saw worse anomalies later)

  • actual << typical with count/low_count/low_mean → absence/outage, not just a low value

  • Low scores across many jobs > one high score — composite cross-job signal often beats single-detector severity

Full score definitions, renormalization mechanics, and anomaly_score_explanation components: references/score-reference.md.

Core concepts

Treat .ml-anomalies-* as layered result types via result_type in search queries:

result_typeScopeKey fields
bucketTime windowanomaly_score, initial_anomaly_score, timestamp
recordDetector rowrecord_score, initial_record_score, actual, typical, anomaly_score_explanation
influencerEntity × bucketinfluencer_field_name, influencer_field_value, influencer_score
model_plotBoundsmodel_lower, model_upper, actual
category_definitionLog patternscategory_id, terms, regex, examples

Read scores this way:

  • anomaly_score / record_score = current normalized values (move as the model sees new extremes).

  • initial_anomaly_score / initial_record_score = immutable snapshots from detection time.

  • influencer_score ranks entity responsibility within a bucket — the highest score is the primary suspect, not the bucket-level anomaly_score alone.

  • Map entities via partition_field_value / by_field_value / over_field_value.

  • Read multi_bucket_impact (-5 to +5) to separate single-bucket spikes from sustained trends.


Mode: Investigate — RCA

When: "what broke?", "which entity caused this?", cross-job correlation, blast radius, attack/cascade chains.

Process
  1. Discover jobs. Call GET /_ml/anomaly_detectors when the job ID is unknown. Call GET /_ml/anomaly_detectors/{job_id} and GET /_ml/datafeeds/datafeed-{job_id} to learn source indices, entity fields (by_field_name, over_field_name, partition_field_name), and bucket_span. The decision: identify the related job group — jobs sharing a datafeed index or entity field monitor the same system from different angles.

  2. Scope the incident window. Call POST /.ml-anomalies-*/_search with result_type: bucket, a time range, and optional minimum anomaly_score. The decision: fix the incident start/end and count how many jobs co-fire in that window. Low scores across many jobs simultaneously often indicate a systemic root cause.

  3. Attribute to entities (critical for RCA). For the anomalous bucket timestamp, call POST /.ml-anomalies-*/_search with result_type: influencer, the job ID(s), and the bucket time range. Sort by influencer_score descending. The decision: name the entity with the highest influencer_score as the likely cause — it ranks how unusual each entity is in that bucket. Do not restate only the bucket anomaly_score without attributing responsibility. Recommend drilling into that entity's records next.

  4. Cross-job confirmation. Re-query influencers (or bucket records) across related job IDs for the same entity values and time window. Entities anomalous in 2+ jobs are prime suspects (resource fault or systemic failure); single-job entities are often downstream victims. See references/protocols/investigation.md.

  5. Drill into records. Call POST /.ml-anomalies-*/_search with result_type: record, exact job ID, entity filters (partition_field_value, by_field_value), and low minimum record_score (25 or lower). Read multi_bucket_impact ≥ 3 as sustained behavioral shift. Read actual vs typical for fault class (spike vs absence/outage).

  6. Confirm with source evidence. Call POST /{index}/_search on the datafeed source index for the suspect entity and time window. Raw source documents are ground truth — never close an RCA without them.

  7. Synthesize. Report: root cause entity · affected jobs · temporal progression · fault class · severity · recommended actions. Worked walkthrough: references/worked-example.md. Query templates: references/investigation-queries.md.

Rules
  1. Rank by influencer_score, not anomaly_score, for "which entity?" — bucket score is aggregate; influencer score attributes cause.

  2. Multi-job entities are prime suspects; single-job entities are usually victims.

  3. Earliest anomaly timestamp wins — reconstruct chronology from record timestamps across jobs.

  4. multi_bucket_impact ≥ 3 = sustained behavioral shift, weight higher than transient spikes.

  5. Use low score thresholds (25 or lower) for influencer/record queries — high thresholds miss correlated entities.

  6. Never close an RCA without source evidence from the datafeed index.


Mode: Explain — Score / model behavior

When: "why is my score 30/90?", "score dropped overnight", "what is renormalization?", "why wasn't this detected?".

Process
  1. Decide fetch vs interpret. If the user supplies a record with record_score, initial_record_score, actual, and typical, interpret directly. Otherwise load config with GET /_ml/anomaly_detectors/{job_id} and records with POST /.ml-anomalies-*/_search (result_type: record).

  2. Always show both initial_record_score and record_score. The gap is the renormalization story. Large positive drift (initial_record_score >> record_score) means a later, more extreme anomaly rescale this record downward — expected healthy behavior, not a broken model.

  3. Classify the pattern before speculating.

    PatternInterpretation
    initial_record_score >> record_scoreRenormalization — explain before suggesting config changes
    actual << typical with low_count/count/low_meanAbsence/outage anomaly — investigate the outage, not score tuning
    high_variance_penalty: true in anomaly_score_explanationNoisy metric — wide bounds absorbed the spike
    incomplete_bucket_penalty: trueIngest lag or sparse bucket — score legitimately reduced

    Only cite anomaly_score_explanation factors present in the record.

  4. Quantify renormalization (optional). Re-query records sorted by timestamp; compute score_drift = initial_record_score - record_score and flag large drift.

  5. Add visual context when needed. If model_plot_config.enabled, query result_type: model_plot and compare actual to model_lower/model_upper. For categorization jobs, query result_type: category_definition.

  6. Check job health when scores look wrong persistently. Call GET /_ml/anomaly_detectors/{job_id}/_stats — model_size_stats.memory_status of hard_limit corrupts learning and can invalidate scores. Escalate to Troubleshoot mode.

anomaly_score_explanation components
ComponentEffectWhat it means
anomaly_length↑ scoreMore consecutive anomalous buckets
single_bucket_impact↑ scoreLower probability → higher impact
multi_bucket_impact↑ scoreSustained pattern contribution
anomaly_characteristics_impact↑ scoreMean shift vs. variance change
high_variance_penalty↓ scoreNoisy data → wide bounds → anomaly less surprising
incomplete_bucket_penalty↓ scoreBucket has less data than expected (ingest lag, sparse data)
Rules
  1. Explain renormalization before diagnosing config — score drift is the most common "score dropped" cause.

  2. actual << typical with count/low_count is an absence anomaly — distinguish outages from value spikes.

  3. Weekly seasonality needs ≥3 weeks of training data — flag young jobs as the cause.

  4. Detector function direction matters — see references/anomaly-detection-functions.md.


Mode: Troubleshoot — Job lifecycle

When: "missing documents", "datafeed stopped", hard_limit, "results look wrong", lifecycle changes.

Process
  1. Load job and datafeed state. Call GET /_ml/anomaly_detectors/{job_id}/_stats and GET /_ml/datafeeds/datafeed-{job_id}/_stats. Read state, data_counts, model_size_stats, and datafeed state. If the user embeds stats JSON, diagnose from memory_status and datafeed state directly.

  2. Diagnose memory status first (critical). Inspect model_size_stats:

    FieldMeaning
    memory_statusok / soft_limit (pruning) / hard_limit (critical)
    model_bytesCurrent memory used
    model_bytes_memory_limitConfigured model_memory_limit

    When memory_status is hard_limit and model_bytes equals model_bytes_memory_limit, the model hit its memory ceiling — it stops learning new entities and results degrade or stop. A stopped datafeed is often a symptom, not the root cause. Do not recommend only restarting the datafeed — that alone does not clear a hard limit.

  3. Remediate hard_limit. The fix is to raise model_memory_limit (via job update) and/or reduce model size by lowering cardinality (fewer partition/by/over field values, split into multiple jobs). Raising the limit requires the lifecycle sequence below (stop datafeed → close job → update → open → start). Optionally call POST /_ml/anomaly_detectors/_estimate_model_memory to size the new limit from source cardinality.

  4. Diagnose missing documents / query timing. After memory is healthy, inspect datafeed query_delay and delayed_data_check_config via GET /_ml/datafeeds/datafeed-{job_id}. Search .ml-annotations-* for delayed-data events. Set query_delay to P95 ingest latency + buffer (default 60s–120s).

  5. Read job messages. Search .ml-notifications-* for the job ID when errors are unclear.

  6. Recover corrupted model state. Call POST /_ml/anomaly_detectors/{job_id}/model_snapshots/{snapshot_id}/_revert to revert to a known-good snapshot when the model was corrupted during hard_limit.

Lifecycle for config changes (memory limit, query_delay)

Apply in order — skipping steps causes rejected updates:

  1. POST /_ml/datafeeds/datafeed-{job_id}/_stop

  2. POST /_ml/anomaly_detectors/{job_id}/_close

  3. POST /_ml/anomaly_detectors/{job_id}/_update (memory limit) and/or POST /_ml/datafeeds/datafeed-{job_id}/_update (query_delay)

  4. POST /_ml/anomaly_detectors/{job_id}/_open

  5. POST /_ml/datafeeds/datafeed-{job_id}/_start

Preview changes with POST /_ml/datafeeds/datafeed-{job_id}/_preview before restarting.

hard_limit corrupts model state and causes downstream missing-doc false alarms. Fix memory before fixing query_delay. Full troubleshooting detail: references/troubleshooting-reference.md.

Rules
  1. Ground lifecycle diagnosis in memory_status — not generic "restart it" advice.

  2. Fix memory before query_delay — hard_limit invalidates downstream diagnostics.

  3. Stop datafeed → close job → update → open → start for any memory or datafeed config change.

  4. Do not delete the job as first remediation for hard_limit — raise limit and/or reduce cardinality.


Mode: Manage — Create / configure jobs

When: "set up a job", "create an ML detector", "monitor X over time".

For the full create/open/start lifecycle, prefer the elasticsearch-anomaly-detection skill. This mode summarizes the sequence and detector selection:

  1. Verify target index. Call GET /{index}/_mapping — confirm time field and detector fields exist.

  2. Create job. Call PUT /_ml/anomaly_detectors/{job_id} with analysis_config (detectors, bucket_span, influencers) and data_description.time_field.

  3. Create datafeed. Call PUT /_ml/datafeeds/datafeed-{job_id} with indices, query, and query_delay.

  4. Open and start. Call POST /_ml/anomaly_detectors/{job_id}/_open, then POST /_ml/datafeeds/datafeed-{job_id}/_start.

  5. Confirm. Call GET /_ml/anomaly_detectors/{job_id}/_stats and GET /_ml/datafeeds/datafeed-{job_id}/_stats.

Choose detector functions from user intent — see references/anomaly-detection-functions.md. Worked JSON bodies: references/job-creation-recipes.md.

Rules
  1. Create job before datafeed. Open job before starting datafeed.

  2. query_delay = P95 ingest latency + buffer (60s–120s safe default).

  3. by_field_name vs over_field_name: by compares entity to its own history; over compares to peer group.

  4. Forecasts require non-population jobs — jobs with over_field_name cannot be forecasted.


Examples

RCA: "Something caused a spike in checkout latency — which entity?" → Query influencers for the bucket → web-07 has highest influencer_score (91.5) vs 22.0 and 8.4 → name web-07 as likely cause → recommend drilling into its records — do not answer with only bucket anomaly_score 88.

Score drop: "Score went from 90 to 55 — did the model change?" → Compare initial_record_score vs record_score → explain renormalization if drift is large.

Memory limit: "Job shows hard_limit and datafeed stopped." → Diagnose model_size_stats.memory_status = hard_limit → raise model_memory_limit via close/update/open lifecycle and/or reduce cardinality — not "just restart the datafeed".

New job: "Detect unusual error rates per host." → high_count with by_field_name: host.keyword → create/open/start sequence.


Guidelines

  1. Pick a mode first. Don't blend RCA logic with score-explanation logic in one response.

  2. For "which entity?" rank influencer_score, not bucket anomaly_score.

  3. For lifecycle failures read memory_status before recommending datafeed restarts.

  4. Show initial_record_score alongside record_score — the gap tells the renormalization story.

  5. Fix memory before query_delay. Hard_limit invalidates downstream diagnostics.

  6. Confirm RCAs with source evidence from the datafeed index.

Operations

HTTP API (shorthand)elastic CLI command
GET /{index}/_mappingelastic es indices get-mapping --index '<index>'
POST /{index}/_searchelastic es search --index '<index>' --input-file '<search-body.json>'
GET /_ml/anomaly_detectorselastic es ml get-jobs
GET /_ml/anomaly_detectors/{job_id}elastic es ml get-jobs --job-id '<job_id>'
GET /_ml/anomaly_detectors/{job_id}/_statselastic es ml get-job-stats --job-id '<job_id>'
GET /_ml/datafeeds/datafeed-{job_id}elastic es ml get-datafeeds --datafeed-id 'datafeed-<job_id>'
GET /_ml/datafeeds/datafeed-{job_id}/_statselastic es ml get-datafeed-stats --datafeed-id 'datafeed-<job_id>'
POST /.ml-anomalies-*/_searchelastic es search --index '.ml-anomalies-*' --input-file '<search-body.json>'
POST /.ml-annotations-*/_searchelastic es search --index '.ml-annotations-*' --input-file '<search-body.json>'
POST /.ml-notifications-*/_searchelastic es search --index '.ml-notifications-*' --input-file '<search-body.json>'
POST /_ml/anomaly_detectors/_estimate_model_memoryelastic es ml estimate-model-memory --analysis-config '<json>'
PUT /_ml/anomaly_detectors/{job_id}elastic es ml put-job --job-id '<job_id>' --input-file '<job-body.json>'
PUT /_ml/datafeeds/datafeed-{job_id}elastic es ml put-datafeed --datafeed-id 'datafeed-<job_id>' --input-file '<datafeed-body.json>'
POST /_ml/anomaly_detectors/{job_id}/_openelastic es ml open-job --job-id '<job_id>'
POST /_ml/anomaly_detectors/{job_id}/_closeelastic es ml close-job --job-id '<job_id>'
POST /_ml/anomaly_detectors/{job_id}/_updateelastic es ml update-job --job-id '<job_id>' --analysis-limits '<json>'
POST /_ml/datafeeds/datafeed-{job_id}/_updateelastic es ml update-datafeed --datafeed-id 'datafeed-<job_id>' --input-file '<update-body.json>'
POST /_ml/datafeeds/datafeed-{job_id}/_startelastic es ml start-datafeed --datafeed-id 'datafeed-<job_id>'
POST /_ml/datafeeds/datafeed-{job_id}/_stopelastic es ml stop-datafeed --datafeed-id 'datafeed-<job_id>'
POST /_ml/datafeeds/datafeed-{job_id}/_previewelastic es ml preview-datafeed --datafeed-id 'datafeed-<job_id>'
POST /_ml/anomaly_detectors/{job_id}/model_snapshots/{snapshot_id}/_revertelastic es ml revert-model-snapshot --job-id '<job_id>' --snapshot-id '<snapshot_id>'

Search body shapes for each result_type and troubleshooting queries are documented in references/investigation-queries.md and references/troubleshooting-reference.md.

Files

references/anomaly-detection-functions.md

Elastic ML Anomaly Detection Functions

Functions marked with * support high/low one-sided variants (e.g., high_count, low_mean) to detect anomalies in only one direction.


Count Functions

Analyze the occurrence rate of events or documents over time.

FunctionDescription
count *Number of documents in a bucket
non_zero_count *Like count but ignores zero-count buckets — use for sparse data
distinct_count *Cardinality (uniqueness) of values for a specific field
count vs non_zero_count
ScenarioUse
Events arrive in every bucket (e.g., web traffic, heartbeats)count — zero buckets are meaningful (outage signal)
Events arrive intermittently (e.g., batch jobs, error logs)non_zero_count — zeros are expected, not anomalous
Detecting a drop to zero as an outagecount with low_count variant
Detecting bursts in normally sparse trafficnon_zero_count with high_non_zero_count variant

Example: An intrusion detection log index gets events only when triggered. Using count means the model learns that zero-event buckets are normal overnight — making it impossible to distinguish a genuine quiet night from a monitoring gap. Use non_zero_count so the model only learns from buckets that had events, and flags when event volumes spike unexpectedly.

distinct_count

Counts the number of unique values for a field within each bucket. Useful for detecting credential stuffing (unusually high distinct usernames), data exfiltration (high distinct destination IPs), or DGA activity (high distinct DNS query names).


Metric Functions

Operate on numerical fields within the data.

FunctionDescription
min / maxMinimum or maximum value in a bucket
mean *Average value
median *Median value
sum / non_null_sum *Total sum of a field; non_null_sum is for sparse data
varp *Variance / volatility of a metric
metricShorthand that applies min, max, and mean simultaneously
Choosing the right metric function
GoalFunction
Detect average latency spikemean or high_mean
Detect worst-case latency (tail)max
Detect sustained volume droplow_sum
Detect unusual volatility (erratic metric)high_varp
Detect both high and low deviationsmean (bidirectional, default)
Detect only spikes, not dropshigh_mean
Monitor noisy metrics with outliersmedian (more robust than mean)
sum vs non_null_sum

Use non_null_sum when the field is frequently absent from documents (sparse). Like non_zero_count, it skips empty buckets so the model learns only from active periods.

metric shorthand

Creates three detectors in one: min, max, and mean on the same field. Convenient for a quick initial setup, but produces three anomaly records per detection event. Prefer explicit functions once you know which direction matters.


Advanced & Specialized Functions

Handle complex analysis types such as rarity or geographic data.

FunctionDescription
rareIdentifies values that occur at low frequency compared to the dataset
freq_rareFinds population members that cause rare values to occur frequently
info_content *Entropy of text strings — useful for detecting encrypted/obfuscated commands
lat_longDetects unusual geographic locations from latitude/longitude coordinates
time_of_dayDetects behavioral changes relative to time of day
time_of_weekDetects behavioral changes relative to day of week

rare vs freq_rare

These two are often confused but answer different questions:

FunctionQuestion answeredRequires over_field
rare"Which values of the by_field are unusual globally?"No
freq_rare"Which entities (over_field) frequently cause rare values?"Yes

rare example: Detect rare process.name values across all hosts. If svchost.exe with unusual arguments appears only once in 30 days, rare flags it. The focus is the rarity of the value.

freq_rare example: Detect which users (over_field) frequently trigger rare process executions (by_field). Most users run rare processes occasionally, but a user running rare processes consistently is a lateral movement signal. The focus is the entity's behavior pattern.

Practical guidance:

  • Use rare for hunting unknown unknowns — values that shouldn't exist at all.

  • Use freq_rare for insider threat and lateral movement scenarios — who is repeatedly doing unusual things.

  • rare generates high false-positive rates in noisy environments; use custom rules to suppress known-good rare values.

  • freq_rare requires an over_field (population) — without it, use rare.

Types of rare analysis

Translate business goals to the correct rare detector configuration:

GoalExampleDetector config
Find infrequent values for a fieldDetect hosts occurring infrequentlyrare by_field_name: host
Find infrequent values compared to peersDetect hosts visited by few users vs. other hosts; detect users visiting rare hostsrare by_field_name: host over_field_name: user
Find infrequent values segmented by another fieldPer location, detect infrequently seen hostsrare by_field_name: host partition_field_name: location
Find infrequent values by another field compared to peers, segmentedPer location, detect hosts visited by few users vs. peers; detect users visiting rare hosts in a locationrare by_field_name: host over_field_name: user partition_field_name: location

info_content

Measures the Shannon entropy of text strings. High entropy = high randomness = potentially encoded, encrypted, or machine-generated content.

Entropy rangeInterpretationExample
Low (predictable)Normal human-readable textGET /api/users HTTP/1.1
MediumMixed structured/variable contentLog messages with variable IDs
High (random)Encoded, encrypted, or DGA-generatedaGVsbG8gd29ybGQ= (base64), zxq7v9abc.com (DGA)

Use cases:

  • DNS query names with by_field_name: "dns.question.name" — DGA malware generates high-entropy domain names (e.g., xk9p2mnqabcdef.ru).

  • User-agent strings — malware C2 frameworks often use randomized or encoded user agents.

  • URL paths / query strings — webshell commands embedded in request parameters.

  • Command-line arguments — base64-encoded PowerShell payloads.

high_info_content (one-sided): only alerts on unusually high entropy, which is almost always the right choice for security use cases.


time_of_day vs time_of_week

Both detect when something happens relative to established patterns, not how much.

FunctionGranularityBest for
time_of_dayHour/minute of dayDetecting off-hours access (3am login for a 9-5 user)
time_of_weekDay of weekDetecting weekend/holiday activity (batch job on Sunday)

time_of_day example: A database admin who always connects between 08:00–18:00 on weekdays. time_of_day learns this pattern. A connection at 02:30 scores anomalous — even if the volume of activity is normal.

time_of_week example: A data pipeline that runs Monday–Friday. time_of_week flags it running on Saturday. time_of_day would not catch this (the time of day, e.g., 08:00, may be normal).

Choosing between them:

  • Use time_of_day when the anomaly is "wrong hour of the day."

  • Use time_of_week when the anomaly is "wrong day of the week."

  • Use both together (two detectors) for comprehensive temporal coverage.

  • Neither function cares about count or metric values — use count/mean detectors in the same job for volume-based detection.


One-Sided Variants

Functions marked with * support high_ and low_ prefixes:

VariantDetects
high_<function>Only values significantly above the expected range
low_<function>Only values significantly below the expected range
<function> (no prefix)Both directions (bidirectional)

When to use one-sided:

  • high_mean(response_time) — only alert on latency spikes, not drops (a faster response is never a problem).

  • low_count(login_events) — only alert on unusually low login volume (could indicate authentication system failure).

  • high_distinct_count(destination.ip) — only alert on abnormally high unique destination IPs (exfiltration signal).

Bidirectional variants (mean, count) generate alerts for both directions, which can produce noise when only one direction is operationally relevant.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

The author says: Elasticsearch 8.x–9.x or Elastic Cloud Serverless with ML anomaly detection; Kibana 8.x–9.x for saved-object context only

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
elastic
Publisher
Elastic (Official)
Source
elastic/agent-skills · skills/kibana/kibana-anomaly-detection · commit 09d879d
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Also distributed in
elastic/agent-skills

AutoGineer is not affiliated with or endorsed by elastic or Elastic.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download kibana-anomaly-detection.zip