Skip to content

Incident Management

Incident Management is RhythmX's formal escalation layer. It promotes high-risk actors from the triage view into tracked, assignable incidents with full audit trails, SLA enforcement, campaign linking, and external ticket integration (Jira/ServiceNow).


Full Incident Workflow

Data Collection (continuous, 24/7)

  • LogRhythm SIEMalarm_actors table (alarms, rules, MITRE)
  • RhythmX Detection Enginesigma_alerts table (individual detections)
  • Correlation Enginethreat_cases table (attack patterns: brute force, lateral movement, APT chain, etc.)

Risk Score Calculation (every 5 minutes — actor_cache_sync)

For every actor (user/host/IP), the system calculates a 0–100 risk score from 7 factors:

Score = Threat Cases (30%) + RhythmX Detections (20%) + Volume (10%) + Velocity (10%) + Diversity (10%) + MITRE Coverage (10%) + Recency (10%)

  • Volume and Velocity combine SIEM alarms + RhythmX Sigma detections for complete event coverage
  • Recency uses the freshest timestamp across all sources (SIEM alarms, Sigma alerts, threat cases)
  • Diversity uses SIEM rules only (Sigma rules are already counted in RhythmX Detections)

The score and last_seen timestamp are stored in the actor_risk_summary table.

Incident Qualification (every 5 minutes — incident_sync_service)

Step 1 — Auto-close stale incidents:

For every OPEN or IN_PROGRESS incident:

  • If last_alarm_time is older than 24 hours (configurable) AND there are no open threat cases
  • → Set status = CLOSED and add resolution note: "Auto-closed: no activity for 24 hours and no open threat cases."

Step 2 — Find qualifying actors:

  • Path A: Has at least 1 open threat case → qualifies immediately (no time filter, no risk threshold)
  • Path B: Risk score ≥ 70 → qualifies (no time filter — recency is built into the risk score itself)
  • Everything else stays in the triage view (analyst can promote manually)

Step 3 — Update or Create incident:

  • If an OPEN incident already exists for this actor (same entity):

    • → Update last_alarm_time and refresh attack_type (adds any new types, comma-separated)
    • → No new incident is created
  • If no OPEN incident exists (after any closure — manual CLOSED, FALSE_POSITIVE, or auto-closed):

    • → Increment sequence number (counts all previous CLOSED + FALSE_POSITIVE incidents for this actor + entity)
    • → Generate new incident_id
    • → Determine campaign (see Campaign Linking below)
    • Create new incident with status = OPEN
    • → System state set based on risk + threat cases (MONITORING / ACTIVE / ESCALATED)
    • → Incident classified by recency (ACTIVE / RECENT / HISTORICAL)
    • → Attack trend initialized (STABLE)
    • → Auto-push to Jira/ServiceNow after ESCALATED stability check (2 cycles, ~10 min) — skipped for HISTORICAL incidents
flowchart TD
    START["<b>Sync Service Runs</b><br>Every 5 minutes"] --> STEP1["<b>Step 1: Auto-Close</b><br>OPEN/IN_PROGRESS incidents<br>where last_alarm > 24h<br>AND no open threat cases"]
    STEP1 --> CLOSE["Set status = CLOSED<br>Note: Auto-closed"]
    STEP1 --> STEP2["<b>Step 2: Find Actors</b><br>Path A: threat_case ≥ 1<br>Path B: risk ≥ 70 + active 24h"]
    STEP2 --> CHECK{"OPEN incident<br>exists for<br>actor + entity?"}
    CHECK -->|"Yes"| UPDATE["<b>Update</b><br>Refresh last_alarm<br>Refresh attack_type"]
    CHECK -->|"No"| SEQ["Increment sequence<br>Generate new ID"]
    SEQ --> CMP["Check for closed incident<br>within 7 days (campaign)"]
    CMP --> CREATE2["<b>Create Incident</b><br>Status: OPEN"]
    CREATE2 --> PUSHCHECK{"Auto-push?"}
    PUSHCHECK -->|"Yes"| PUSH2["Jira / ServiceNow"]
    PUSHCHECK -->|"No"| DONE["Done"]
    PUSH2 --> DONE

    style START fill:#4a148c,stroke:#9c27b0,color:#fff
    style STEP1 fill:#37474f,stroke:#78909c,color:#fff
    style CLOSE fill:#1b5e20,stroke:#66bb6a,color:#fff
    style STEP2 fill:#1a237e,stroke:#534bae,color:#fff
    style CHECK fill:#00695c,stroke:#4db6ac,color:#fff
    style UPDATE fill:#0d47a1,stroke:#42a5f5,color:#fff
    style SEQ fill:#e65100,stroke:#ff9800,color:#fff
    style CMP fill:#6a1b9a,stroke:#ab47bc,color:#fff
    style CREATE2 fill:#b71c1c,stroke:#ef5350,color:#fff
    style PUSHCHECK fill:#00695c,stroke:#4db6ac,color:#fff
    style PUSH2 fill:#1565c0,stroke:#64b5f6,color:#fff
    style DONE fill:#1b5e20,stroke:#66bb6a,color:#fff

Key Rules:

  • Only one open incident per actor per entity at any time
  • Incidents are created directly as OPEN — there is no "NEW" status
  • last_alarm_time is automatically copied from actor_risk_summary.last_seen on every sync cycle
  • After any closure (manual or auto), the next qualifying activity always creates a new incident — never reopens the old one

Incident Statuses

There are four statuses. There is no separate AUTO-CLOSED status in the system.

Status Description How it gets here UI Display
OPEN Awaiting analyst assignment Auto-created by sync service OPEN
IN_PROGRESS Analyst actively investigating Analyst assigns themselves IN PROGRESS
CLOSED Resolved — threat handled or inactivity timeout Analyst closes manually OR system auto-closes after 24h CLOSED or CLOSED (auto)
FALSE_POSITIVE Benign — not a genuine threat Analyst determines not a real threat FALSE POSITIVE

Important: When the system auto-closes an incident, it sets the status to CLOSED and adds the resolution note "Auto-closed: no activity for 24 hours and no open threat cases." In the UI, this displays as CLOSED (auto) for clarity — but it is the same status as a manual CLOSED.

All closure types (manual CLOSED, FALSE_POSITIVE, and auto-closed) behave identically for:

  • Campaign linking
  • Sequence counting
  • New incident creation
  • Analyst closures do not break the campaign chain

Incident Lifecycle

Status transitions during an active incident:

flowchart LR
    OPEN["<b>OPEN</b>"] --> IP["<b>IN_PROGRESS</b>"]
    IP --> CLOSED["<b>CLOSED</b>"]
    IP --> FP["<b>FALSE_POSITIVE</b>"]

    style OPEN fill:#e65100,stroke:#ff9800,color:#fff
    style IP fill:#0d47a1,stroke:#42a5f5,color:#fff
    style CLOSED fill:#1b5e20,stroke:#66bb6a,color:#fff
    style FP fill:#6a1b9a,stroke:#ab47bc,color:#fff
  • OPEN → IN_PROGRESS: Analyst assigns the incident
  • IN_PROGRESS → CLOSED: Threat confirmed and remediated
  • IN_PROGRESS → FALSE_POSITIVE: Determined to be benign
  • OPEN → CLOSED: Auto-closed after 24h inactivity + no open threat cases

What happens after closure:

flowchart LR
    CLOSED["<b>CLOSED</b><br>manual or auto"] --> GAP{"Gap since<br>closure?"}
    GAP -->|"< 7 days"| SAME["New Incident<br><b>Same Campaign</b>"]
    GAP -->|"> 7 days"| NEW["New Incident<br><b>New Campaign</b>"]
    GAP -->|"No new activity"| DONE["Stays Closed"]

    style CLOSED fill:#1b5e20,stroke:#66bb6a,color:#fff
    style GAP fill:#00695c,stroke:#4db6ac,color:#fff
    style SAME fill:#b71c1c,stroke:#ef5350,color:#fff
    style NEW fill:#e65100,stroke:#ff9800,color:#fff
    style DONE fill:#37474f,stroke:#78909c,color:#fff

New activity after closure always creates a new incident with a new ID — never reopens the old one. The old incident stays closed as part of the audit trail.


Incident Identity

Each incident is uniquely identified by WHO (actor) + WHERE (entity) — not by timestamps or attack types. One actor per entity = one open incident at a time.

Field What it determines
Incident ID INC-{hash(actor_key + actor_type + entity_name + sequence)} — deterministic, stable
Sequence Counts all previous CLOSED + FALSE_POSITIVE incidents for this actor + entity — increments on each new episode
Actor The user, host, or IP being investigated
Entity The customer site/tenant — ensures MSSP isolation
Attack Type All active case types combined (e.g., APT_CHAIN,PASSWORD_SPRAYING) — updated each sync cycle
Campaign ID Links related incidents across time within the same entity

MSSP Entity Isolation

In MSSP environments, the same username (e.g., administrator) may exist across multiple customer entities. The system treats each as a completely separate actor:

flowchart LR
    A1["<b>administrator</b><br>Site A"] --> INC1["INC-52e728f0<br>OPEN · Risk 85"]
    A2["<b>administrator</b><br>Site B"] --> INC2["INC-2c72b927<br>OPEN · Risk 72"]
    A3["<b>administrator</b><br>Site C"] --> INC3["No incident<br>Risk 45 · Triage only"]

    style A1 fill:#0d47a1,stroke:#42a5f5,color:#fff
    style A2 fill:#7b1fa2,stroke:#ce93d8,color:#fff
    style A3 fill:#37474f,stroke:#78909c,color:#fff
    style INC1 fill:#b71c1c,stroke:#ef5350,color:#fff
    style INC2 fill:#e65100,stroke:#ff9800,color:#fff
    style INC3 fill:#37474f,stroke:#78909c,color:#fff

Entity scoping applies to: incident creation, open-incident lookup, sequence counting, campaign linking, and auto-close. Closing an incident for administrator at Site A has zero effect on administrator at Site B.

Single-entity customers: Entity scoping is transparent — all data belongs to one entity, so it behaves as if scoping doesn't exist. No special configuration needed.


What the Incident Contains

The incident record is a lightweight tracking wrapper. Most data is queried live from source tables when the incident is viewed.

Stored in Incident Record Queried Live from Source Tables
incident_id, campaign_id All alarms (alarm_actors)
actor_key, actor_type, entity_name All sigma detections (sigma_alerts)
attack_type (comma-separated) All threat cases (threat_cases)
status (OPEN / IN_PROGRESS / CLOSED / FALSE_POSITIVE) Current risk score (actor_risk_summary)
first_alarm_time, last_alarm_time MITRE tactics & techniques
assigned_analyst, investigation_notes Raw logs and timeline
Resolution note (e.g., "Auto-closed...")
flowchart LR
    AA["<b>alarm_actors</b><br>SIEM alarms"] --> DETAIL["<b>Incident Detail Page</b><br>Shows ALL current data"]
    SA["<b>sigma_alerts</b><br>RhythmX detections"] --> DETAIL
    TC["<b>threat_cases</b><br>Correlated cases"] --> DETAIL
    ARS2["<b>actor_risk_summary</b><br>7-factor score"] --> DETAIL
    META["<b>Incident Record</b><br>ID · status · campaign"] --> DETAIL

    style AA fill:#0d47a1,stroke:#42a5f5,color:#fff
    style SA fill:#7b1fa2,stroke:#ce93d8,color:#fff
    style TC fill:#b71c1c,stroke:#ef5350,color:#fff
    style ARS2 fill:#4a148c,stroke:#9c27b0,color:#fff
    style META fill:#37474f,stroke:#78909c,color:#fff
    style DETAIL fill:#1b5e20,stroke:#66bb6a,color:#fff

Benefit: New alarms, detections, threat cases, and risk scores appear immediately — no need to refresh or rebuild the incident.


Campaign Linking

Campaigns group related incidents for the same actor to show attack progression over time.

Campaign Rule (applied when creating a new incident):

  1. Look at the most recent closed incident for this actor + entity
  2. If it was closed within the last 7 days (configurable: INCIDENT_CAMPAIGN_LINK_DAYS) → reuse the same campaign_id
  3. If the gap is more than 7 days → create a new campaign_id

All closure types (manual CLOSED, FALSE_POSITIVE, and auto-closed) are treated identically. Analyst manual closures do not break the campaign chain.

Campaign Example

Campaign CMP-123 — SERVER01 Compromise:

flowchart LR
    INC1["<b>BRUTE_FORCE</b><br>Day 1–3 · Closed (auto)"] -->|"2 day gap"| INC2["<b>LATERAL_MOVEMENT</b><br>Day 5–8 · Closed (manual)"] -->|"1 day gap"| INC3["<b>RANSOMWARE</b><br>Day 9–now · Open"]

    style INC1 fill:#1b5e20,stroke:#66bb6a,color:#fff
    style INC2 fill:#1b5e20,stroke:#66bb6a,color:#fff
    style INC3 fill:#b71c1c,stroke:#ef5350,color:#fff
Day Event Incident Status Closure Type Campaign
1 Brute force detected INC-aaa created (OPEN) OPEN CMP-123
3 No activity 24h INC-aaa → CLOSED CLOSED Auto CMP-123
5 Lateral movement detected INC-bbb created (OPEN) OPEN CMP-123 (linked)
8 Analyst closes INC-bbb → CLOSED CLOSED Manual CMP-123
10 Ransomware attempt INC-ccc created (OPEN) OPEN CMP-123 (linked)
20 New unrelated activity INC-ddd created (OPEN) OPEN CMP-456 (new — gap > 7 days)

Analysts see the full attack progression (brute force → lateral movement → ransomware) grouped under one campaign, even though it spans multiple incident records. The auto-closure on Day 3 and the manual closure on Day 8 are treated identically — neither breaks the campaign chain.


Campaign Timeline

gantt
    title Campaign CMP-123 — SERVER01 Compromise
    dateFormat  YYYY-MM-DD
    section Brute Force
    INC-aaa (CLOSED auto)     :done, bf, 2026-03-01, 3d
    section Lateral Movement
    INC-bbb (CLOSED manual)   :done, lm, 2026-03-05, 4d
    section Ransomware
    INC-ccc (OPEN)            :active, rw, 2026-03-09, 5d

Incident Detail View

When an analyst clicks an incident, the detail page shows:

Risk Intelligence Panel

  • Risk Score with 7-factor breakdown (RhythmX / SIEM / Combined attribution)
  • Attack Shape — Multi-Stage Compromise, Focused Persistent Attack, Broad Reconnaissance, etc.
  • Confidence Level — HIGH / MEDIUM / LOW with reasoning
  • Primary Driver — which factor contributed most
  • Recommended Action — context-aware next step

Activity Data (Live)

  • LogRhythm Alarms — grouped by rule, with raw log drill-down
  • RhythmX Detections — grouped by rule, with ML outlier indicators
  • Threat Cases — correlated attack patterns with severity and status
  • MITRE ATT&CK Coverage — tactics and techniques as color-coded badges
  • Timeline — first alert to last alert with duration

Timeframe Control

A dropdown on the detail page lets analysts scope the data:

  • Incident Window (default) — only data between first and last alarm
  • 1h / 4h / 12h / 1d / 3d / 7d / 14d / 30d / 90d — expand or narrow as needed

Analyst Actions

Action Description
Assign Analyst Assign the incident to yourself or another team member
Change Status Move through the lifecycle: OPEN → IN_PROGRESS → CLOSED / FALSE_POSITIVE
Add Investigation Notes Document findings during the investigation
Add Resolution Notes Document conclusions and remediation steps at close
Create External Ticket Push the incident to Jira or ServiceNow with full risk context
Link External Ticket Attach an existing external ticket reference (ID and URL)
Bulk Status Update Select multiple incidents and change their status in a single action
View History See the full audit trail — every status change, assignment, and note update
AI Triage Run AI-powered analysis of the actor's behavior and risk factors

External Ticket Integration

From the incident detail view, analysts can push incidents to Jira or ServiceNow. The ticket includes:

  • Actor information and entity
  • Risk score with full 7-factor breakdown
  • MITRE ATT&CK tactics and techniques
  • SLA status and target
  • Alert rules triggered (up to 10, with overflow count)
  • Investigation notes (if present)

Priority mapping:

Risk Level Jira Priority ServiceNow Priority
Critical Highest 1
High High 2
Medium Medium 3
Low Low 4

Auto-Push: When enabled, incidents meeting the severity threshold are automatically pushed to Jira/ServiceNow on creation. Failed pushes are retried up to 5 times via the retry queue.


SLA Tracking

SLA timer starts when the incident is created:

flowchart LR
    C["<b>Critical ≥ 80</b><br>2 hours"] --> STATUS["On Track · At Risk · Breached · Resolved"]
    H["<b>High 60–79</b><br>4 hours"] --> STATUS
    M["<b>Medium 40–59</b><br>24 hours"] --> STATUS
    L["<b>Low < 40</b><br>48 hours"] --> STATUS

    style C fill:#b71c1c,stroke:#ef5350,color:#fff
    style H fill:#e65100,stroke:#ff9800,color:#fff
    style M fill:#f9a825,stroke:#ffee58,color:#000
    style L fill:#1b5e20,stroke:#66bb6a,color:#fff
    style STATUS fill:#37474f,stroke:#78909c,color:#fff
  • On Track — Within the target window
  • At Risk — 75%+ of the target time has elapsed
  • Breached — Target time exceeded
  • Resolved — Incident closed within the target

System State Model

Every incident has a system_state (machine-owned) and an analyst_status (human-owned). These are independent dimensions — the system manages threat progression while the analyst manages their workflow.

System States

State Condition Ticket Action
MONITORING 1 threat case AND risk < 40 No ticket created
ACTIVE risk ≥ 40 OR cases > 1 No ticket created
ESCALATED risk ≥ 70 OR (cases > 1 AND risk ≥ 40) Ticket created after stability check
RESOLVED No activity for 24h + no open cases Ticket closed

State Transitions with Cooldown

Escalation is fast (catch attacks quickly). De-escalation is slow (don't drop urgency prematurely).

Transition Cooldown Direction
MONITORING → ACTIVE 5 minutes Escalation (fast)
ACTIVE → ESCALATED 10 minutes Escalation (fast)
ESCALATED → ACTIVE 10 minutes De-escalation (slow)
ACTIVE → MONITORING 15 minutes De-escalation (slow)
MONITORING → RESOLVED 24 hours Closure

Incident Time Classification

Every incident is continuously classified by activity recency:

Type Condition Behavior
ACTIVE Last activity < 1 hour Full escalation + ticket
RECENT Last activity < 24h OR open threat cases with activity < 48h Ticket with "monitor closely" warning
HISTORICAL Last activity ≥ 24h, no recent threat cases Ticket skipped — prevents noise

Attack Trend

Each incident tracks risk trajectory:

Trend Condition Meaning
ACCELERATING Risk increased by 5+ since last cycle Threat is growing
COOLING Risk decreased by 5+ Threat is subsiding
STABLE Risk roughly unchanged No significant change

Ticket Sync (Jira / ServiceNow)

Event Ticket Action
ESCALATED + stable (2 cycles) Create ticket (skip if HISTORICAL)
Risk level changes Update ticket priority via API (not just comment)
State de-escalates Add comment with reason
Re-escalation while analyst IN_PROGRESS Add comment with RE-ESCALATION warning
RESOLVED Close ticket (SNOW state=7 / Jira comment)

Ticket Priority Mapping

Risk Score Base Priority With HIGH/CRITICAL Confidence
≥ 80 P1 (Critical) P1 (Critical)
60 – 79 P2 (High) P1 (Critical) — confidence boost
40 – 59 P3 (Medium) P3 (Medium)
< 40 P4 (Low) P4 (Low)

Ownership Lock

When an analyst sets status to IN_PROGRESS, the system activates an ownership lock:

  • State downgrade blocked — system will NOT move ESCALATED → ACTIVE
  • Auto-close blocked — system will NOT move to RESOLVED
  • Upgrades allowed — system CAN still escalate (adds RE-ESCALATION comment to ticket)
  • Analyst must close manually when investigation is complete

Analyst Status (Independent)

Status Meaning Who sets it
OPEN New, unassigned System (on creation)
IN_PROGRESS Analyst is investigating Analyst
CLOSED Investigation complete Analyst or system (auto-close if OPEN + RESOLVED)
FALSE_POSITIVE Not a real threat Analyst

Scenario Walkthrough — Path A (Threat Case)

A user triggers a brute force detection. The system creates an incident, monitors it, and escalates as the attack progresses.

flowchart TD
    T0["<b>Minute 0</b><br>Threat case fires: Brute Force<br>Risk: 39 · 1 case · 10 sigma alerts"] --> INC["<b>Incident Created</b><br>Path A — threat case exists<br>State: MONITORING<br>Type: ACTIVE<br>Trend: ➡ STABLE"]

    INC --> T2["<b>Minute 2</b><br>More sigma alerts + LR alarms arrive<br>Risk climbs to 55"]
    T2 --> ST1["State: MONITORING → <b>ACTIVE</b><br><i>(5 min cooldown starts)</i>"]

    ST1 --> T7["<b>Minute 7</b><br>Cooldown passed<br>2nd threat case: Lateral Movement<br>Risk: 65"]
    T7 --> ST2["State: <b>ACTIVE</b><br>Trend: ⬆ ACCELERATING<br><i>No ticket yet</i>"]

    ST2 --> T12["<b>Minute 12</b><br>3rd threat case: Persistence<br>Risk: 76 · 3 cases · 50 sigma + 200 LR alarms"]
    T12 --> ST3["State: ACTIVE → <b>ESCALATED</b><br><i>(10 min cooldown starts)</i>"]

    ST3 --> T22["<b>Minute 22</b><br>Cooldown passed · Stability check:<br>Cycle 1/2 — wait"]
    T22 --> T24["<b>Minute 24</b><br>Stability check:<br>Cycle 2/2 — <b>STABLE</b>"]

    T24 --> TICKET["<b>Ticket Created</b><br>SNOW/Jira<br>Priority: P1 (risk 76 + HIGH confidence)<br>Primary Driver: 3 threat cases<br>Attack Trend: ⬆ ACCELERATING<br>Activity Status: ACTIVE"]

    TICKET --> SYNC["<b>Ongoing Sync</b><br>Every 5 min:<br>· Risk changes → update priority<br>· New cases → add comment<br>· Analyst sets IN_PROGRESS → ownership lock"]

    SYNC --> T24H["<b>24 hours later</b><br>No new activity · All cases closed<br>Risk dropped to 25"]
    T24H --> RES["State: <b>RESOLVED</b><br>SNOW ticket closed<br>analyst_status: CLOSED (auto)"]

    style T0 fill:#37474f,stroke:#78909c,color:#fff
    style INC fill:#546e7a,stroke:#90a4ae,color:#fff
    style T2 fill:#e65100,stroke:#ff9800,color:#fff
    style ST1 fill:#f9a825,stroke:#ffee58,color:#000
    style T7 fill:#ef6c00,stroke:#ffb74d,color:#fff
    style ST2 fill:#f57f17,stroke:#fff176,color:#000
    style T12 fill:#d84315,stroke:#ff7043,color:#fff
    style ST3 fill:#c62828,stroke:#ef9a9a,color:#fff
    style T22 fill:#b71c1c,stroke:#ef5350,color:#fff
    style T24 fill:#b71c1c,stroke:#ef5350,color:#fff
    style TICKET fill:#0d47a1,stroke:#42a5f5,color:#fff
    style SYNC fill:#1a237e,stroke:#534bae,color:#fff
    style T24H fill:#2e7d32,stroke:#81c784,color:#fff
    style RES fill:#1b5e20,stroke:#66bb6a,color:#fff

Risk Score Progression (Path A)

Time Threat Cases Sigma Alerts LR Alarms Risk Score State
Min 0 1 (Brute Force) 10 0 39 MONITORING
Min 2 1 25 50 55 ACTIVE
Min 7 2 (+Lateral Movement) 35 120 65 ACTIVE
Min 12 3 (+Persistence) 50 200 76 ESCALATED
Min 24 3 50 200 76 ESCALATED → Ticket
+24h 0 (all closed) 50 200 25 RESOLVED

Scenario Walkthrough — Path B (Risk Score Only)

A host generates high volume of diverse detections but no threat cases. Risk crosses 70 from sigma + SIEM signals alone.

flowchart TD
    T0["<b>Hour 0</b><br>Host: srv-web-01<br>50 sigma alerts · 500 LR alarms<br>8 unique rules · 4 MITRE tactics<br>Risk: 72 · 0 threat cases"] --> INC["<b>Incident Created</b><br>Path B — risk ≥ 70<br>State: ESCALATED<br>Type: ACTIVE<br>Trend: ➡ STABLE"]

    INC --> STAB["<b>+10 min</b><br>Stability check: 2/2 — STABLE"]
    STAB --> TICKET["<b>Ticket Created</b><br>SNOW/Jira<br>Priority: P2 (risk 72, MEDIUM confidence)<br>Primary Driver: Risk Score 72/100<br>Activity Status: ACTIVE"]

    TICKET --> T4H["<b>+4 hours</b><br>More alerts arrive · Risk: 85<br>Confidence: HIGH"]
    T4H --> PRI["<b>Priority Updated</b><br>P2 → P1 (risk 85 + HIGH confidence)<br>SNOW API PATCH"]

    PRI --> T12H["<b>+12 hours</b><br>Activity slows · Risk: 58"]
    T12H --> DEESC["State: ESCALATED → <b>ACTIVE</b><br><i>(15 min cooldown passed)</i><br>Comment added to ticket"]

    DEESC --> T48H["<b>+48 hours</b><br>No new activity · Risk: 30"]
    T48H --> RES["State: <b>RESOLVED</b><br>SNOW ticket closed"]

    style T0 fill:#e65100,stroke:#ff9800,color:#fff
    style INC fill:#c62828,stroke:#ef9a9a,color:#fff
    style STAB fill:#b71c1c,stroke:#ef5350,color:#fff
    style TICKET fill:#0d47a1,stroke:#42a5f5,color:#fff
    style T4H fill:#d84315,stroke:#ff7043,color:#fff
    style PRI fill:#4a148c,stroke:#9c27b0,color:#fff
    style T12H fill:#f9a825,stroke:#ffee58,color:#000
    style DEESC fill:#f57f17,stroke:#fff176,color:#000
    style T48H fill:#2e7d32,stroke:#81c784,color:#fff
    style RES fill:#1b5e20,stroke:#66bb6a,color:#fff

Risk Score Progression (Path B)

Time Threat Cases Sigma Alerts LR Alarms Risk Score State Ticket
Hour 0 0 50 500 72 ESCALATED
+10 min 0 50 500 72 ESCALATED Created (P2)
+4 hours 0 80 800 85 ESCALATED Priority → P1
+12 hours 0 80 800 58 ACTIVE Comment added
+48 hours 0 80 800 30 RESOLVED Closed

Scenario — HISTORICAL Skip (No False Ticket)

An actor had high risk 3 days ago but is no longer active. The system creates an incident but does NOT generate a ticket.

flowchart TD
    T0["<b>3 days ago</b><br>Risk was 74 · 2 threat cases<br>Activity stopped"] --> TODAY["<b>Today</b><br>Risk still 72 (from cached data)<br>No new activity · Cases still open"]

    TODAY --> INC["<b>Incident exists</b><br>State: ESCALATED<br>Type: <b>HISTORICAL</b><br><i>(last activity > 24h, cases open but stale > 48h)</i>"]

    INC --> STAB["Stability check passes"]
    STAB --> SKIP["<b>TICKET SKIPPED</b><br>Log: 'Skipping auto-ticket — HISTORICAL'<br><i>No noise for analyst</i>"]

    SKIP --> LATER["<b>Later — new activity appears</b><br>Fresh sigma alerts · Risk: 78"]
    LATER --> RECLASS["Type: HISTORICAL → <b>ACTIVE</b><br>Trend: ⬆ ACCELERATING"]
    RECLASS --> TICKET["<b>NOW ticket is created</b><br>Because incident is ACTIVE again"]

    style T0 fill:#37474f,stroke:#78909c,color:#fff
    style TODAY fill:#546e7a,stroke:#90a4ae,color:#fff
    style INC fill:#78909c,stroke:#b0bec5,color:#fff
    style STAB fill:#78909c,stroke:#b0bec5,color:#fff
    style SKIP fill:#1b5e20,stroke:#66bb6a,color:#fff
    style LATER fill:#e65100,stroke:#ff9800,color:#fff
    style RECLASS fill:#d84315,stroke:#ff7043,color:#fff
    style TICKET fill:#0d47a1,stroke:#42a5f5,color:#fff

Scenario — Ownership Lock (Analyst Protection)

An analyst is actively investigating. The system protects their workflow.

flowchart TD
    ESC["<b>Incident ESCALATED</b><br>Risk: 78 · Ticket: SNOW-1234"] --> ANALYST["<b>Analyst sets IN_PROGRESS</b><br>Ownership lock activates"]

    ANALYST --> DROP["<b>Risk drops to 55</b><br>System wants: ESCALATED → ACTIVE"]
    DROP --> BLOCK["<b>BLOCKED</b><br>Ownership lock prevents downgrade<br>State stays: ESCALATED<br><i>Analyst is working, don't interfere</i>"]

    ANALYST --> SPIKE["<b>Risk spikes to 92</b><br>New threat case appears"]
    SPIKE --> REESC["<b>RE-ESCALATION</b><br>Comment added to SNOW-1234:<br>'⚠️ Risk increased to 92 while<br>analyst is investigating'<br>Priority: P2 → P1"]

    ANALYST --> CLOSE["<b>Analyst closes investigation</b><br>analyst_status: CLOSED<br>Ownership lock released"]

    style ESC fill:#b71c1c,stroke:#ef5350,color:#fff
    style ANALYST fill:#0d47a1,stroke:#42a5f5,color:#fff
    style DROP fill:#f9a825,stroke:#ffee58,color:#000
    style BLOCK fill:#1b5e20,stroke:#66bb6a,color:#fff
    style SPIKE fill:#d84315,stroke:#ff7043,color:#fff
    style REESC fill:#c62828,stroke:#ef9a9a,color:#fff
    style CLOSE fill:#2e7d32,stroke:#81c784,color:#fff

Configuration

All settings are configurable in .env.production:

Setting Default Description
INCIDENT_SYNC_ENABLED true Enable/disable incident sync service
INCIDENT_MIN_RISK_SCORE 70 Minimum risk score for Path B qualification
INCIDENT_RISK_FRESHNESS_HOURS 24 Legacy — no longer used (recency is in the risk score)
INCIDENT_THREAT_CASE_DAYS 30 Threat case lookback window (days)
INCIDENT_AUTO_CLOSE_HOURS 24 Auto-close after this many hours of inactivity
INCIDENT_CAMPAIGN_LINK_DAYS 7 Link incidents within this window to same campaign
AUTO_PUSH_MIN_SEVERITY CRITICAL Minimum severity for auto-push to Jira/ServiceNow
RETRY_MAX_ATTEMPTS 5 Max retry attempts for failed ticket pushes
FEED_RATE_LIMIT 120/minute External API rate limit per API key