Architecture

detsema is structured like a compiler: frontends parse rule dialects into one typed IR, passes answer questions about the IR, emitters render findings.

frontends/sigma      Sigma YAML -> IR (documented subset; explicit unsupported verdicts)
ir                   typed predicate trees + reference evaluator Eval
passes/deps          field-dependency extraction
passes/check         schema verdicts: unknown-field, case-mismatch, type-mismatch, unsupported
schema               detsema-schema/v1 inventories: load, infer from NDJSON, import ECS
emitters/sarif,jsonl SARIF 2.1.0 and JSON-lines output
cmd/detsema          CLI and exit codes

IR

Rules compile to boolean trees (And, Or, Not) over leaf predicates. A predicate applies one typed matcher to one event field:

Matcher Carries From Sigma
StringMatch anchor (exact/contains/prefix/suffix), pattern, wildcard structure, case flag, windash plain values, contains, startswith, endswith, cased, windash
NumCompare operator, bound lt, lte, gt, gte
RegexMatch compiled RE2 re (+ i, m, s)
CIDRMatch parsed prefix cidr
ExistsMatch presence assertion exists, field: null

Matchers are structured data, not closures, so passes can reason about them by type: numeric intervals from NumCompare, prefix/suffix compatibility from StringMatch, value domains from schema enums. This is what lets the planned satisfiability pass work without an SMT solver.

Eval(node, event) is the reference semantics for the whole toolchain. The semantic decisions (case-insensitive matching by default, wildcard escaping, null-means-absent, list values match on any element, dotted field lookup) are specified in SUBSET.md.

Pass contract

A pass consumes IR plus an optional schema inventory and emits findings with rule id, title, file, line, severity and a stable type. Two rules are load-bearing:

  1. No silent approximation. A construct the frontend cannot represent exactly becomes an unsupported verdict naming the construct. Parseable fragments of such rules still contribute dependency data, marked partial.
  2. Witness verification. Any future pass that emits a witness event (“this event matches rule A but not rule B”) must check the claim with Eval before emitting it.

Schema inventories

An inventory (detsema-schema/v1) declares fields with types (string, number, bool, ip, timestamp, object, unknown) and optional closed enum domains. Every verdict is relative to a declared inventory and reports name the inventory source. Sources: inferred from NDJSON event samples, imported from ECS, hand-written, or (planned) exported from live cluster mappings by deadair.

Safety properties

  • No network access at runtime; only the files named on the command line are read.
  • No telemetry, no phone-home.
  • Inputs are untrusted: malformed YAML/JSON produces clean errors; rule patterns compile to RE2 (linear-time matching).
  • Runtime dependency surface is one YAML parser; growing it is an explicit maintainer decision.

Relationship to deadair

Same maintainer, separate repositories on purpose. deadair asks the environment-side question against a live cluster; detsema asks the logic-side question offline.

  deadair detsema
Ground truth live cluster state rule text + declared schema
Question can enabled rules see telemetry, here, now? is the logic coherent, anywhere, ever?
Finds disconnected/starved rules, stale sources, lag windows dead field references, type incoherence; next: contradictions, shadowing, semantic diffs
Blind to logic defects in well-fed rules starvation of well-formed rules

They stay separate so detsema’s CI users and library consumers never inherit cluster clients or credential handling, and so “this library touches no network” stays verifiable per repo.

Scope and roadmap

Out of scope permanently: translating rules between dialects, hosting or ranking rule content, live telemetry monitoring, full-language SPL/KQL support (frontends cover documented subsets).

Planned: sat (schema-aware satisfiability: provably dead logic, filters that swallow their selections), diff (semantic diff of rule changes, with witness events), shadow (corpus-wide subsumption), fixture (evaluate IR against labeled events), KQL/EQL subset frontends, SMT-backed regex equivalence, an MCP server over the same passes.