Open-source SIEM detection health.
Find enabled detections that are blind because their telemetry is missing, stale, late, or
schema-incompatible.
Runs locally · Read-only · No agent · No telemetry upload
Read the technical write-up · Featured in Detection Engineering Weekly
Real scan of a disposable Elastic lab with deliberately missing, stale, late, and unused telemetry. Reproduce it with make record-scan-lab.
A rule can be enabled, scheduled, and error-free while the data it needs is gone. deadair reads the live rule inventory, resolves each rule's inputs using the backend's native semantics, and checks the concrete sources behind them.
It catches:
- rules whose index, alias, or data-stream selectors resolve to nothing;
- rules whose matching sources are all stale or empty;
- rules running with missing fields or an ingest-lag blind window;
- healthy telemetry that no enabled detection reads.
deadair currently works with Elastic Security and OpenSearch Security Analytics.
Download a binary for macOS, Linux, or Windows from GitHub Releases, or install with Go:
go install github.com/alephnull-sh/deadair/cmd/deadair@latestConnect a read-only SIEM credential:
deadair setup elastic # print the least-privilege setup
deadair check # verify the credential can scan
deadair scan # assess live rules and telemetryExit codes are stable: 0 is healthy, 1 means findings, and 2 means the scan failed.
| Stage | What deadair does |
|---|---|
| Inventory | reads enabled detections and the inputs they declare |
| Resolve | asks Elastic or OpenSearch to resolve index patterns, aliases, data streams, selectors, and remote inputs |
| Measure | checks document count, freshest event, storage, field mappings, schema history, and ingest lag |
| Report | emits terminal, JSON, HTML, fleet rollups, and Prometheus metrics with the evidence behind each verdict |
deadair proves whether a detection's observable telemetry prerequisites are present and healthy. It does not prove that the rule logic is correct or that a simulated attack will produce an alert. Pair it with static rule validation and end-to-end detection testing for those layers.
| Finding | Meaning | First check |
|---|---|---|
| no matching source | none of the rule's inputs resolve to a visible index or data stream | pattern changes, missing integrations, and credential scope |
| all sources stale or empty | every resolved source is unusable right now | source cadence and the ingest path |
| missing fields | declared fields are absent from every matched source mapping | parser, package, and mapping changes |
| lag blind window | measured ingest lag exceeds the rule's lookback margin | rule interval, lookback, timestamp override, and pipeline delay |
| source degradation | a source is stale, empty, low-volume, or schema-drifted | source history and expected maintenance |
| unused telemetry | data is being stored but no enabled local detection resolves to it | disabled rules and intentional collection |
Every verdict is limited to what the configured credential can see. JSON reports include the configured expressions, resolved sources, resolution method, assessment status, backend metadata, and capability evidence. See the usage guide for worked examples and triage.
Elastic:
export DEADAIR_ES_URL=https://es.example.internal:9200
export DEADAIR_KIBANA_URL=https://kibana.example.internal:5601
export DEADAIR_API_KEY=<read-only-api-key>
deadair check
deadair scan --json-out report.json --html-out report.htmlOpenSearch:
export DEADAIR_BACKEND=opensearch
export DEADAIR_OPENSEARCH_URL=https://opensearch.example.internal:9200
export DEADAIR_OPENSEARCH_USERNAME=deadair
export DEADAIR_OPENSEARCH_PASSWORD=<password>
deadair check
deadair scanUse the documented least-privilege roles for Elastic or OpenSearch. The trusted integration suite also proves that write attempts made with those credentials are rejected.
# Gate a candidate rule against live source availability.
deadair scan --rule new-rule.json
# Fail only on new regressions between reports.
deadair diff yesterday.json today.json
# Scan multiple SIEM instances from one process.
deadair scan --fleet fleet.json
# Export cached scan results as Prometheus metrics.
deadair serve --interval 5mscan --rule isolates the candidate rule from unrelated backlog. diff works with deterministically
redacted reports. Fleet configuration references secrets through environment variables rather than
storing secret values.
A candidate-rule gate and report diff against a throwaway Elastic stack.
See CI gate behavior, fleet and MSSP deployment, and the Prometheus examples for production patterns.
The integration workflow currently tests these exact versions:
| Backend | Exact live-CI versions |
|---|---|
| Elastic Security | 8.19.19, 9.4.4 |
| OpenSearch Security Analytics | 2.19.6, 3.7.0 |
Other versions may work but are not covered by the current CI matrix.
- All backend access is read-only; trusted integration tests prove the documented credentials cannot write.
- Reports, HTML, state files, and fleet output are written
0600on POSIX systems. - Credentials can come from environment variables or files, avoiding secrets in process arguments.
--redactreplaces tenant, rule, source, pattern, and field names with stable digests.- The exporter binds to loopback by default.
- deadair has no phone-home behavior or usage telemetry.
Treat reports as sensitive SOC artifacts: they identify blind detections, source names, schema gaps, and unused collection.
- Usage guide — first scans, report evidence, findings, CI gates, state, and fleets
- Validation and dogfooding — what is proven and what still needs field evidence
- Architecture — backend contract, data model, safety properties, and limits
- Best practices — rollout order, alert context, and routing
- MSSP guide — secrets, redaction, retention, sizing, and tenant failure handling
- Detections that run but can't see — the problem and a reproducible simulation
Bug reports, sanitized fixtures, correctness cases, docs, and backend proposals are welcome. Start with CONTRIBUTING.md and use the backend RFC template for adapter work.
Apache-2.0.

