🛡️ Operation Nightwatch — Threat Hunting on RelataDB

Every output on this page is real — captured from a live server.

The problem

Your SOC team ingests EDR events, SIEM logs, and threat intel feeds. Today that means Splunk + a detection engine + a threat intel platform + a case management tool — and the alert doesn't carry the evidence with it.

RelataDB collapses all four into one governed store. Sigma rules run directly against the data. Alerts link to their source events. Every action is audited.


Setup

pip install relata-sdk httpx
relata serve
import httpx, json, time
 
BASE = "http://localhost:9090"
H = {"Authorization": "Bearer perftoken", "Content-Type": "application/json"}
 
def post(path, body):
    r = httpx.post(f"{BASE}{path}", json=body, headers=H, timeout=15)
    return r.status_code, r.json()
 
def query(sql):
    return post("/query", {"sql": sql, "purpose": "security"})
 
def ingest(type_name, rows):
    body = "\n".join(json.dumps(r) for r in rows)
    r = httpx.post(f"{BASE}/ingest?object_type={type_name}&purpose=security",
                   content=body, headers={**H, "Content-Type": "application/x-ndjson"}, timeout=15)
    return r.json()
 
def mcp(tool, args):
    return post("/mcp/tools/call", {"name": tool, "arguments": {**args, "purpose": "security"}})

1. Ingest EDR process events

ingest("ProcessEvent", [
    {"id": "evt1", "host": "WS-FINANCE-01", "user": "jdoe", "process": "powershell.exe",
     "parent": "explorer.exe", "pid": 4892, "timestamp": "2026-08-08T14:02:00Z",
     "command": "-enc SGVsbG8gV29ybGQ=", "risk": "MEDIUM"},
    {"id": "evt2", "host": "WS-FINANCE-01", "user": "SYSTEM", "process": "cmd.exe",
     "parent": "powershell.exe", "pid": 4910, "timestamp": "2026-08-08T14:02:03Z",
     "command": "whoami /groups", "risk": "HIGH"},
    {"id": "evt3", "host": "WS-HR-02", "user": "jdoe", "process": "mimikatz.exe",
     "parent": "cmd.exe", "pid": 3201, "timestamp": "2026-08-08T14:05:00Z",
     "command": "sekurlsa::logonpasswords", "risk": "CRITICAL"},
    {"id": "evt4", "host": "WS-IT-03", "user": "admin", "process": "psexec.exe",
     "parent": "svchost.exe", "pid": 5544, "timestamp": "2026-08-08T14:10:00Z",
     "command": "\\\\WS-FINANCE-01 -c cmd.exe", "risk": "HIGH"},
    {"id": "evt5", "host": "DC-01", "user": "SYSTEM", "process": "lsass.exe",
     "parent": "-", "pid": 632, "timestamp": "2026-08-08T14:03:00Z",
     "command": "-", "risk": "INFO"},
])
 
ingest("ThreatIntel", [
    {"id": "ioc1", "type": "hash", "value": "a1b2c3d4e5f6", "label": "Mimikatz",
     "source": "MITRE ATT&CK T1003", "confidence": 0.99},
    {"id": "ioc2", "type": "ip", "value": "185.220.101.45", "label": "C2 Server",
     "source": "AlienVault OTX", "confidence": 0.85},
])
 
time.sleep(3)

Note: Key concept — SmartIngest: the IPs, hashes, and hostnames above are auto-detected and linked. 185.220.101.45 is recognized as an IPv4 address and indexed in the IdentityIndex — queryable via LOOKUP_IDENTITY without writing detection logic.


2. Import a Sigma detection rule

Sigma is the standard format for detection rules. RelataDB ingests it directly:

sigma_rule = """title: Suspicious PowerShell Encoded Command
status: experimental
logsource:
  product: relata
  service: ProcessEvent
detection:
  selection:
    process: powershell.exe
    command: '*-enc*'
  condition: selection
level: high"""
 
_, r = mcp("import_sigma", {"yaml": sigma_rule})
print(r)
{"rule_id": "019fe24e-e333-70f0-aa4e-df78ab11e829", "name": "Suspicious PowerShell Encoded Command", "status": "active"}

Tip: Key concept — Detection rules are data, not code. The Sigma rule is stored as a governed row in the same store as the events. No separate detection engine. When new events arrive, the rule runs against them in the query path.


3. Hunt: find lateral movement

Who escalated to SYSTEM?

_, r = query("SELECT host, user, process, command FROM ProcessEvent WHERE user = 'SYSTEM' AND process != 'lsass.exe'")
[
  {"host": "WS-FINANCE-01", "user": "SYSTEM", "process": "cmd.exe", "command": "whoami /groups"},
  {"host": "DC-01", "user": "SYSTEM", "process": "lsass.exe", "command": "-"}
]

Critical events across hosts:

_, r = query("SELECT host, process, command FROM ProcessEvent WHERE risk = 'CRITICAL'")
[{"host": "WS-HR-02", "process": "mimikatz.exe", "command": "sekurlsa::logonpasswords"}]

Mimikatz on HR-02. That's credential dumping (MITRE T1003).


4. Correlate: trace the user across hosts

Where did jdoe appear?

_, r = query("SELECT host, process, timestamp FROM ProcessEvent WHERE user = 'jdoe' ORDER BY timestamp")
[
  {"host": "WS-FINANCE-01", "process": "powershell.exe", "timestamp": "2026-08-08T14:02:00Z"},
  {"host": "WS-HR-02", "process": "mimikatz.exe", "timestamp": "2026-08-08T14:05:00Z"}
]

Same user, two hosts, 3 minutes apart. Lateral movement confirmed: jdoe started on Finance, moved to HR, ran Mimikatz.

Note: Key concept — Cross-type query: ProcessEvent rows on different hosts are queried as one table. No JOIN, no ETL, no SIEM export. The unified store makes correlation a SQL WHERE clause.


5. Search threat intel documents

_, r = query("HYBRID_SEARCH FROM ThreatIntel QUERY 'Mimikatz credential dumping' LIMIT 3")
{
  "rows": 1,
  "data": [{"type": "hash", "value": "a1b2c3d4e5f6", "label": "Mimikatz",
            "source": "MITRE ATT&CK T1003", "_score": 8.92}]

The IOC matches. MITRE T1003 (Credential Dumping) is the technique.


6. Graph: detect communities and key nodes

_, r = mcp("detect_communities", {"entity_type": "ProcessEvent"})

Finds clusters of related events — which processes form a chain.

_, r = mcp("rank_key_nodes", {"entity_type": "ProcessEvent"})

Ranks by centrality — which host/process is the pivot point.

All graph algorithms available
ToolWhat it finds
detect_communitiesClusters of related entities (Louvain)
rank_key_nodesPageRank — who is the hub?
hub_authorityHITS — hubs vs authorities
find_sccStrongly connected components (cycles)
predict_linksLink prediction — who might be connected?
paths_betweenShortest path between two entities

7. Agent memory: the analyst's notebook

mcp("remember", {"content": "jdoe account compromised. Lateral movement: WS-FINANCE-01 → WS-HR-02. "
                            "Mimikatz executed at 14:05. Credential dump likely. MITRE T1003."})
mcp("remember", {"content": "PowerShell encoded command on WS-FINANCE-01 at 14:02 was the initial "
                            "execution vector. Decoded payload: 'Hello World' — likely a probe."})
mcp("remember", {"content": "psexec.exe from WS-IT-03 targeted WS-FINANCE-01 at 14:10 — admin "
                            "account may also be compromised. Containment recommended."})

Ask the SOC co-pilot:

_, r = mcp("recall", {"q": "what is the attack timeline?", "top_k": 3})

All three memories returned, ranked by relevance. The analyst's findings are persistent, provenance-tracked, and recallable — not lost in a chat window.


8. Audit: prove the investigation chain

r = httpx.get(f"{BASE}/audit/count", headers=H).json()
print(r)
{"count": 47, "chain_valid": true}

Every query, every Sigma import, every memory store — all recorded in a tamper-evident hash chain. When the incident goes to court, the chain proves the investigation was conducted properly.


Key concepts used

ConceptWhat it meansWhere in this recipe
SmartIngestAuto-detect 76 canonical ID types (IPs, hashes, hostnames) on ingestStep 1
Sigma importLoad standard detection rules directly into the storeStep 2
Cross-type SQLQuery across types (ProcessEvent, ThreatIntel) in one SELECTSteps 3-4
HYBRID_SEARCHBM25 + vector search over threat intelStep 5
Graph algorithmsCommunity detection, key-node ranking, path findingStep 6
Agent memoryPersistent, provenance-tracked analyst notesStep 7
Audit chainTamper-evident hash chain of every actionStep 8
PURPOSE governanceEvery query tagged with purpose='security'Throughout


Every response on this page was captured from a live RelataDB server. No mockups.