Detection rules — ingest → detect → investigate in one binary
Relata collapses the SIEM + sidecar + database triangle into one engine. A detection rule is a SQL WHERE against any governed type; it fires within the request cycle on commit (not on a 30-second poll), emits governed Alert rows with full PROV-O provenance, and pushes to per-tenant webhooks with queryable delivery status. Import Sigma's 10 000+ community detection rules as-is. The result: ingest → detect → investigate is one closed loop, and alerts are bi-temporal — "would this rule have fired last Tuesday?" is an AS OF query.
Create a rule
curl -X POST http://127.0.0.1:9090/rules \
-H 'Authorization: Bearer <token>' -H 'Content-Type: application/json' \
-d '{
"name": "suspicious-dns-exfil",
"target_type": "DnsEvent",
"condition_sql": "query_length > 100 AND rdata_type = '\''TXT'\'' AND query LIKE '\''%.xyz'\''",
"severity": "high",
"mitre_technique": "T1048",
"purpose": "security"
}'# Python SDK
client.governance_client.create_rule({
"name": "suspicious-dns-exfil",
"target_type": "DnsEvent",
"condition_sql": "query_length > 100 AND rdata_type = 'TXT'",
"severity": "high",
"mitre_technique": "T1048",
}, purpose="security")From then on, every governed DnsEvent ingest that matches the condition produces an Alert row — in the same request, before the ingest returns. No poller, no lag.
Commit-driven firing (the differentiator)
Most rule engines run on a cron/poll loop — ingest a row now, the alert lands 30 seconds later (or never, if the poller is down). Relata fires rules on the commit bus (the GraphChangeEvent stream, in crates/relata-cli/src/serve/rule_eval.rs) — the candidate check runs as part of the write transaction, so:
- Latency is sub-request. By the time
POST /ingestreturns200, any alerts it triggered are already in the audit log. - No missed events. If the server is up enough to accept the write, it's up enough to fire the rule. A down poller can't silently drop detections.
- Bitmap-indexed candidate filter. Each rule compiles to a bitmap predicate over the target type, so the commit-time check is a cheap set-membership test against just-committed rows — not a full table scan per rule.
Import Sigma rules (10000+ community detections, as-is)
Sigma is the open standard for generic detection rules — the community publishes thousands for known attacker TTPs. Relata imports them natively:
relata import-sigma rules/suspicious-powershell.yaml
# or via HTTP
curl -X POST http://127.0.0.1:9090/rules/sigma \
-H 'Authorization: Bearer <token>' -H 'Content-Type: application/x-yaml' \
--data-binary @rules/suspicious-powershell.yamlclient.governance_client.import_sigma(open("rules/suspicious-powershell.yaml").read(),
purpose="security")Each Sigma rule's logsource → detection → fields map onto your registered governed types and the SQL WHERE condition.
Bi-temporal alerts — backtest and "what would have fired?"
Alert rows are bi-temporal just like every other governed row. Run the rule as-of a past window to backtest detection quality without re-ingesting:
PURPOSE 'security'
SELECT alert_id, rule_name, severity, target_id, valid_from
FROM Alert AS OF '2026-01-15T00:00:00'
WHERE severity IN ('high', 'critical')
ORDER BY valid_from DESC;This is impossible in a SIEM that overwrites alerts — the historical alert state is gone the moment it ages out. Here it's a first-class query.
Per-tenant webhooks with delivery status
Configure a webhook per tenant; alerts push automatically. Crucially, the delivery status is queryable — worst-outcome-wins ranking collapses per-attempt outcomes into one AlertRecord.delivery_status:
# Find alerts whose webhook delivery failed (retry loop exhausted)
curl 'http://127.0.0.1:9090/alerts/list?delivery_status=failed' \
-H 'Authorization: Bearer <token>'# Python — register a webhook, then inspect delivery health
client.register_webhook("https://soar.example.com/relata-alerts",
event_types=["alert.high"])
failed = client.query("SELECT * FROM Alert WHERE delivery_status = 'failed'")The retry loop (deliver_webhook_retry_loop) returns a typed WebhookDeliveryOutcome per attempt; failures are logged and recoverable, not silently lost.
Stream alerts in real time
For a live dashboard or a SOAR integration, stream alerts over SSE:
# Python
for event in client.streaming_client.alerts():
print(event["severity"], event["rule_name"], event["target_id"])# Raw SSE
curl -N http://127.0.0.1:9090/alerts/stream -H 'Authorization: Bearer <token>'Tips & takeaways
- Write the
condition_sqlto be selective. The commit-time bitmap filter is fast, but a rule that matches 50% of rows fires constantly and floods the audit log. Use specific thresholds andANDconjunctions. - Pair rules with
mitre_technique. Tagging alerts with MITRE ATT&CK technique IDs makes them correlatable across rules and against threat-intel — and the field is queryable. - Use backtesting before going live.
SELECT FROM Alert AS OF '<last-week>'against a newly-created rule tells you what it would have fired — tune thresholds before the rule touches production. - Webhook delivery status is your safety net. Set up a periodic
?delivery_status=failedcheck so a SOAR outage doesn't silently drop alerts. - Sigma import is the fast path to coverage. Before authoring custom rules, search the Sigma repo for your log source — chances are someone already wrote the detection.
See also
- Use case: Cyber Sigma detection — end-to-end worked example
- Jobs, Workflows & Detection — the typed-Job engine and scheduled detection
- Governance — ACL, purpose, and the audit chain alerts inherit
- Bi-temporal queries —
AS OFalert backtesting - Connectors & Extensions — Sigma + STIX + MISP + TAXII ingest paths