A vendor-neutral detection-engineering copilot that works both directions — turn intel into rules, and rules into a reviewed merge.
Draft, lint, dedupe and review detections as Sigma or Cortex XQL — and go the other way, from a raw threat report to a grounded ATT&CK + Sigma/YARA/Suricata package. Deterministic primitives that never raise; the LLM is an enhancement that can fail without taking the tool down.
The junior move is to make the whole pipeline a single model call — draft, validate, dedupe and review all in one prompt. Then the model is slow, rate-limited, or simply absent in CI, and the entire stage falls over with it.
The fix isn't a more reliable model. It's structural: the deterministic parts run without a model, the AI is an optional enhancement, and every verb degrades instead of breaking.
Pick a verb, then toggle the model off. The deterministic core keeps running, the LLM layer drops to a floor or sets an error field — and nothing ever raises.
A new advisory drops and someone has to translate it into detection content; or an analyst describes a behavior and someone has to turn it into a reviewed rule. detflow does both halves of the detection-as-code loop — the only vendor-specific parts (where intel comes from, where rules deploy) are exactly what it leaves to you.
Paste a CVE advisory or a CTI writeup; get back tactic-ordered techniques with evidence, generated rules with the Sigma linted in place, and one-call exports to STIX 2.1, an ATT&CK Navigator layer, or an audience-aware Markdown brief.
Describe the behavior; get a first-draft rule, linted offline, deduped against the catalog you already run, and reviewed like a senior engineer — quality score, false-positive risk, ATT&CK mapping and the gaps worth flagging.
Every piece runs on its own, and the boring ones run with no model, no network and no keys — so they drop straight into CI.
Map a raw report to tactic-ordered ATT&CK techniques with confidence, generate Sigma / YARA / Suricata, and treat the report as hostile input — the prompt is hardened against injection and never invents technique IDs.
Turn a sentence into a first-draft rule in either language. The XQL prompt knows XQL — no startswith, no SQL where — so you don't get SQL-shaped hallucinations back.
Validate a rule offline — stdlib plus PyYAML — and get pass / warn / fail with per-finding messages. Runs in CI with zero secrets.
Check a rule against the inventory you already run, so you don't ship coverage you already have. Deterministic, no network.
Quality score, false-positive risk, ATT&CK mapping and verdict when a model is present — and a deterministic floor (lint + overlaps + parsed techniques) when it isn't. Never raises.
Each is one pure function of the analysis with deterministic IDs — a STIX 2.1 bundle for the TIP, an ATT&CK Navigator layer for the coverage map, and a Markdown brief framed for the reader you pass.
The brief is the one piece that knows its audience: pass audience= and the same analysis is framed for dr, soc, leadership, purple_team, red_team or general — one analysis, six lenses. The mappings and rules don't change; only the framing does.
detflow imports no SDK and hard-codes no provider. A "model" is anything with a single complete() method — so an OpenAI-compatible endpoint, a local vLLM/Ollama server, or a LangChain failover chain all drop straight in.
import detflow # both directions, one import ---------------------------------- sigma = detflow.draft("encoded PowerShell spawned from a Word macro") a = detflow.analyze(advisory_text, audience="dr") # report → package # the deterministic verbs need no model, no network, no keys report = detflow.lint(sigma.rule) # pass / warn / fail overlaps = detflow.find_overlaps(sigma.rule, catalog) detflow.to_stix_bundle(a) # pure fn → your TIP # review uses a model when present, a floor when it isn't result = detflow.review(sigma.rule, catalog=catalog) print(result.quality_score, result.false_positive_risk, result.verdict) # bring any model — here, a langchain-failover chain from langchain_failover import FailoverChatModel from detflow.llm import LangChainModel model = LangChainModel(FailoverChatModel(models=[primary, local_fallback])) detflow.analyze(advisory_text, model=model) # rides the failover chain
detflow is the detection-side sibling of iocflow — iocflow handles the indicator lifecycle; detflow handles the rule lifecycle. Same design DNA.
I kept re-implementing the same generic stages inside every pipeline — lint a rule, draft one from English, dedupe against the catalog, review it, and translate an advisory into detection content. The only vendor-specific parts are compiling to your query language and dry-running against your tenant — so I left exactly those out, scrubbed the generic middle, and shipped it. It even eats its own dog food: the failover model is langchain-failover, another package extracted from the same work. Deterministic primitives plus optional AI — the boring parts are boring and tested, the model adds judgment where judgment helps, and nothing falls over when it's slow or absent.