{
  "id": "gcQL6CuHB17OzWiN",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "[Template] AI Powered Incident Management",
  "tags": [],
  "nodes": [
    {
      "id": "334458ec-a24e-4033-a286-ac3dc8521d5f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3840,
        2256
      ],
      "parameters": {
        "color": 2,
        "width": 4416,
        "height": 1392,
        "content": "# Ingestion Pipeline\n\nRun these pipelines ONCE before the first demo, and again whenever source data changes.\n\nEach pipeline is independent and can be run separately.\n\nRecommended order:\n  1) Ingest Resolved Incidents\n  2) Ingest Reference Playbooks\n  3) Ingest Test Incidents"
      },
      "typeVersion": 1
    },
    {
      "id": "97c4f761-febf-4338-a1fd-f58cdabe82b7",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2896,
        2448
      ],
      "parameters": {
        "color": 4,
        "width": 448,
        "height": 256,
        "content": "## 1) Ingest Resolved Incidents\n\nSource:    GitHub (Resolved Incidents/)\nTarget:    resolved_incidents_v1 (vector table)\nEmbedding: Gemini models/gemini-embedding-001\n\nRe-run after adding or updating resolved\nincident JSON files."
      },
      "typeVersion": 1
    },
    {
      "id": "ca2fb3f1-393e-42e7-9fea-6f41aeb2e542",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1216,
        2768
      ],
      "parameters": {
        "color": 4,
        "width": 448,
        "height": 176,
        "content": "## 2) Ingest Reference Playbooks\n\nSource:    GitHub (Reference Playbooks/)\nTarget:    reference_playbooks_v1 (vector table)\nEmbedding: Gemini models/gemini-embedding-001\n\nRe-run after adding new .md playbook files\n(e.g. ransomware, IAM credential compromise)."
      },
      "typeVersion": 1
    },
    {
      "id": "9d63fedc-75ea-4715-bdda-29bd4fffe380",
      "name": "Resolved Incidents Data Loader",
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "position": [
        -2448,
        2624
      ],
      "parameters": {
        "options": {
          "metadata": {
            "metadataValues": [
              {
                "name": "=incident_id",
                "value": "={{ $('Prepare Markdown Document & Metadata').item.json.metadata.incident_id }}"
              },
              {
                "name": "severity",
                "value": "={{ $('Prepare Markdown Document & Metadata').item.json.metadata.severity }}"
              },
              {
                "name": "category",
                "value": "={{ $('Prepare Markdown Document & Metadata').item.json.metadata.category }}"
              },
              {
                "name": "affected_systems",
                "value": "={{ $('Prepare Markdown Document & Metadata').item.json.metadata.affected_systems }}"
              },
              {
                "name": "mitre_tactic_ids",
                "value": "={{ $json.metadata.mitre_tactic_ids }}"
              },
              {
                "name": "mitre_technique_ids",
                "value": "={{ $json.metadata.mitre_technique_ids }}"
              },
              {
                "name": "mitre_prevention_ids",
                "value": "={{ $json.metadata.mitre_prevention_ids }}"
              },
              {
                "name": "@timestamp",
                "value": "={{ $json.metadata['@timestamp'] }}"
              },
              {
                "name": "description",
                "value": "={{ $json.metadata.description }}"
              },
              {
                "name": "alert_source",
                "value": "={{ $json.metadata.alert_source }}"
              },
              {
                "name": "entities",
                "value": "={{ $json.metadata.entities }}"
              },
              {
                "name": "detection_gap",
                "value": "={{ $json.metadata.detection_gap }}"
              },
              {
                "name": "what_went_well",
                "value": "={{ $json.metadata.what_went_well }}"
              },
              {
                "name": "what_went_wrong",
                "value": "={{ $json.metadata.what_went_wrong }}"
              }
            ]
          }
        },
        "jsonData": "={{ $('Prepare Markdown Document & Metadata').item.json.pageContent }}",
        "jsonMode": "expressionData"
      },
      "typeVersion": 1
    },
    {
      "id": "bbb9c6c0-c0aa-472e-9db0-17529fb339c8",
      "name": "Gemini Embeddings (Resolved Incidents)",
      "type": "@n8n/n8n-nodes-langchain.embeddingsGoogleGemini",
      "position": [
        -2160,
        2624
      ],
      "parameters": {
        "modelName": "={{ $('Config-Ingestion').item.json.model_embedding }}"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "58223e31-4441-4b14-870c-732dcb6cb456",
      "name": "Get Reference Playbooks From GitHub",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2304,
        3008
      ],
      "parameters": {
        "url": "={{ $('Config-Ingestion').item.json.github_baseUrl + '/' + $('Config-Ingestion').item.json.github_repo + '/contents/Reference%20Playbooks' }}",
        "options": {}
      },
      "executeOnce": true,
      "retryOnFail": true,
      "typeVersion": 4.4
    },
    {
      "id": "1a6ccefd-01be-4a85-9d01-cbbd74bdf968",
      "name": "Get Resolved Incidents from GitHub",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3344,
        3008
      ],
      "parameters": {
        "url": "={{ $('Config-Ingestion').item.json.github_baseUrl + '/' + $('Config-Ingestion').item.json.github_repo + '/contents/Resolved%20Incidents' }}",
        "options": {}
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "74f99ed4-9051-4dbf-aad8-34f2cc376a78",
      "name": "Download Reference Playbooks From GitHub",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1952,
        2944
      ],
      "parameters": {
        "url": "={{$json[\"download_url\"]}}",
        "options": {}
      },
      "retryOnFail": true,
      "typeVersion": 4.4
    },
    {
      "id": "f3783cca-59f7-4803-a57f-be13ffc82945",
      "name": "Download Resolved Incidents From GitHub",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3120,
        3008
      ],
      "parameters": {
        "url": "={{$json[\"download_url\"]}}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4
    },
    {
      "id": "5ff4ebbc-e18c-4c67-9d05-cfd1ca08305c",
      "name": "Prepare Markdown Document & Metadata",
      "type": "n8n-nodes-base.code",
      "position": [
        -2896,
        3008
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const data = $json;\n\n// -----------------------------\n// Metadata (STRICT \u2013 Only Requested Fields)\n// -----------------------------\nconst metadata = {\n  \"@timestamp\": data[\"@timestamp\"] ?? null,\n  incident_id: data.incident_id ?? null,\n  severity: data.severity ?? null,\n  category: data.category ?? null,\n  type: data.type ?? null,\n\n  affected_systems: Array.isArray(data.affected_systems)\n    ? data.affected_systems\n    : [],\n\n  mitre_tactic_ids: Array.isArray(data.mitre_tactic_ids)\n    ? data.mitre_tactic_ids\n    : [],\n\n  mitre_technique_ids: Array.isArray(data.mitre_technique_ids)\n    ? data.mitre_technique_ids\n    : [],\n\n  mitre_prevention_ids: Array.isArray(data.mitre_prevention_ids)\n    ? data.mitre_prevention_ids\n    : [],\n\n  // \u2705 New fields retained\n  description: data.description ?? null,\n  alert_source: data.alert_source ?? null,\n  rule_name: data.rule_name ?? null,\n  entities: typeof data.entities === 'object' ? data.entities : {},\n  detection_gap: data.detection_gap ?? null,\n  what_went_well: Array.isArray(data.what_went_well) ? data.what_went_well : [],\n  what_went_wrong: Array.isArray(data.what_went_wrong) ? data.what_went_wrong : []\n};\n\n// Remove null/undefined only\nObject.keys(metadata).forEach(key => {\n  if (metadata[key] === undefined || metadata[key] === null) {\n    delete metadata[key];\n  }\n});\n\n// -----------------------------\n// Markdown Content\n// -----------------------------\nconst sections = [];\n\n// Header\nsections.push(`# Incident: ${data.title ?? \"Untitled Incident\"}`);\nsections.push(`**Incident ID:** ${data.incident_id ?? \"N/A\"}`);\nsections.push(`**Severity:** ${data.severity ?? \"N/A\"}`);\nsections.push(`**Category:** ${data.category ?? \"N/A\"}`);\nsections.push(`**Type:** ${data.type ?? \"N/A\"}`);\nsections.push(`**Status:** ${data.status ?? \"N/A\"}`);\nsections.push(`**Date:** ${data[\"@timestamp\"] ?? \"N/A\"}`);\n\nif (Array.isArray(data.affected_systems) && data.affected_systems.length > 0) {\n  sections.push(`**Affected Systems:** ${data.affected_systems.join(\", \")}`);\n}\n\n// Alert Source\nif (data.alert_source) {\n  sections.push(\"\");\n  sections.push(\"## Alert Source\");\n  sections.push(`**Detection Tool:** ${data.alert_source}`);\n  if (data.rule_name) {\n    sections.push(`**Rule/Finding:** ${data.rule_name}`);\n  }\n}\n\n// Entities (clean + safe)\nconst entities = metadata.entities;\n\nif (\n  entities &&\n  typeof entities === 'object' &&\n  Object.keys(entities).length > 0\n) {\n  const entityLines = [];\n\n  if (Array.isArray(entities.users) && entities.users.length > 0)\n    entityLines.push(`**Users:** ${entities.users.join(', ')}`);\n\n  if (Array.isArray(entities.ips) && entities.ips.length > 0)\n    entityLines.push(`**IPs:** ${entities.ips.join(', ')}`);\n\n  if (Array.isArray(entities.hosts) && entities.hosts.length > 0)\n    entityLines.push(`**Hosts:** ${entities.hosts.join(', ')}`);\n\n  if (Array.isArray(entities.domains) && entities.domains.length > 0)\n    entityLines.push(`**Domains:** ${entities.domains.join(', ')}`);\n\n  if (Array.isArray(entities.hashes) && entities.hashes.length > 0)\n    entityLines.push(`**Hashes:** ${entities.hashes.join(', ')}`);\n\n  if (entityLines.length > 0) {\n    sections.push(\"\");\n    sections.push(\"## Entities\");\n    entityLines.forEach(l => sections.push(l));\n  }\n}\n\n// Description\nif (data.description) {\n  sections.push(\"\");\n  sections.push(\"## Description\");\n  sections.push(data.description);\n}\n\n// Detection Gap\nif (data.detection_gap) {\n  sections.push(\"\");\n  sections.push(\"## Detection Gap\");\n  sections.push(data.detection_gap);\n}\n\n// Timeline (safe)\nif (Array.isArray(data.timeline) && data.timeline.length > 0) {\n  sections.push(\"\");\n  sections.push(\"## Timeline\");\n\n  data.timeline.slice(0, 50).forEach(t => {\n    if (t && t.timestamp && t.event) {\n      sections.push(`- ${t.timestamp} \u2014 ${t.event}`);\n    }\n  });\n}\n\n// Root Cause\nif (data.root_cause_analysis) {\n  sections.push(\"\");\n  sections.push(\"## Root Cause\");\n  sections.push(data.root_cause_analysis);\n}\n\n// What Went Well\nif (Array.isArray(data.what_went_well) && data.what_went_well.length > 0) {\n  sections.push(\"\");\n  sections.push(\"## What Went Well\");\n  data.what_went_well.forEach(item => sections.push(`- ${item}`));\n}\n\n// What Went Wrong\nif (Array.isArray(data.what_went_wrong) && data.what_went_wrong.length > 0) {\n  sections.push(\"\");\n  sections.push(\"## What Went Wrong\");\n  data.what_went_wrong.forEach(item => sections.push(`- ${item}`));\n}\n\n// Resolution Summary\nif (data.resolution_summary) {\n  sections.push(\"\");\n  sections.push(\"## Resolution Summary\");\n  sections.push(data.resolution_summary);\n}\n\n// Remediation Actions\nif (Array.isArray(data.remediation_actions) && data.remediation_actions.length > 0) {\n  sections.push(\"\");\n  sections.push(\"## Remediation Actions\");\n  data.remediation_actions.forEach(action => {\n    sections.push(`- ${action}`);\n  });\n}\n\n// Lessons Learned\nif (data.lessons_learned) {\n  sections.push(\"\");\n  sections.push(\"## Lessons Learned\");\n  sections.push(data.lessons_learned);\n}\n\n// MITRE Mapping (safe)\nconst mitre = [];\n\nif (Array.isArray(metadata.mitre_tactic_ids) && metadata.mitre_tactic_ids.length > 0)\n  mitre.push(`Tactics: ${metadata.mitre_tactic_ids.join(\", \")}`);\n\nif (Array.isArray(metadata.mitre_technique_ids) && metadata.mitre_technique_ids.length > 0)\n  mitre.push(`Techniques: ${metadata.mitre_technique_ids.join(\", \")}`);\n\nif (Array.isArray(metadata.mitre_prevention_ids) && metadata.mitre_prevention_ids.length > 0)\n  mitre.push(`Prevention: ${metadata.mitre_prevention_ids.join(\", \")}`);\n\nif (mitre.length > 0) {\n  sections.push(\"\");\n  sections.push(\"## MITRE Mapping\");\n  sections.push(mitre.join(\"\\n\"));\n}\n\n// Final Output\nconst pageContent = sections.join(\"\\n\");\n\nreturn {\n  json: {\n    pageContent,\n    metadata\n  }\n};"
      },
      "executeOnce": false,
      "retryOnFail": true,
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "2afcc845-1303-42f9-931d-baeb068b840e",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3840,
        3760
      ],
      "parameters": {
        "color": 5,
        "width": 4624,
        "height": 1584,
        "content": "# Retrieval & Response Workflow\n\nThis is the main pipeline. It processes one test incident\nend-to-end through three parallel intelligence branches\nand produces a structured JSON response report.\n\nFull flow:\n  Step 1 --> Fetch Incident from Supabase\n  Step 2 --> Parallel Retrieval (3 branches simultaneously)\n  Step 3 --> Merge All Intelligence\n  Step 4 --> Synthesize Report\n  Step 5 --> Validate Output (If node)\n  Step 6 --> Write to Supabase (or skip if degraded)\n\nConfigure the incident to process in the Set Incident ID node."
      },
      "typeVersion": 1
    },
    {
      "id": "63cb4b2d-249d-4c9e-bb1c-cc37315b1ade",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        2768
      ],
      "parameters": {
        "color": 4,
        "width": 416,
        "height": 176,
        "content": "## 3) Ingest Test Incidents\n\nSource:    GitHub (Test Incidents/)\nTarget:    test_incidents_v1 (flat table)\n\nRe-run after updating test incident JSON files.\nClears and re-inserts all 13 rows."
      },
      "typeVersion": 1
    },
    {
      "id": "45073f01-633f-4f16-9648-9ea80c422179",
      "name": "Create a Row of Test Incident on Supabase",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -208,
        3008
      ],
      "parameters": {
        "tableId": "={{ $('Config-Ingestion').item.json.table_test }}",
        "dataToSend": "autoMapInputData"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "87c836df-fb31-4a6e-8b34-ba61f937c11e",
      "name": "Gemini Embeddings (Playbooks Ingestion)",
      "type": "@n8n/n8n-nodes-langchain.embeddingsGoogleGemini",
      "position": [
        -864,
        3232
      ],
      "parameters": {
        "modelName": "={{ $('Config-Ingestion').item.json.model_embedding }}"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "73966a1f-24b6-44db-ae8e-0d5aab353f79",
      "name": "Reference Playbooks Data Loader",
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "position": [
        -1152,
        3232
      ],
      "parameters": {
        "options": {
          "metadata": {
            "metadataValues": [
              {
                "name": "name",
                "value": "={{ $json.name }}"
              },
              {
                "name": "full_text",
                "value": "={{ $json.data }}"
              }
            ]
          }
        },
        "jsonData": "={{ $json.text }}",
        "jsonMode": "expressionData"
      },
      "typeVersion": 1
    },
    {
      "id": "a51aa36c-26f0-4d75-a68a-f09405f768e9",
      "name": "Merge Ingestion Branches",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1376,
        2992
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition",
        "numberInputs": 3
      },
      "typeVersion": 3.2
    },
    {
      "id": "ef1cc9cb-d519-474c-a64e-90f00e8909e4",
      "name": "Summarize Reference Playbooks for Embeddings",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -1728,
        2816
      ],
      "parameters": {
        "text": "={{ $json.data }}",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "You are a cybersecurity retrieval optimization engine.\n\nYour task is to convert an incident response playbook into a trigger-optimized semantic summary for vector similarity search.\n\nThis summary is not for human readability.\nIt is for maximizing retrieval accuracy.\n\nYour output must:\n\n1. Clearly define the PRIMARY trigger scenario:\n   - What exact incident pattern activates this playbook?\n   - What detection signals or log patterns indicate it?\n\n2. Explicitly describe distinguishing characteristics:\n   - How this scenario differs from related attack types.\n   - What makes this playbook unique compared to similar ones.\n\n3. Include:\n   - MITRE ATT&CK IDs and tactics.\n   - Common detection signals (SIEM, EDR, Cloud logs, IdP logs).\n   - Platform scope (AWS, Azure AD, VPN, S3, Email, etc.).\n   - Likely attacker behaviors.\n   - Alternative terminology and analyst search phrases.\n\n4. Include boundary conditions:\n   - When this playbook should NOT be used.\n   - Common false positives.\n\n5. Avoid:\n   - Step-by-step remediation instructions.\n   - Escalation workflow details.\n   - Version history.\n   - Ownership metadata.\n\n6. Keep output between 300\u2013450 words.\n7. Use dense paragraphs.\n8. Do not use markdown formatting.\n9. End the summary with a compact comma-separated list of retrieval keywords."
            }
          ]
        },
        "promptType": "define"
      },
      "retryOnFail": true,
      "typeVersion": 1.9
    },
    {
      "id": "c6e5fc5a-63c9-4f36-bc3d-6a449a47e89d",
      "name": "Get Test Incidents From GitHub",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -656,
        3008
      ],
      "parameters": {
        "url": "={{ $('Config-Ingestion').item.json.github_baseUrl + '/' + $('Config-Ingestion').item.json.github_repo + '/contents/Test%20Incidents' }}",
        "options": {}
      },
      "executeOnce": true,
      "retryOnFail": true,
      "typeVersion": 4.4
    },
    {
      "id": "8b803ad3-521d-4a34-9d00-abd7777fa003",
      "name": "Download Test Incidents From GitHub",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -432,
        3008
      ],
      "parameters": {
        "url": "={{$json[\"download_url\"]}}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4
    },
    {
      "id": "5e43ac58-af0e-488b-9a9e-a926e8fcfa50",
      "name": "Threat Intel Enrichment Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "onError": "continueErrorOutput",
      "position": [
        -1824,
        4880
      ],
      "parameters": {
        "text": "={{ JSON.stringify($json, null, 2) }}",
        "options": {
          "systemMessage": "# ROLE\nYou are a Real-Time Threat Intelligence & Technical Enrichment Agent.\n\nYou augment incident analysis by performing structured, targeted research using external sources.\nYou do NOT generate the final report. You produce structured research findings for a downstream Synthesizer LLM.\n\n# OBJECTIVE\n\nGiven an incident, determine whether external research is necessary and gather:\n- Active threat intelligence and named campaigns\n- CVE information (exact IDs only \u2014 never invent)\n- Vendor advisories and patch status\n- Exploit availability and active exploitation status\n- Actionable, phase-labeled mitigation guidance\n- Concrete IOCs (hashes, IPs, registry keys, process names) where available\n\nOnly search when it adds value.\n\n# STEP 1 \u2014 CLASSIFY THE INCIDENT TYPE (DO THIS FIRST)\n\nBefore deciding whether to search, classify the incident into one of these types:\n\n| Type | Examples | Search Behavior |\n|---|---|---|\n| SECURITY_ATTACK | exfiltration, ransomware, phishing, credential leak, C2, injection, malware | ALWAYS search |\n| SECURITY_VULNERABILITY | CVE detected, Snyk finding, vulnerable dependency | ALWAYS search \u2014 CVE-first |\n| CLOUD_SECURITY | AWS/Azure/GCP alert, IAM anomaly, S3 access | ALWAYS search |\n| POLICY_VIOLATION | unauthorized software, AUP breach, T1219 | NARROW search only (tooling/uninstall scripts) |\n| AVAILABILITY | memory leak, replication failure, OOM, 500 errors with no exploit evidence | DO NOT search unless MITRE attack technique is present |\n| PERFORMANCE | CPU spike, query slowdown, timeout | DO NOT search |\n\nCRITICAL: A metric spike (HTTP 500 errors, high request rate) is NOT evidence of exploitation unless:\n- A specific CVE pattern is present in the incident data, OR\n- The MITRE technique maps to a known attack (not just error handling), OR\n- A security tool (GuardDuty, EDR, SIEM) has flagged it as malicious\n\nIf in doubt, classify as AVAILABILITY and do NOT search.\n\n# STEP 2 \u2014 SEARCH DECISION RULES\n\n## ALWAYS search when incident type is SECURITY_ATTACK or SECURITY_VULNERABILITY:\n- CVE ID explicitly mentioned in incident data or alert\n- Named threat actor or campaign referenced\n- Active exploitation signals (exfiltration volume, GuardDuty finding, EDR alert)\n- Cloud provider security alert (AWS, Azure, GCP)\n- MITRE ATT&CK technique classified as attack-type (T1059, T1190, T1486, T1530, etc.)\n\n## NARROW search when incident type is POLICY_VIOLATION:\n- Search ONLY for: tool-specific removal commands, network signatures, Group Policy config\n- Do NOT search for threat actors, CVEs, or campaigns\n- Maximum 3 searches\n\n## DO NOT search when:\n- Incident type is AVAILABILITY with no MITRE attack technique\n- Incident type is PERFORMANCE\n- Information is already definitive\n\n## Maximum searches: 10 (3 for POLICY_VIOLATION)\n\n# STEP 3 \u2014 CVE SOURCE HIERARCHY (MANDATORY)\n\nWhen including a CVE card, sources MUST be used in this priority order:\n\n1. MSRC \u2014 `msrc.microsoft.com` (for Microsoft CVEs)\n2. NVD \u2014 `nvd.nist.gov/vuln/detail/CVE-XXXX-XXXXX` (for all CVEs)\n3. Vendor security page \u2014 e.g. `postgresql.org/support/security/`, `apache.org/security/`\n4. CISA KEV \u2014 `cisa.gov/known-exploited-vulnerabilities`\n5. Security research blog \u2014 only if none of the above cover this CVE\n\nA CVE card using a blog as its ONLY source must include `\"applicability_confidence\": \"low\"`.\n\nIf a CVE is NOT explicitly referenced in the incident data:\n- Set `\"applicability_confidence\": \"medium\"` or `\"low\"` with explanation in `applicability_notes`\n- Do NOT set `exploitation_status: \"active\"` unless the CVE is directly correlated to the incident\n\n# STEP 4 \u2014 MITIGATION GUIDANCE RULES\n\n## Phase Labels (REQUIRED on every action)\nEvery action's `phase` field MUST be one of:\n\n- `IMMEDIATE` \u2014 Act within 15 minutes (block, isolate, revoke credentials)\n- `CONTAINMENT` \u2014 Stop the spread (disable shares, quarantine endpoint, network segment)\n- `DETECTION` \u2014 Identify scope (query logs, run forensic commands, check GuardDuty)\n- `INVESTIGATION` \u2014 Determine root cause (Athena queries, heap dumps, git history)\n- `HARDENING` \u2014 Prevent recurrence (policy changes, MFA, patching, WAF rules)\n- `RECOVERY` \u2014 Restore service (promote replica, restore from backup, patch and restart)\n\n## Count Cap: Maximum 6 steps. Prioritize IMMEDIATE and CONTAINMENT first.\n\n## Source Rules:\n- Every step should have a `source_url` where an authoritative source exists.\n- `source_url: null` only for generic first-responder steps with no specific authoritative source.\n- Prefer: official vendor docs > CISA advisories > authoritative security research > vendor blogs\n\n# STEP 5 \u2014 IOC EXTRACTION\n\nExtract concrete, specific IOCs from your research into the `ioc_indicators` array:\n- File hashes (MD5, SHA256)\n- Malicious IP ranges or C2 domain patterns\n- Registry keys used for persistence\n- Specific process names or command strings\n- Port numbers used for C2\n- Ransom note filenames or file extensions\n\nIf no specific IOCs found: set `ioc_indicators: []`\n\n# CRITICAL RULES\n- Zero hallucination: Never invent CVE IDs, version numbers, dates, tool names, threat actor names, or operation names.\n- Date format: Use ISO-8601 `YYYY-MM-DD` or `YYYY-MM` only. Year-only strings like `\"2025\"` are NOT allowed \u2014 use `null` if month is unknown.\n- sources_consulted: Must ONLY list URLs that appear as `source_url` in threat_intelligence or mitigation_guidance. No ghost citations.\n- Category rules:\n  - `\"CVE\"` \u2014 only for entries with a real CVE ID (CVE-YYYY-NNNNN format)\n  - `\"Active Campaign\"` \u2014 named, documented threat actor operations\n  - `\"Vendor Advisory\"` \u2014 official vendor security bulletins\n  - `\"Known Issue\"` \u2014 documented vulnerability patterns without CVE\n  - `\"Exploit Activity\"` \u2014 documented exploitation methodology\n- active_exploitation_observed: Set `true` only if at least one card has `exploitation_status: \"active\"` AND the threat is directly relevant to the incident type.\n- Source deduplication: Do not include two cards from the same source URL.\n- Keep summaries concise: Max 3 sentences per summary. No narrative paragraphs.\n- Output must be valid JSON matching the provided schema.\n- Evidence priority: Vendor documentation > CISA/NVD > security blogs > forums."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "retryOnFail": true,
      "typeVersion": 3.1
    },
    {
      "id": "68606b1b-a948-4304-b238-b790498cc750",
      "name": "Search in Tavily",
      "type": "@tavily/n8n-nodes-tavily.tavilyTool",
      "position": [
        -1424,
        5136
      ],
      "parameters": {
        "query": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Query', ``, 'string') }}",
        "options": {}
      },
      "credentials": {
        "tavilyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "85fcf85d-32d4-4b1c-9bfe-671150345aba",
      "name": "Gemini Embeddings (Historical RAG)",
      "type": "@n8n/n8n-nodes-langchain.embeddingsGoogleGemini",
      "position": [
        -1760,
        4144
      ],
      "parameters": {
        "modelName": "={{ $('Config-Ingestion').item.json.model_embedding }}"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "6528e1d6-81d7-49ed-9fea-a189ff6d3967",
      "name": "Historical Incidents RAG Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "onError": "continueErrorOutput",
      "position": [
        -1840,
        3824
      ],
      "parameters": {
        "text": "=test_incident: {{ JSON.stringify($('Webhook').item.json.body, null, 2) }}",
        "options": {
          "systemMessage": "You are a Historical Incident Intelligence Agent.\n\nINPUT:\n- test_incident: the raw alert JSON from the current security incident.\n\nTOOL:\n- Use the Supabase Vector Store tool to retrieve similar past resolved incidents via semantic similarity.\n\nDATA FORMAT \u2014 Critical, read before extracting anything:\nEach retrieved result is a single text block of Markdown (pageContent). There is no separate metadata object. All fields must be extracted from the Markdown text by scanning for section headers.\n\nThe Markdown structure is:\n- Header block contains: Incident ID, Severity, Category, Type, Date, Affected Systems\n- ## Alert Source \u2014 contains Detection Tool and Rule/Finding\n- ## Description \u2014 raw alert description\n- ## Detection Gap \u2014 why the alert was not caught sooner (may be absent)\n- ## Root Cause \u2014 root cause analysis text\n- ## What Went Well \u2014 bullet list of positive response actions\n- ## What Went Wrong \u2014 bullet list of response failures\n- ## Resolution Summary \u2014 how the incident was resolved\n- ## Remediation Actions \u2014 bullet list of specific actions taken\n- ## Lessons Learned \u2014 post-incident insight\n- ## MITRE Mapping \u2014 Tactics, Techniques, Prevention IDs\n\nExtraction rules:\n- Parse the incident_id from the line \"**Incident ID:** [value]\" in the header block.\n- Parse severity from \"**Severity:** [value]\", type from \"**Type:** [value]\".\n- For mitre_techniques_used: parse the \"Techniques: [list]\" line under ## MITRE Mapping.\n- For sections that are absent: return null for string fields, [] for array fields.\n\nTASK:\n1. Use the Supabase Vector Store tool to retrieve similar past resolved incidents.\n2. Analyze the retrieved incidents and extract structured historical intelligence.\n\nDo NOT write a triage note.\nDo NOT write a final report.\nDo NOT generate narrative paragraphs.\nYour job is to extract and synthesize patterns from past incidents in structured JSON form only.\n\n---------------------------------\nANALYSIS REQUIREMENTS\n---------------------------------\n\nYou MUST output ONLY valid JSON matching the schema enforced by the output parser. Do not add any explanation text outside the JSON.\n\nIntelligence synthesis rules:\nsimilarity_search_performed: Always true.\n\nmatch_quality:\n  \"strong\"   \u2192 top result similarity >= 0.80\n  \"moderate\" \u2192 top result similarity >= 0.60\n  \"weak\"     \u2192 top result similarity < 0.60\n  \"none\"     \u2192 0 results returned\n\nincidents_retrieved: For EACH retrieved incident, extract and return:\n  incident_id: parse from \"**Incident ID:** [value]\" in the header block\n  similarity_score: similarity float, rounded to 3 decimal places\n  incident_type: parse from \"**Type:** [value]\" in the header block\n  severity: parse from \"**Severity:** [value]\" in the header block\n  resolution_time_minutes: parse from content if mentioned (e.g., \"resolved in 45 minutes\"), else null\n  root_cause: 1\u20132 sentence summary from the \"## Root Cause\" section\n  resolution_summary: 1\u20132 sentence summary from the \"## Resolution Summary\" section\n  key_remediation_actions: top 3 most specific/actionable bullet items from \"## Remediation Actions\"\n  lessons_learned: 1 sentence from the \"## Lessons Learned\" section, or null if absent\n  mitre_techniques_used: parse the \"Techniques: [T1xxx, T1xxx]\" line under \"## MITRE Mapping\", return as array\n  detection_gap: full text from the \"## Detection Gap\" section, or null if that section is absent\n  what_went_well: array of bullet items from the \"## What Went Well\" section, or [] if absent\n  what_went_wrong: array of bullet items from the \"## What Went Wrong\" section, or [] if absent\n\nhistorical_patterns: Cross-incident synthesis across ALL retrieved incidents:\n  most_common_root_cause: the root cause pattern repeating most often (1\u20132 sentences)\n  avg_resolution_time_minutes: arithmetic mean of all non-null resolution_time_minutes, or null\n  proven_remediation_steps: the 3\u20135 remediation actions appearing most consistently across incidents\n  common_mitre_techniques: deduplicated list of all MITRE technique IDs across all retrieved incidents\n  recurring_detection_gaps: array \u2014 summarize detection failures that appeared in multiple incidents. Empty array if none found.\n  what_went_well_patterns: array \u2014 1\u20132 positive response patterns that historically minimized impact. Empty array if none.\n  what_went_wrong_patterns: array \u2014 1\u20132 negative response patterns that historically worsened outcomes. Empty array if none.\n\ncontextual_relevance: How well retrieved incidents match the CURRENT test incident:\n  overlap_summary: 1\u20132 sentences on specific overlaps (same type? same MITRE tactic? same affected system?)\n  key_differentiators: 1\u20132 sentences on what is DIFFERENT about the test incident vs historical cases\n  confidence: \"high\" / \"medium\" / \"low\" \u2014 overall confidence that historical patterns apply to this incident\n\nfallback_note: If 0 results returned, explain that no matches were found and suggest a broader search. If results exist, set to null."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "retryOnFail": true,
      "typeVersion": 1.7
    },
    {
      "id": "08ef03eb-41ae-437a-b174-4df59197923c",
      "name": "Resolved Incidents Vector Store",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
      "position": [
        -1760,
        4016
      ],
      "parameters": {
        "mode": "retrieve-as-tool",
        "topK": 3,
        "options": {
          "queryName": "={{ $('Config-Ingestion').item.json.query_incidents }}"
        },
        "tableName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').item.json.table_resolved }}"
        },
        "toolDescription": "Search for past incidents using semantic similarity.",
        "includeDocumentMetadata": false
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "b8dddfdb-3587-48da-83bf-90ee946da31a",
      "name": "Playbook Routing Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "maxTries": 2,
      "position": [
        -1824,
        4320
      ],
      "parameters": {
        "text": "={{ JSON.stringify($('Code in JavaScript').item.json.body, null, 2) }}",
        "options": {
          "systemMessage": "You are an Incident Response Playbook Routing Agent.\n\nYour responsibility is to determine whether any retrieved reference playbook is a valid and appropriate match for the current security incident.\n\nPROCESS:\n\nCall the \"Supabase Vector Store\" tool using the incident\u2019s core symptoms, detection signals, affected systems, and MITRE context as your search query.\n\nReview up to 3 retrieved candidate playbooks.\n\nEvaluate whether the playbook\u2019s trigger conditions, attack vector, and operational scope truly match the incident.\n\nMATCHING RULES:\n\nThe attack vector must align (e.g., email phishing, identity brute force, S3 data exfiltration, etc.).\n\nThe scope must align (identity layer vs storage layer vs endpoint).\n\nPrefer the most specific playbook.\n\nDo not select a playbook if the match is weak or generic.\n\nIf uncertain, return no match.\n\nOUTPUT FORMAT (STRICT JSON ONLY). No additional commentary."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "retryOnFail": true,
      "typeVersion": 1.7,
      "waitBetweenTries": 3000
    },
    {
      "id": "43fba87f-2e66-4a57-8a23-dfb0e41891dc",
      "name": "Reference Playbooks Vector Store",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
      "position": [
        -1648,
        4544
      ],
      "parameters": {
        "mode": "retrieve-as-tool",
        "topK": 3,
        "options": {
          "queryName": "={{ $('Config-Ingestion').item.json.query_playbooks }}"
        },
        "tableName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').item.json.table_playbooks }}"
        },
        "toolDescription": "Use this tool to retrieve candidate incident response reference playbooks using semantic similarity against the current incident.\n\nPass a concise description of the incident\u2019s core symptoms, attack vectors, detection signals, and affected systems as the search query.\n\nThe tool returns up to 3 candidate playbooks ranked by similarity. Each result includes:\n\nA semantic summary (content)\nPlaybook uuid and name inside metadata\n\nIMPORTANT: Retrieved playbooks are only candidates. You must evaluate whether their trigger conditions and scope truly match the current incident before selecting one."
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "cf9b40e4-9fa4-4fcf-9f94-a4f8ae8df30c",
      "name": "Gemini Embeddings (Playbooks)",
      "type": "@n8n/n8n-nodes-langchain.embeddingsGoogleGemini",
      "position": [
        -1776,
        4672
      ],
      "parameters": {
        "modelName": "={{ $('Config').item.json.model_embedding }}"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "64d89692-4547-4320-82ca-3abd3c1ed46a",
      "name": "Structured Output Parser - Playbook Router",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -1360,
        4544
      ],
      "parameters": {
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"match_found\": {\n      \"type\": \"boolean\",\n      \"description\": \"Indicates whether a relevant reference playbook was identified.\"\n    },\n    \"playbook_id\": {\n      \"type\": [\"string\", \"null\"],\n      \"description\": \"UUID of the selected playbook. Null if no match.\"\n    },\n    \"playbook_name\": {\n      \"type\": [\"string\", \"null\"],\n      \"description\": \"Exact name of the selected playbook. Null if no match.\"\n    },\n    \"confidence\": {\n      \"type\": \"string\",\n      \"enum\": [\"high\", \"medium\", \"low\"]\n    },\n    \"reasoning\": {\n      \"type\": \"string\"\n    }\n  },\n  \"required\": [\n    \"match_found\",\n    \"playbook_id\",\n    \"playbook_name\",\n    \"confidence\",\n    \"reasoning\"\n  ],\n  \"additionalProperties\": false\n}"
      },
      "retryOnFail": true,
      "typeVersion": 1.3
    },
    {
      "id": "04302602-1d53-4d05-a68c-43df10e28c54",
      "name": "Playbook Text Extractor",
      "type": "n8n-nodes-base.set",
      "position": [
        -1136,
        4320
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "186d6db5-5244-404d-9ce4-6b51034997f5",
              "name": "playbook_text",
              "type": "string",
              "value": "={{ $('Playbook Routing Agent').item.json.output.match_found ? $json.metadata.full_text : `# Incident Response Playbook: Generic Enterprise Security Incident Response\n\n> **Document Version:** 1.0\n> **Date:** 2026-02-27\n> **Classification:** INTERNAL \u2013 SOC Operations\n> **Review Cycle:** Quarterly\n> **Applies When:** No technique-specific reference playbook is matched for the incident type.\n\n---\n\n## Title & Use Case ID\n\n| Field | Value |\n|---|---|\n| **Playbook Title** | Generic Enterprise Security Incident Response |\n| **Use Case ID** | GEN-UNIVERSAL-001 |\n| **Scope** | All incident categories: Security, Application, Infrastructure, Identity, Availability |\n| **Frameworks** | NIST SP 800-61r2, MITRE ATT&CK Enterprise, CIS Controls v8 |\n| **Version** | 1.0 |\n| **Last Updated** | 2026-02-27 |\n| **Owner** | SOC Manager / Incident Response Lead |\n\n---\n\n## MITRE ATT&CK Mapping\n\n> This playbook is technique-agnostic. Specific MITRE techniques will be identified during triage. Common starting points by incident category:\n\n| Incident Category | Common Tactics | Common Techniques to Investigate |\n|---|---|---|\n| **Identity / Credential** | Initial Access, Credential Access, Persistence | T1078 (Valid Accounts), T1110 (Brute Force), T1098 (Account Manipulation) |\n| **Application / Vulnerability** | Initial Access, Execution | T1190 (Exploit Public-Facing Application), T1059 (Command & Scripting Interpreter) |\n| **Data Exfiltration** | Collection, Exfiltration | T1530 (Data from Cloud Storage), T1020 (Automated Exfiltration) |\n| **Malware / Ransomware** | Execution, Impact | T1486 (Data Encrypted for Impact), T1059 (Command & Scripting Interpreter) |\n| **Insider / Policy Violation** | Collection, Exfiltration | T1039 (Data from Network Shared Drive), T1048 (Exfiltration Over Alt Protocol) |\n| **Infrastructure / Availability** | Impact | T1498 (Network DoS), T1499 (Endpoint DoS) |\n| **Phishing** | Initial Access | T1566 (Phishing), T1078 (Valid Accounts post-compromise) |\n\n> Once the specific technique is confirmed during triage, refer to a technique-specific playbook if one exists.\n\n---\n\n## Objective Statement\n\nThis generic playbook is activated when no technique-specific playbook matches the incoming alert. It provides a **universal incident response lifecycle** applicable to any security, application, infrastructure, or availability incident at an enterprise organisation. The goal is to rapidly determine whether the alert represents malicious activity or a benign anomaly; if malicious, to assess the blast radius, contain the threat, eradicate the root cause, restore systems to normal operation, and capture lessons learned \u2014 all while preserving forensic evidence and meeting SLA obligations.\n\n---\n\n## Alert Analysis\n\n### Alert Triggers (Universal)\n\n- SIEM correlation rule fires on anomalous activity pattern\n- Security tool detection (EDR, XDR, CSPM, WAF, IDS/IPS, DLP) raises alert\n- Automated monitoring detects threshold breach (error rate, authentication failures, traffic spike, data volume anomaly)\n- Threat intelligence platform matches IOC against live traffic or logs\n- User or system administrator reports suspicious behaviour\n- External notification (partner, ISP, law enforcement, bug bounty)\n- Vulnerability scanner or SCA tool identifies a critical finding in production\n\n### Detection Logic / Data Sources\n\n| Data Source | What to Query |\n|---|---|\n| SIEM | Correlated alert details: rule name, triggering events, involved assets and accounts |\n| EDR / XDR | Process trees, file system changes, network connections, lateral movement indicators |\n| Authentication Logs (AD/Entra ID/IdP) | Failed/successful logins, MFA events, account lockouts, session anomalies |\n| Network / Firewall / WAF Logs | Inbound/outbound connections, blocked requests, traffic volume anomalies |\n| Application / Web Logs | HTTP error codes, request volume spikes, API abuse patterns |\n| Cloud Platform Logs (CloudTrail/GCP Audit/Azure Activity) | API calls, configuration changes, IAM activity, resource access |\n| Vulnerability / SCA Scanner Output | CVE identifiers, affected library versions, CVSS scores |\n| DLP / CASB | Data movement alerts, sensitive data access outside normal patterns |\n| Threat Intelligence Platform | IOC lookups: IPs, domains, hashes, CVEs |\n\n---\n\n## Initial Analyst Checklist\n\n> **Note:** Steps marked with \ud83e\udd16 are candidates for SOAR/workflow automation.\n\n- [ ] \ud83e\udd16 **Acknowledge and log the alert** within SLA window; assign incident ticket with severity label\n- [ ] \ud83e\udd16 **Identify the primary affected assets**: hosts, accounts, cloud resources, applications, services\n- [ ] \ud83e\udd16 **Identify the responsible actor(s)**: source IP, user identity, service account, process, or IAM principal\n- [ ] \ud83e\udd16 **Enrich identified IPs and hashes**: query threat intelligence (VirusTotal, AbuseIPDB, GreyNoise) for known malicious indicators\n- [ ] **Determine the earliest known timestamp** of anomalous activity (the \"patient zero\" event)\n- [ ] **Identify the alert type and category**: Security / Application / Infrastructure / Availability / Identity\n- [ ] **Assess initial false-positive likelihood**: Is this activity consistent with a known scheduled job, maintenance window, test activity, or misconfiguration?\n- [ ] **Determine privilege level of involved accounts**: Standard user / Privileged admin / Service account / Shared account\n- [ ] **Identify whether sensitive data is involved**: PII, PHI, financial data, credentials, intellectual property\n- [ ] **Check for lateral movement indicators**: are multiple systems or accounts involved beyond the initial alert?\n- [ ] **Assign severity** per the Severity Classification Matrix below\n- [ ] **Document all findings in the incident ticket** with timestamps, evidence sources, and reasoning\n\n---\n\n## Indicators of Compromise (IOC) Checklist\n\n| IOC Type | Value | Source | Verified? |\n|---|---|---|---|\n| Source IP Address(es) | | Logs / Alerts | \u2610 |\n| Geo-location / ASN of Source IP | | GeoIP / WHOIS | \u2610 |\n| User Account(s) / IAM Principals Involved | | Auth Logs / SIEM | \u2610 |\n| Hostname(s) / Device(s) Affected | | EDR / SIEM | \u2610 |\n| Malware Hash (MD5/SHA256) | | EDR / AV | \u2610 |\n| Malicious Domain / URL | | Web / Proxy Logs | \u2610 |\n| CVE Identifier(s) | | Scanner / External Feed | \u2610 |\n| Anomalous Process Name or Command Line | | EDR | \u2610 |\n| Suspicious File Path or Registry Key | | EDR | \u2610 |\n| Attacked Service / Port / Endpoint | | Firewall / WAF / App Logs | \u2610 |\n| Timeframe of Anomalous Activity | | SIEM | \u2610 |\n| Data Volume Affected (if exfiltration suspected) | | DLP / Network Logs | \u2610 |\n\n---\n\n## Severity Classification Matrix\n\n| Severity | Criteria | Response SLA |\n|---|---|---|\n| **Critical (P1)** | Confirmed compromise of privileged/admin accounts; confirmed data exfiltration of sensitive data; active ransomware or destructive attack; business-critical system unavailable; lateral movement confirmed; regulatory breach threshold likely met | Immediate escalation. Incident bridge within 15 min. |\n| **High (P2)** | Confirmed compromise of standard user account; exploitation of public-facing vulnerability with evidence of code execution; significant service degradation; MFA disabled or bypassed; sensitive data at risk but not confirmed exfiltrated | Escalate to Tier 2 within 15 min. Containment within 1 hour. |\n| **Medium (P3)** | Suspicious activity with no confirmed compromise; policy violation with limited data exposure; single failed exploitation attempt; anomalous access pattern that is not yet confirmed malicious | Triage within 30 min. Investigate and monitor. |\n| **Low (P4)** | Low-fidelity alert; high likelihood of false positive; known benign tool or scanner; no sensitive data involved; single user/system with no escalation indicators | Acknowledge within 1 hour. Verify and close or tune detection rule. |\n\n---\n\n## Triage Steps\n\n1. **Validate the alert is not a false positive**:\n   - Does the activity match a known, approved change, maintenance window, or scheduled job?\n   - Is the source IP or account associated with an authorised penetration test?\n   - Is this a misconfigured service or application generating noise (e.g., stale credentials, expired token)?\n   - Cross-reference with the change management system: was anything deployed or modified recently that could explain this behaviour?\n\n2. **Characterise the incident type**:\n   - **Security (Attacker-driven)**: malicious external or insider activity \u2014 prioritise containment\n   - **Application (Vulnerability/Bug)**: unpatched vulnerability, dependency, or misconfiguration being exploited \u2014 prioritise patch/fix\n   - **Infrastructure (Availability)**: system failure, resource exhaustion, or connectivity issue \u2014 prioritise restoration\n   - **Identity (Account compromise/misconfiguration)**: credential attack or over-permissive access \u2014 prioritise credential revocation\n\n3. **Determine the blast radius**:\n   - How many systems, accounts, or services are affected?\n   - Is the impact isolated or expanding?\n   - What data classifications are at risk?\n   - Is there evidence the attacker has achieved their objective, or are they still active?\n\n4. **Map the MITRE ATT&CK stages** present in the evidence:\n   - Initial Access \u2192 Execution \u2192 Persistence \u2192 Privilege Escalation \u2192 Lateral Movement \u2192 Collection \u2192 Exfiltration \u2192 Impact\n   - Determine at which stage the attacker is (or was) operating\n\n5. **Assign severity** per the matrix above and escalate accordingly\n\n---\n\n## De-escalated and Expected Benign Events\n\nThe following should be classified as **false positive / benign** and closed after verification:\n\n- **Scheduled automation or batch job**: High-volume API calls, database queries, or file transfers from a known service account during an expected time window. Action: document as baseline event; tune detection threshold.\n- **Authorised change or deployment**: Configuration change, access policy update, or software deployment performed via approved change management. Action: verify ticket reference; close with change ID documented.\n- **Known security scanner or pen test**: Source IP confirmed as authorised internal scanner (Nessus, Snyk, Qualys) or pre-approved penetration test. Action: verify authorisation; add to allowlist; close.\n- **User error / application misconfiguration**: Expired credentials, rotated API keys not yet updated in config, password manager sync issue. Action: assist with remediation; close.\n- **Benign internet scanner (GreyNoise RIOT)**: Source IP is a known harmless research scanner. Action: add to allowlist if recurring; close.\n- **Monitoring or observability tool behaviour**: Legitimate health-check probes, load balancer checks, or synthetic monitoring generating alerts. Action: tune detection to exclude known patterns; close.\n\n---\n\n## Escalation of Incident\n\n### Tier 1 \u2192 Tier 2 Escalation\n**Trigger**: Alert is confirmed as malicious (not false positive) OR severity is P1/P2 OR any of: confirmed system compromise, privileged account involved, sensitive data at risk, lateral movement evidence.\n\n**Actions**:\n- Assign to Tier 2 with full IOC checklist and initial scope assessment\n- Initiate containment steps immediately without waiting for Tier 2 acknowledgement if severity is High+\n- Document handover clearly: what is known, what is unknown, what actions have already been taken\n\n### Tier 2 \u2192 Tier 3 / IR Lead Escalation\n**Trigger**: Confirmed post-compromise activity (data access, privilege escalation, lateral movement, persistence established); business-critical system unavailable; attack is ongoing and spreading; initial containment insufficient.\n\n**Actions**:\n- Page IR Lead and CSIRT (Computer Security Incident Response Team)\n- Open incident bridge / war room\n- Begin forensic preservation of relevant logs (do not alter or delete evidence)\n- Engage relevant SMEs (IAM team, Cloud Security, Network Security, Application Security) per the contacts table below\n- Assess whether law enforcement or regulatory bodies need to be notified\n\n### Tier 3 \u2192 Executive / Legal / Regulatory Escalation\n**Trigger**: Confirmed exfiltration of PII/PHI/regulated data; ransom demand received; business-critical systems unavailable for >2 hours; confirmed state-sponsored or advanced persistent threat activity; regulatory breach notification obligation likely triggered.\n\n**Actions**:\n- Notify CISO within 1 hour\n- Engage Legal / DPO for breach assessment and notification obligations (GDPR, CCPA, HIPAA, etc.)\n- Engage Cyber Insurance carrier\n- Prepare internal and external communications if public-facing impact is possible\n- Consider engaging external forensic firm if internal capability is insufficient\n\n---\n\n## Containment Actions\n\n> **Critical**: Preserve forensic evidence BEFORE destructive containment steps. Capture memory, logs, and disk images from affected systems before isolation or remediation where possible.\n\n### Identity & Access Containment\n- **Disable or lock compromised accounts** immediately upon confirmation of compromise\n- **Revoke all active sessions** for compromised accounts (force sign-out from all devices)\n- **Rotate credentials**: Reset passwords and rotate API keys, tokens, and certificates associated with compromised identities\n- **Revoke MFA devices** for compromised accounts and require re-enrolment through a verified process\n- **Apply temporary deny-all policy** to compromised IAM roles/service principals\n\n### Network Containment\n- **Block malicious IPs/domains** at firewall, WAF, DNS resolver, and proxy levels\n- **Isolate highly affected systems** from the network (VLAN isolation or physical disconnection) if active attacker activity is ongoing\n- **Block outbound connections** on suspicious ports or protocols identified in the investigation\n\n### Application & System Containment\n- **Disable or remove exploited functionality** temporarily (e.g., disable a vulnerable API endpoint, remove a vulnerable plugin)\n- **Enable runtime protection or WAF rules** to block the specific exploit pattern while a patch is prepared\n- **Take snapshots of affected instances** before any remediation (for forensics)\n\n### Data Containment\n- **Restrict access to affected data stores** to read-only or specific approved principals only\n- **Enable additional logging and alerting** on the affected data resource immediately\n\n---\n\n## Eradication & Recovery\n\n1. **Confirm root cause**: Do not proceed to recovery until the root cause is fully understood and documented. Premature recovery without root cause analysis leads to reinfection.\n\n2. **Remove malicious artefacts**: Delete attacker-created accounts, API keys, scheduled tasks, cron jobs, startup items, registry keys, malware files, and any backdoors or persistence mechanisms discovered.\n\n3. **Patch or remediate the exploited vector**:\n   - Apply vendor security patches for identified CVEs\n   - Update vulnerable libraries or dependencies to non-vulnerable versions\n   - Fix the misconfiguration or insecure code that allowed the incident\n\n4. **Rotate all potentially exposed credentials**: Even if not confirmed compromised, rotate credentials for accounts and services that had access to the affected systems during the attack window.\n\n5. **Harden configurations**:\n   - Review and apply principle of least privilege to all accounts and services involved\n   - Remove unnecessary services, ports, and permissions\n   - Validate logging and monitoring coverage is complete before closure\n\n6. **Validate recovery**:\n   - Confirm affected systems are stable and operating normally\n   - Confirm no reoccurrence of attacker indicators in monitoring\n   - Confirm data integrity where data was at risk\n   - Obtain sign-off from data/system owner before formal closure\n\n7. **Update detection rules**: Add new IOCs (IPs, hashes, domains, signatures) to SIEM, EDR, and threat intelligence platform watchlists. Tune detection thresholds if alert was noisy.\n\n---\n\n## Automation Opportunities\n\n| Step | Automation Capability | Tool Example |\n|---|---|---|\n| Alert triage and enrichment (IP, hash, domain lookup) | Fully automatable | SOAR + TIP (VirusTotal, GreyNoise, AbuseIPDB) |\n| Asset and identity context enrichment | Fully automatable | SOAR + CMDB / IAM API |\n| IOC blocklist update (firewall, WAF, DNS) | Semi-automatable (approval for P1/P2) | SOAR + Firewall API / DNS RPZ |\n| Account lock / credential revocation | Semi-automatable (approval gate) | SOAR + IdP API |\n| Session revocation | Fully automatable | SOAR + IdP API |\n| Forensic snapshot of affected systems | Fully automatable | SOAR + Cloud Snapshot API / EDR |\n| Ticket creation, enrichment, and escalation routing | Fully automatable | SOAR + ITSM API |\n| Stakeholder notification emails | Fully automatable | SOAR email action |\n| IOC detonation in sandbox | Fully automatable | SOAR + Sandbox API (Any.run, Cuckoo) |\n| Post-patch verification scan | Fully automatable | SOAR + Scanner API (Snyk, Qualys, Nessus) |\n\n---\n\n## Lessons Learned / Post-Incident Review\n\nThe IR Lead should schedule a post-incident review within **5 business days** of closure. Address:\n\n- What was the root cause, and could it have been prevented with existing controls?\n- Were detection thresholds appropriately tuned? Was MTTD (Mean Time to Detect) within target?\n- Was MTTR (Mean Time to Respond) within SLA? If not, where were the delays?\n- Were the right people notified at the right time through the right escalation path?\n- Are there detection gaps that allowed the attacker to operate undetected for a period?\n- Did any forensic evidence get lost due to log retention gaps or destructive containment steps?\n- What automation could reduce manual effort in future similar incidents?\n- Does this incident indicate a systemic issue requiring a broader remediation programme?\n- Should this incident type now have a dedicated technique-specific playbook created?\n\nDocument all findings in the incident ticket. If this incident type is not covered by an existing reference playbook and will recur, assign a task to create one.\n\n---\n\n## Related Frameworks & References\n\n- NIST SP 800-61r2 \u2013 Computer Security Incident Handling Guide\n- NIST SP 800-137 \u2013 Information Security Continuous Monitoring\n- MITRE ATT&CK Enterprise \u2013 https://attack.mitre.org/\n- CIS Controls v8 \u2013 https://www.cisecurity.org/controls/\n- ISO/IEC 27035 \u2013 Information Security Incident Management\n- SANS Incident Handler's Handbook\n\n---\n\n## Revision History\n\n| Version | Date | Author | Changes |\n|---|---|---|---|\n| 1.0 | 2026-02-27 | [Author] | Initial release \u2013 generic fallback playbook |\n` }}"
            }
          ]
        }
      },
      "retryOnFail": true,
      "typeVersion": 3.4
    },
    {
      "id": "58ddc17d-af50-4c43-a2d0-1ebfb369e8a4",
      "name": "Structured Output Parser - Threat Intel",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -1776,
        5104
      ],
      "parameters": {
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"incident_classification\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"SECURITY_ATTACK\",\n        \"SECURITY_VULNERABILITY\",\n        \"CLOUD_SECURITY\",\n        \"POLICY_VIOLATION\",\n        \"AVAILABILITY\",\n        \"PERFORMANCE\"\n      ],\n      \"description\": \"Incident type classification determined in Step 1. Controls search behavior.\"\n    },\n    \"external_research_used\": {\n      \"type\": \"boolean\",\n      \"description\": \"Indicates whether external research was performed.\"\n    },\n    \"threat_intelligence\": {\n      \"type\": \"array\",\n      \"maxItems\": 6,\n      \"description\": \"Structured findings from CVEs, advisories, campaigns, or known issues. Maximum 6 items.\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"category\": {\n            \"type\": \"string\",\n            \"enum\": [\n              \"CVE\",\n              \"Vendor Advisory\",\n              \"Active Campaign\",\n              \"Known Issue\",\n              \"Exploit Activity\"\n            ],\n            \"description\": \"CVE only for real CVE-YYYY-NNNNN IDs. Active Campaign for named threat actors.\"\n          },\n          \"title\": { \"type\": \"string\" },\n          \"summary\": {\n            \"type\": \"string\",\n            \"description\": \"Max 3 sentences. Concise and technical. No narrative paragraphs.\"\n          },\n          \"affected_products\": { \"type\": [\"string\", \"null\"] },\n          \"affected_versions\": { \"type\": [\"string\", \"null\"] },\n          \"severity\": { \"type\": [\"string\", \"null\"] },\n          \"exploitation_status\": {\n            \"type\": [\"string\", \"null\"],\n            \"enum\": [\"active\", \"proof_of_concept\", \"theoretical\", null]\n          },\n          \"source_name\": { \"type\": \"string\" },\n          \"source_url\": { \"type\": \"string\" },\n          \"published_date\": {\n            \"type\": [\"string\", \"null\"],\n            \"description\": \"ISO-8601 YYYY-MM-DD or YYYY-MM only. Never year-only strings like '2025'. Use null if month unknown.\"\n          },\n          \"applicability_confidence\": {\n            \"type\": \"string\",\n            \"enum\": [\"high\", \"medium\", \"low\"],\n            \"description\": \"How directly this finding relates to the incident. high=exact product match or directly referenced. medium=same product family or technique class. low=general context only.\"\n          },\n          \"applicability_notes\": {\n            \"type\": [\"string\", \"null\"],\n            \"description\": \"Required when applicability_confidence is medium or low. Explain the relevance connection.\"\n          }\n        },\n        \"required\": [\n          \"category\",\n          \"title\",\n          \"summary\",\n          \"source_name\",\n          \"source_url\",\n          \"applicability_confidence\"\n        ],\n        \"additionalProperties\": false\n      }\n    },\n    \"ioc_indicators\": {\n      \"type\": \"array\",\n      \"description\": \"Concrete, specific indicators of compromise extracted from research. Empty array if none found.\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"type\": {\n            \"type\": \"string\",\n            \"enum\": [\n              \"file_hash\",\n              \"ip_address\",\n              \"domain\",\n              \"registry_key\",\n              \"process_name\",\n              \"file_extension\",\n              \"port\",\n              \"command_pattern\",\n              \"filename\"\n            ]\n          },\n          \"value\": { \"type\": \"string\" },\n          \"context\": {\n            \"type\": \"string\",\n            \"description\": \"What this IOC represents (e.g., 'TamperedChef info-stealer persistence registry key')\"\n          },\n          \"source_url\": { \"type\": \"string\" }\n        },\n        \"required\": [\"type\", \"value\", \"context\", \"source_url\"],\n        \"additionalProperties\": false\n      }\n    },\n    \"mitigation_guidance\": {\n      \"type\": \"array\",\n      \"maxItems\": 6,\n      \"description\": \"Phase-labeled, actionable remediation steps. Maximum 6. Prioritize IMMEDIATE and CONTAINMENT.\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"phase\": {\n            \"type\": \"string\",\n            \"enum\": [\n              \"IMMEDIATE\",\n              \"CONTAINMENT\",\n              \"DETECTION\",\n              \"INVESTIGATION\",\n              \"HARDENING\",\n              \"RECOVERY\"\n            ],\n            \"description\": \"IMMEDIATE=act within 15min. CONTAINMENT=stop spread. DETECTION=identify scope. INVESTIGATION=root cause. HARDENING=prevent recurrence. RECOVERY=restore service.\"\n          },\n          \"action\": {\n            \"type\": \"string\",\n            \"description\": \"Specific, actionable step. Include exact commands, paths, query strings, or API calls where available.\"\n          },\n          \"source_name\": { \"type\": [\"string\", \"null\"] },\n          \"source_url\": { \"type\": [\"string\", \"null\"] }\n        },\n        \"required\": [\"phase\", \"action\"],\n        \"additionalProperties\": false\n      }\n    },\n    \"emerging_risk_assessment\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"active_exploitation_observed\": {\n          \"type\": [\"boolean\", \"null\"],\n          \"description\": \"true only if exploitation_status=active AND threat is directly relevant to this specific incident type. Not true for unrelated active campaigns.\"\n        },\n        \"risk_trend\": {\n          \"type\": \"string\",\n          \"enum\": [\"increasing\", \"stable\", \"decreasing\", \"unknown\"]\n        },\n        \"confidence\": {\n          \"type\": \"string\",\n          \"enum\": [\"high\", \"medium\", \"low\"]\n        },\n        \"notes\": {\n          \"type\": \"string\",\n          \"description\": \"Max 4 sentences. Summarize threat landscape context and incident-specific risk factors.\"\n        }\n      },\n      \"required\": [\n        \"active_exploitation_observed\",\n        \"risk_trend\",\n        \"confidence\",\n        \"notes\"\n      ],\n      \"additionalProperties\": false\n    },\n    \"sources_consulted\": {\n      \"type\": \"array\",\n      \"description\": \"MUST ONLY contain URLs that appear as source_url in threat_intelligence or mitigation_guidance. No additional URLs.\",\n      \"items\": { \"type\": \"string\" }\n    },\n    \"overall_confidence\": {\n      \"type\": \"string\",\n      \"enum\": [\"high\", \"medium\", \"low\"]\n    }\n  },\n  \"required\": [\n    \"incident_classification\",\n    \"external_research_used\",\n    \"threat_intelligence\",\n    \"ioc_indicators\",\n    \"mitigation_guidance\",\n    \"emerging_risk_assessment\",\n    \"sources_consulted\",\n    \"overall_confidence\"\n  ],\n  \"additionalProperties\": false\n}"
      },
      "retryOnFail": true,
      "typeVersion": 1.3
    },
    {
      "id": "5bf82fab-d781-4805-906d-12e9292b49cb",
      "name": "Merge All Intelligence",
      "type": "n8n-nodes-base.merge",
      "position": [
        -608,
        4304
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "includeUnpaired": true
        },
        "combineBy": "combineByPosition",
        "numberInputs": 4
      },
      "typeVersion": 3.2,
      "alwaysOutputData": true
    },
    {
      "id": "c87e8aa8-82b3-4477-9ae1-788c60974c38",
      "name": "Structured Output Parser - Historical RAG",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -1504,
        4048
      ],
      "parameters": {
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"similarity_search_performed\": { \"type\": \"boolean\" },\n    \"match_quality\": {\n      \"type\": \"string\",\n      \"enum\": [\"strong\", \"moderate\", \"weak\", \"none\"]\n    },\n    \"incidents_retrieved\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"incident_id\":              { \"type\": \"string\" },\n          \"similarity_score\":         { \"type\": \"number\" },\n          \"incident_type\":            { \"type\": \"string\" },\n          \"severity\":                 { \"type\": \"string\" },\n          \"resolution_time_minutes\":  { \"type\": [\"number\", \"null\"] },\n          \"root_cause\":               { \"type\": \"string\" },\n          \"resolution_summary\":       { \"type\": \"string\" },\n          \"key_remediation_actions\":  { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n          \"lessons_learned\":          { \"type\": [\"string\", \"null\"] },\n          \"mitre_techniques_used\":    { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n          \"detection_gap\":            { \"type\": [\"string\", \"null\"] },\n          \"what_went_well\":           { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n          \"what_went_wrong\":          { \"type\": \"array\", \"items\": { \"type\": \"string\" } }\n        },\n        \"required\": [\n          \"incident_id\", \"similarity_score\", \"incident_type\", \"severity\",\n          \"root_cause\", \"resolution_summary\", \"key_remediation_actions\",\n          \"detection_gap\", \"what_went_well\", \"what_went_wrong\"\n        ],\n        \"additionalProperties\": false\n      }\n    },\n    \"historical_patterns\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"most_common_root_cause\":     { \"type\": \"string\" },\n        \"avg_resolution_time_minutes\":{ \"type\": [\"number\", \"null\"] },\n        \"proven_remediation_steps\":   { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n        \"common_mitre_techniques\":    { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n        \"recurring_detection_gaps\":   { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n        \"what_went_well_patterns\":    { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n        \"what_went_wrong_patterns\":   { \"type\": \"array\", \"items\": { \"type\": \"string\" } }\n      },\n      \"required\": [\n        \"most_common_root_cause\", \"proven_remediation_steps\", \"common_mitre_techniques\",\n        \"recurring_detection_gaps\", \"what_went_well_patterns\", \"what_went_wrong_patterns\"\n      ],\n      \"additionalProperties\": false\n    },\n    \"contextual_relevance\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"overlap_summary\":      { \"type\": \"string\" },\n        \"key_differentiators\":  { \"type\": \"string\" },\n        \"confidence\":           { \"type\": \"string\", \"enum\": [\"high\", \"medium\", \"low\"] }\n      },\n      \"required\": [\"overlap_summary\", \"key_differentiators\", \"confidence\"],\n      \"additionalProperties\": false\n    },\n    \"fallback_note\": { \"type\": [\"string\", \"null\"] }\n  },\n  \"required\": [\n    \"similarity_search_performed\", \"match_quality\", \"incidents_retrieved\",\n    \"historical_patterns\", \"contextual_relevance\", \"fallback_note\"\n  ],\n  \"additionalProperties\": false\n}"
      },
      "retryOnFail": true,
      "typeVersion": 1.3
    },
    {
      "id": "aa0f1d66-d27b-4326-8d66-ac25320870c6",
      "name": "Threat Intel Data Formatter",
      "type": "n8n-nodes-base.set",
      "position": [
        -1344,
        4720
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2641d975-d1d0-4348-ad03-b3851f44d54b",
              "name": "external_threat_data",
              "type": "object",
              "value": "={{ $json.output }}"
            }
          ]
        }
      },
      "retryOnFail": true,
      "typeVersion": 3.4
    },
    {
      "id": "98093759-4556-4111-aacf-71ec76425c79",
      "name": "Report Synthesizer",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -464,
        4432
      ],
      "parameters": {
        "text": "=test_incident\n\n\npast_incident_data\n{{ JSON.stringify($json.past_incident_data) }}\n\nreference_playbook\n{{ JSON.stringify($json.playbook_text) }}\n\nexternal_threat_data\n{{ JSON.stringify($json.external_threat_data) }}",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "You are an elite Senior Incident Response Analyst AI working inside an enterprise SOC.\n\nYour job is to synthesize intelligence from three sources \u2014 a reference playbook, historical incident patterns, and live external threat intelligence \u2014 and produce a single, battle-ready Incident Response Report that an on-call engineer can immediately act on.\n\n### YOUR INPUTS\n\nYou will receive four elements:\n\n\u2014 test_incident\nThe raw incident alert JSON. This is your most reliable source for: incident_id, severity, category, type, affected_systems, MITRE IDs, detection_method, description, title, and any IPs/domains/hashes/file paths mentioned in the alert text. Mine this FIRST before any other source.\n\n\u2014 past_incident_data\nA structured JSON object from a vector similarity search against resolved past incidents. Key fields include: `match_quality`, `incidents_retrieved` (each with `key_remediation_actions` extracted from the `remediation_actions` field of past incidents), `historical_patterns.proven_remediation_steps`, `contextual_relevance`.\n\n\u2014 reference_playbook\nA full markdown-formatted incident response playbook. Either a specific technique-matched playbook or the GENERIC ENTERPRISE INCIDENT RESPONSE PLAYBOOK (identified by title \"Generic Enterprise Security Incident Response\" or Use Case ID \"GEN-UNIVERSAL-001\").\n\n\u2014 **external_threat_data\nA structured JSON object from live external research. Key fields: `incident_classification`, `external_research_used`, `threat_intelligence`, `ioc_indicators`, `mitigation_guidance` (phased: IMMEDIATE / CONTAINMENT / DETECTION / HARDENING), `emerging_risk_assessment`, `overall_confidence`.\n\n### TRUST HIERARCHY \u2014 STRICTLY ENFORCE THIS\n\nWhen information from different sources conflicts or overlaps:\n\n1. Playbook (highest trust) \u2014 defines the STRUCTURE and METHODOLOGY of the response. Even generic playbook steps are authoritative for process.\n2. Historical Patterns \u2014 highest trust for WHAT ACTUALLY WORKED internally. Proven remediation steps from past incidents outweigh generic advice.\n3. External Threat Intel \u2014 provides CVE context, IOCs, and emerging threat data. Use for enrichment and evidence, not as the primary action driver.\n4. Your general knowledge \u2014 use ONLY to fill gaps where all three sources are silent. Always signal when using general knowledge by prefixing with: [General Knowledge \u2014 not sourced from inputs]\n\n### EDGE CASE RULES\n\nIf `match_quality` is \"none\" OR `incidents_retrieved` is empty:\n\n- State clearly: \"No historical incidents matched this alert. The guidance below is based on the playbook and external intelligence only.\"\n- Do NOT fabricate historical patterns. Skip the Historical Context section or mark it as \"Not available \u2014 no matching past incidents found.\"\n- Omit avg_resolution_time from SLA Guidance.\n\nIf the playbook is the GENERIC playbook (GEN-UNIVERSAL-001):\n\n- Do NOT claim a specific technique-based playbook was found.\n- Use the generic playbook structure but adapt it to the specific incident type shown in the incoming alert.\n- Flag it: \"\u26a0\ufe0f No specific playbook matched this incident type. A generic enterprise doctrine has been applied. A dedicated playbook should be created.\"\n\nIf `external_research_used` is false or `threat_intelligence` is empty:\n\n- Skip the Threat Intelligence Briefing section.\n- Do NOT hallucinate CVE data or threat actor names.\n\nIf `contextual_relevance.confidence` is \"low\":\n\n- In the Historical Context section, clearly caveat: \"Historical matches are weak \u2014 treat these patterns as directional, not confirmatory.\"\n\nIf any field is null or missing:\n\n- Silently skip that sub-point rather than writing \"N/A\" inline.\n- Do NOT generate placeholder text.\n\nFor SLA / resolution time guidance:\n\n- Only include time estimates when `avg_resolution_time_minutes` is a real number from the data.\n- If null, use the playbook's SLA matrix tier instead.\n\n### OUTPUT FORMAT\n\nProduce a single Markdown document. Use the EXACT section structure below. Do not add, rename, or reorder sections.\n\n# \ud83d\udea8 Incident Response Report \u2014 [INCIDENT_TITLE]\n\nIncident ID: [incident_id] | Severity: [severity] | Category: [category] | Type: [type]\nDetection Method: [detection_method] | Affected Systems: [affected_systems joined with \", \"]\nIncident Status: [Active \u2014 not yet contained | Contained \u2014 systems isolated | Blocked \u2014 attack prevented pre-execution] \u2190 derive from alert description: if attack succeeded and is ongoing = Active; if isolation/containment already applied = Contained; if threat detected before impact = Blocked; default to Active if unclear.\nSeverity Assessment: [severity from alert] [Add \"| \u26a0\ufe0f Recommended Upgrade to [HIGHER_SEVERITY] \u2014 [1-line reason]\" ONLY if evidence from playbook escalation triggers, active exploitation in external intel, or historical patterns indicates underrating. Omit this field entirely if no upgrade is warranted.]\nReport Generated: AI-Generated \u2014 Validate Before Executing\n\n## 1. Executive Summary\n\n[3\u20135 sentences MAX. Answer: What is happening? How bad is it? Is it confirmed malicious or still unconfirmed? What is the single most important action right now?]\n\n[If generic playbook used, add:]\n\n> \u26a0\ufe0f No specific playbook matched this incident type. Generic enterprise doctrine applied. Create a dedicated playbook after resolution.\n\n[If no historical matches, add:]\n\n> \u2139\ufe0f No historical incidents matched this alert. All guidance is derived from playbook and external intelligence.\n\n## 2. Alert Analysis & Affected Systems\n\nIncident Profile:\n\n- Severity: [severity with P-level: e.g., High (P2)]\n- Category: [category]\n- Type: [type]\n- Affected Systems: [list]\n- MITRE Tactics: [mitre tactic names from test incident]\n- MITRE Techniques: [mitre technique IDs and names from test incident \u2014 label each as \"[Alert-stated]\"]\n- Detection Source: [detection_method from test incident]\n\nWhat Triggered This Alert:\n[Based on playbook's Alert Triggers section: list the 3\u20135 most applicable trigger conditions for this specific incident type. Be specific to the systems involved.]\n\nData Sources to Query Now:\n[From playbook's Detection Logic table: list the top 3\u20134 most relevant log sources for this specific incident, with specific query guidance.]\n\n## 3. Root Cause Hypothesis & MITRE ATT&CK Assessment\n\nMost Likely Root Cause:\n[Synthesise from: playbook triage steps + historical_patterns.most_common_root_cause + external threat context. Write 2\u20134 sentences. Be specific about what likely happened, not vague.]\n\nMITRE ATT&CK Chain:\n[BEFORE mapping, cross-validate: compare each alert-stated technique ID against the actual incident behaviour described in test_incident.description and test_incident.title. If the stated technique does not match the described behaviour (e.g., alert says T1098 Account Manipulation but description says \"outbound traffic on non-standard ports to C2 infrastructure\"), surface both:\n\n- [Alert-stated]: [Technique ID] \u2013 [Technique Name] \u2014 _stated in alert metadata_\n- [Behaviour-inferred]: [Technique ID] \u2013 [Technique Name] \u2014 _better matches the described behaviour_\n  If the stated technique IS consistent with the described behaviour, label it as [Validated] and map normally.\n  Format for each technique:]\n- [Tactic]: [Technique ID] \u2013 [Technique Name] [label] \u2014 [1 sentence on how this manifests in this incident]\n\nConfidence: [use contextual_relevance.confidence \u2014 High / Medium / Low]\n[If Low, add: \"Low confidence \u2014 limited historical precedent for this exact alert profile.\"]\n\nAlternative Hypotheses to Rule Out:\n[From playbook's \"De-escalated and Expected Benign Events\" section: list 2\u20133 false-positive scenarios specific to this incident type that the engineer should quickly rule out before treating as malicious.]\n\n## 4. Indicators of Compromise (IOCs)\n\nHunt for These Indicators:\n\n| IOC Type | Value / Pattern | Source | Priority |\n| -------- | --------------- | ------ | -------- |\n\n[Extract IOCs in this STRICT priority order:\n\n1. Alert Payload (test_incident) \u2014 scan title, description, affected_systems, tags for: IP addresses, domain names, file hashes (MD5/SHA), file paths, CVE IDs, usernames, ports, process names. These are HIGH priority \u2014 they come directly from the live alert.\n2. External ioc_indicators array \u2014 HIGH priority if from vendor advisory / CVE DB / CISA; MEDIUM if from other sources.\n3. Playbook IOC checklist \u2014 MEDIUM priority (template reference).\n4. Historical MITRE techniques \u2014 LOW priority (inferred patterns only).\n   If no IOCs extracted from any source: \"No specific IOC values retrieved. Use the playbook's IOC checklist template below as your investigation framework.\"]\n\nHunting tip: Always search for alert-payload IOCs (step 1 above) in SIEM/EDR BEFORE querying external sources.\n\nIOC Collection Checklist:\n[From playbook's IOC Checklist table: reproduce only the rows relevant to this incident type. Keep as actionable checkboxes.]\n\n## 5. Immediate Response Checklist (Next 30 Minutes)\n\nThese actions should begin NOW, in order. Do not wait for full root cause confirmation before executing IMMEDIATE steps.\n\n### IMMEDIATE (0\u201315 min) \u2014 Stabilise\n\n[Source authority rule \u2014 CRITICAL: For IMMEDIATE steps, ONLY use actions sourced from: (1) the reference playbook, (2) historical proven_remediation_steps from past incidents, or (3) external sources that are vendor advisories, CVE databases (NVD), or government advisories (CISA, NCSC). Do NOT use blog posts, community write-ups, Medium/Substack articles, or forum posts as the basis for IMMEDIATE steps \u2014 these may only appear in DETECTION or HARDENING sections.]\n[From: playbook analyst checklist first 3-4 items + external mitigation_guidance where phase=\"IMMEDIATE\" (authority-filtered). Write as numbered actionable steps. Include specific commands or queries where available from the data.]\n\n### CONTAINMENT (15\u201360 min) \u2014 Stop the Bleeding\n\n[From: external mitigation_guidance where phase=\"CONTAINMENT\" + playbook Containment Actions. Numbered steps. Preserve forensic evidence BEFORE destructive steps.]\n\n### DETECTION & VALIDATION (0\u201360 min, parallel)\n\n[From: external mitigation_guidance where phase=\"DETECTION\" + playbook data sources. Numbered steps with specific queries or commands from the data where available.]\n\n## 6. Escalation Decision\n\nEscalate NOW if any of the following are true:\n[From playbook's Escalation section + the severity matrix. Write as a clear, tight checklist. Include specific trigger conditions relevant to this incident type.]\n\n- [ ] [Condition 1]\n- [ ] [Condition 2]\n- [ ] [Condition 3 \u2014 regulatory/data exposure specific]\n- [ ] [Condition 4 \u2014 lateral movement / privilege escalation]\n\nEscalation Path:\n\n- Tier 1 \u2192 Tier 2: [trigger condition from playbook] \u2014 [who to page]\n- Tier 2 \u2192 IR Lead: [trigger condition] \u2014 [action]\n- IR Lead \u2192 CISO/Legal: [trigger condition \u2014 especially data breach] \u2014 [action]\n\n## 7. Eradication & Recovery Plan\n\nComplete full root cause confirmation before eradication. Premature remediation can destroy forensic evidence.\n\n[Numbered ordered list. Synthesise from: playbook Eradication & Recovery section + historical proven_remediation_steps + external HARDENING phase mitigations. The first 2\u20133 steps should come from proven internal history (if available). Label the source:]\n\n1. [Action] (Source: Internal precedent from [incident_id]) or (Source: Playbook) or (Source: External \u2014 [source_name])\n2. ...\n\n## 8. Historical Context & Pattern Intelligence\n\n[SKIP THIS ENTIRE SECTION if match_quality is \"none\". Replace with:]\n\n> \u2139\ufe0f Historical Context: Not Available \u2014 No matching past incidents found in the resolved incidents database. A new historical benchmark will be created when this incident is resolved.\n\n[If incidents retrieved, include:]\nSimilarity Match Quality: [match_quality] | Historical Confidence: [contextual_relevance.confidence]\n\nWhat Happened in Similar Past Incidents:\n| Incident | Similarity | Type | Severity | Resolution Time | SLA Met |\n|---|---|---|---|---|---|\n[Row for each incident in incidents_retrieved. Use \"Unknown\" for null sla_met.]\n\nCommon Root Cause Pattern:\n\n> [historical_patterns.most_common_root_cause]\n\nWhat Worked (Proven Internal Steps):\n[List historical_patterns.proven_remediation_steps as a numbered list. These come from the `remediation_actions` field of past resolved incidents. IMPORTANT: If the steps appear generic (e.g., contain phrases like \"Isolated and secured affected systems\", \"Applied immediate patches and configuration updates\", \"Verified system integrity and restored normal operations\"), add this caveat after the list:\n\n> \u26a0\ufe0f These historical steps appear generic \u2014 the source incident records may not contain detailed action-level specifics. Cross-check against the actual incident IDs listed above before relying on these as precise precedent.]\n\nHistorical Resolution Benchmark:\n[If avg_resolution_time_minutes is not null:]\n\n- Avg resolution time for this type: [avg_resolution_time_minutes] minutes ([X hours Y min])\n- SLA success rate: [sla_success_rate_pct]% (if available, else \"Not recorded in dataset\")\n- This incident is [severity] severity \u2014 target resolution within [derive from playbook severity matrix SLA for this severity level]\n\nHow This Incident Differs from Historical Cases:\n\n> [contextual_relevance.key_differentiators]\n\n[If confidence is low:]\n\n> \u26a0\ufe0f Historical match confidence is LOW. Treat the above patterns as directional guidance only, not confirmed precedent.\n\n## 9. Threat Intelligence Briefing\n\n[SKIP this section if external_research_used is false or threat_intelligence is empty. Replace with:]\n\n> \u2139\ufe0f External Threat Intelligence: Not Available \u2014 External research was not performed or returned no results.\n\n[If data available:]\nRisk Status: [emerging_risk_assessment.active_exploitation_observed \u2192 \"\u26a0\ufe0f ACTIVE exploitation observed in the wild\" or \"No active exploitation confirmed\"] | Risk Trend: [risk_trend] | Confidence: [overall_confidence]\n\nRelevant Threat Intelligence:\n[For each item in threat_intelligence array, format as:]\n[category] \u2014 [title]\n[summary \u2014 max 2 sentences]\nApplicability: [applicability_confidence] \u2014 [applicability_notes if present]\nSource: [[source_name]]([source_url])\n\nLive IOCs from External Sources:\n[ioc_indicators formatted as a table: Type | Value | Context]\n[If empty: \"No specific IOC values retrieved from external sources.\"]\n\nAnalyst Note:\n\n> [emerging_risk_assessment.notes \u2014 max 3 sentences, trim if longer]\n\n## 10. Long-term Hardening Recommendations\n\n[From: playbook Lessons Learned + Eradication hardening items + external HARDENING phase mitigations. Write as a structured list with priority labels.]\n\nPriority 1 \u2014 Do within 48 hours:\n\n- [Hardening action directly related to the attack vector exploited]\n\nPriority 2 \u2014 Do within 2 weeks:\n\n- [Policy / access control improvements]\n\nPriority 3 \u2014 Do within next quarter:\n\n- [Detection coverage, automation, playbook creation]\n\n> [If generic playbook was used, always add:]\n> \ud83d\udccb Create a dedicated response playbook for [incident type] incidents. This generic doctrine is a temporary measure.\n\n## 11. Documentation Requirements\n\nThe following must be captured in the incident ticket before closure:\n\n- [ ] Detection timestamp and alert source\n- [ ] Initial severity assessment with justification\n- [ ] Systems and accounts confirmed affected\n- [ ] Forensic evidence preserved (log snapshots, memory, screenshots)\n- [ ] Root cause confirmed or best hypothesis with evidence\n- [ ] Containment actions taken with timestamps\n- [ ] Eradication steps executed with validation results\n- [ ] Residual risk statement\n- [ ] Stakeholders notified (with timestamps)\n- [ ] Recovery validation signed off by system/data owner\n- [ ] Recommended control improvements with owners and deadlines\n\nReport generated by Incident Intelligence System. Always validate AI-generated guidance against current system state before executing. This report is a starting point, not a substitute for analyst judgment.\n\n### USING POSTMORTEM INTELLIGENCE FROM HISTORICAL DATA\n\nThe past_incident_data input now contains structured postmortem intelligence. Use the following fields when they are present:\n\nPer-incident fields (from incidents_retrieved[]):\n- detection_gap: If a past incident identified a gap in detection coverage \u2014 check if that gap is likely present in the CURRENT incident. If so, surface it in Section 3 (Root Cause Hypothesis) and Section 10 (Long-term Hardening Recommendations).\n- what_went_well: Use in Section 8 (Historical Context) under the subheading \"What Worked\". These are verified internal success patterns.\n- what_went_wrong: Use in Section 8 under the subheading \"What Failed\". Surface as cautionary context.\n\nCross-incident pattern fields (from historical_patterns):\n- recurring_detection_gaps: Synthesize all detection failure patterns across retrieved incidents. If a recurring gap exists, this is a systemic blind spot \u2014 flag it in Section 3 under \"Most Likely Root Cause\" and in Section 10 under \"Priority 1 \u2014 Do within 48 hours\".\n- what_went_well_patterns: Include in Section 8 under the subheading \"Proven Response Patterns\".\n- what_went_wrong_patterns: Include in Section 8 under the subheading \"Historical Failure Patterns \u2014 Avoid These\".\n\n### OUTPUT FORMAT\n\nYou MUST produce a valid JSON object matching the schema enforced by the output parser.\nPlace the complete 11-section Markdown document (formatted exactly as specified above) inside the `report_markdown` field.\nAll other fields are structured extractions for downstream automation and validation.\n\n### CRITICAL OUTPUT RULES\n\n1. Never hallucinate incident IDs, CVE numbers, IP addresses, commands, or tool names not present in the input data. If unsure, write \"Verify in [log source]\" instead.\n2. Never use placeholder text like \"[Insert here]\" or \"[TBD]\". If data is missing, skip the line or use the edge case rules above.\n3. Always format commands in code blocks for copy-paste readiness.\n4. Section 5 (Immediate Actions) is sacred \u2014 it must be the most specific, concrete, and immediately executable section of the entire report. Never make it vague. Only use high-authority sources (playbook, internal history, vendor/gov advisories) for the IMMEDIATE phase.\n5. Do not summarise the playbook as if it is a source \u2014 synthesise FROM it, don't cite it. The engineer should not need to read the playbook; this report replaces it.\n6. Preserve the on-call engineer's time \u2014 if something can be said in 2 sentences, do not use 5.\n7. Severity upgrade callout is mandatory when evidence warrants it \u2014 never bury a severity upgrade recommendation in prose. It must appear in the report header's Severity Assessment line.\n8. IOC extraction from the alert payload (test_incident) is always Step 1 \u2014 never skip this even if external IOCs are richer. Alert-payload IOCs have the highest evidentiary value because they come from the live system.\n9. Always validate MITRE technique IDs against incident behaviour before mapping. A stated technique ID that does not match the described behaviour must be flagged as [Alert-stated] and your validated mapping shown as [Behaviour-inferred].\n10. Always escape newlines (\\n) and double-quotes (\\\") inside report_markdown correctly. The entire Markdown document is wrapped in a JSON string."
            }
          ]
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "retryOnFail": true,
      "typeVersion": 1.9
    },
    {
      "id": "48fbd7cb-0480-4dcc-9295-db5a5587886e",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3472,
        3168
      ],
      "parameters": {
        "color": "#33285F",
        "width": 368,
        "height": 432,
        "content": "## Test Cases (13 Total)\n\nTC1  -- Test-data_exfiltration-001\nTC2  -- Test-ddos_attack-001\nTC3  -- Test-error_spike-001\nTC4  -- Test-leaked_credentials-001\nTC5  -- Test-malware_detection-001\nTC6  -- Test-memory_leak-001\nTC7  -- Test-phishing_attack-001\nTC8  -- Test-policy_violation-001\nTC9  -- Test-prompt_injection-001\nTC10 -- Test-ransomware_detection-001\nTC11 -- Test-service_outage-001\nTC12 -- Test-unauthorized_access_attempt-001\nTC13 -- Test-vulnerable_dependency-001\n\nChange active incident in the Set Incident ID node."
      },
      "typeVersion": 1
    },
    {
      "id": "50f1378a-1ee1-417d-8e21-f224c98e1883",
      "name": "Historical RAG Data Formatter",
      "type": "n8n-nodes-base.set",
      "position": [
        -1136,
        4032
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "56d336e3-e23e-4045-b1e0-a13bc869f6b8",
              "name": "past_incident_data",
              "type": "object",
              "value": "={{ $json.output }}"
            }
          ]
        }
      },
      "retryOnFail": true,
      "typeVersion": 3.4
    },
    {
      "id": "b69c3b22-561e-4234-b0c7-07df461252e6",
      "name": "Sticky Note24",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -128,
        4032
      ],
      "parameters": {
        "color": "#51B3D1",
        "width": 480,
        "height": 416,
        "content": "## Step 4 -- Report Synthesizer\n\nReceives merged intelligence from all 3 branches\nand produces a complete incident response report.\n\nModel: Claude - Report Synthesizer (OpenRouter)\n\nOutput (JSON validated by Structured Output Parser):\n  incident_summary\n  immediate_actions[]\n  investigation_checklist[]\n  containment_guidance[]\n  evidence_to_preserve[]\n  iocs_extracted[]\n  assumptions_and_confidence\n  output_markdown (full 11-section report)"
      },
      "typeVersion": 1
    },
    {
      "id": "6c448e2b-cf7d-4d9b-b206-f2990f7b31a0",
      "name": "Sticky Note26",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2304,
        3824
      ],
      "parameters": {
        "color": "#18233B",
        "width": 432,
        "height": 368,
        "content": "## Branch A -- Historical RAG\n\nSearches past resolved incidents for similar cases.\n\nAgent:  Historical Incidents RAG Agent\nModel:  Gemini Embeddings + Supabase pgvector\nTop-K:  3 most similar past incidents\n\nOutput fields:\n  match_quality  (strong / moderate / weak / none)\n  incidents_retrieved\n  historical_patterns\n  detection_gap_analysis"
      },
      "typeVersion": 1
    },
    {
      "id": "63a4355d-43a9-4f68-841d-9a17a159f4aa",
      "name": "Sticky Note27",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2304,
        4800
      ],
      "parameters": {
        "color": "#18233B",
        "width": 416,
        "height": 528,
        "content": "## Branch C -- External Threat Intelligence\n\nSearches the live web for CVEs, active threat campaigns,\nIOCs and mitigation guidance relevant to the incident.\n\nAgent:  Threat Intel Enrichment Agent\nTools:  Tavily Search + Claude (OpenRouter)\n\nOutput fields:\n  incident_classification\n  threat_intelligence[]\n  ioc_indicators[]\n  mitigation_guidance[]\n  emerging_risk_assessment\n\nSmart suppression: AVAILABILITY and PERFORMANCE\nincidents skip external search entirely to avoid\nirrelevant security hallucinations."
      },
      "typeVersion": 1
    },
    {
      "id": "1420e8df-8d65-43b8-8b4f-329f3fc9bfde",
      "name": "Sticky Note31",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2304,
        4256
      ],
      "parameters": {
        "color": "#18233B",
        "width": 416,
        "height": 480,
        "content": "## Branch B -- Playbook Router\n\nMatches the incident to the best-fit response playbook.\nFalls back to GEN-UNIVERSAL-001 if no match found.\n\nAgent:  Playbook Routing Agent\nModel:  Gemini Embeddings + Supabase pgvector\n\nOutput fields:\n  match_found  (true / false)\n  playbook_id, playbook_name\n  confidence, reasoning\n\nLoaded playbooks:\n  S3 Bucket Access, Phishing,\n  Phishing Campaign, Brute Force Login"
      },
      "typeVersion": 1
    },
    {
      "id": "c4127644-c004-4370-91a4-0fb53f218a7a",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4384,
        2752
      ],
      "parameters": {
        "color": "#21C45E",
        "width": 496,
        "height": 608,
        "content": "## Configuration\n### Values To Change\n\n**Set these before running any pipeline**\n\n  table_incidents   --> test_incidents_v1\n  table_playbooks   --> reference_playbooks_v1\n  incident_id       --> ID of the test incident to process\n  model_analysis    --> OpenRouter model for RAG + Synthesis\n  model_threat      --> OpenRouter model for Threat Intel\n\nEdit values directly in the Config node.\n\nCredentials required (Settings -> Credentials):\n  - Supabase\n  - OpenRouter\n  - Google Gemini\n  - Tavily"
      },
      "typeVersion": 1
    },
    {
      "id": "940e6a95-ab5b-4b9f-b436-cdd39895cdb0",
      "name": "Structured Output Parser - Synthesizer",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -384,
        4656
      ],
      "parameters": {
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"incident_summary\": {\n      \"type\": \"string\",\n      \"description\": \"3\u20135 sentence summary: what happened, severity, single most important action.\"\n    },\n    \"similar_past_incidents_used\": {\n      \"type\": \"array\",\n      \"description\": \"incident_id values from past_incident_data that materially influenced this response.\",\n      \"items\": { \"type\": \"string\" }\n    },\n    \"immediate_actions\": {\n      \"type\": \"array\",\n      \"description\": \"Each action from the IMMEDIATE (0\u201315 min) block of Section 5, as individual strings.\",\n      \"items\": { \"type\": \"string\" },\n      \"minItems\": 1\n    },\n    \"containment_guidance\": {\n      \"type\": \"array\",\n      \"description\": \"Each action from the CONTAINMENT (15\u201360 min) block of Section 5, as individual strings.\",\n      \"items\": { \"type\": \"string\" }\n    },\n    \"iocs_extracted\": {\n      \"type\": \"array\",\n      \"description\": \"IOC values extracted from Section 4 \u2014 IPs, hashes, domains, process names. Empty array if none.\",\n      \"items\": { \"type\": \"string\" }\n    },\n    \"assumptions_and_confidence\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"confidence_level\": {\n          \"type\": \"string\",\n          \"enum\": [\"high\", \"medium\", \"low\"]\n        },\n        \"generic_playbook_used\": {\n          \"type\": \"boolean\",\n          \"description\": \"true if GEN-UNIVERSAL-001 playbook was used.\"\n        },\n        \"caveat_notes\": {\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\" }\n        }\n      },\n      \"required\": [\"confidence_level\", \"generic_playbook_used\", \"caveat_notes\"],\n      \"additionalProperties\": false\n    },\n    \"report_markdown\": {\n      \"type\": \"string\",\n      \"description\": \"The complete, fully formatted 11-section Markdown IR report. Must not be empty or truncated.\"\n    }\n  },\n  \"required\": [\n    \"incident_summary\",\n    \"immediate_actions\",\n    \"containment_guidance\",\n    \"iocs_extracted\",\n    \"assumptions_and_confidence\",\n    \"report_markdown\"\n  ],\n  \"additionalProperties\": false\n}"
      },
      "retryOnFail": true,
      "typeVersion": 1.3
    },
    {
      "id": "43f65b18-20ed-4d8e-8968-5bc091cd5157",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        208,
        4432
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a7b1dcb2-dfa6-4717-b69a-b6b9a19863bd",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.output.incident_summary }}",
              "rightValue": ""
            },
            {
              "id": "8526c2e3-9a0c-4884-8479-1c490d558e21",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.output.immediate_actions.length }}",
              "rightValue": "={{ 0 }}"
            },
            {
              "id": "76cacd44-d21c-48f0-a247-0cf00c25923e",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.output.report_markdown }}",
              "rightValue": "## 1. Executive Summary"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "89795f85-dd0a-47a8-a522-28189cd16340",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        4688
      ],
      "parameters": {
        "color": "#CF8D97",
        "width": 256,
        "height": 384,
        "content": "## Degraded Output\n\nThe Structured Output Parser did not produce\nall required fields (incident_summary,\nimmediate_actions, output_markdown).\n\nAction: DO NOT write to Supabase.\nCheck the LLM response in execution log\nand re-run the workflow.\n\nThis path should never trigger in normal\noperation. If it does, check model rate\nlimits or context window size."
      },
      "typeVersion": 1
    },
    {
      "id": "ccf987cb-2523-4403-aaa6-f912959c80a5",
      "name": "Fetch Full Playbook from Supabase",
      "type": "n8n-nodes-base.supabase",
      "onError": "continueRegularOutput",
      "position": [
        -1424,
        4320
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "=id",
              "keyValue": "={{ $('Playbook Routing Agent').item.json.output.playbook_id }}"
            }
          ]
        },
        "tableId": "={{ $('Config-Ingestion').item.json.table_playbooks }}",
        "operation": "get"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": false,
      "typeVersion": 1,
      "alwaysOutputData": false
    },
    {
      "id": "adc1993b-1328-4f2e-8bb3-d93273b7d862",
      "name": "Sticky Note - Pipeline Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4384,
        3408
      ],
      "parameters": {
        "color": "#4E80EE",
        "width": 496,
        "height": 656,
        "content": "## Incident Management\n\nWhat this workflow does:\nTakes a raw security or operational incident and produces\na structured, battle-ready incident response report.\n\nThree intelligence branches run in parallel:\n\n  Branch A -- Historical RAG\n  Searches past incidents for similar cases and proven\n  remediation patterns.\n\n  Branch B -- Playbook Router\n  Matches the incident to the best-fit response playbook.\n  Falls back to a generic playbook if no match exists.\n\n  Branch C -- Threat Intelligence\n  Searches the live web for CVEs, active threat campaigns,\n  IOCs and mitigation guidance.\n\nFinal output:\n  - Structured JSON written to test_incidents_v1.output\n  - 11-section Markdown in test_incidents_v1.output_markdown\n\nStack: n8n, Supabase (pgvector), Gemini Embeddings,\n       Claude via OpenRouter, Tavily Search"
      },
      "typeVersion": 1
    },
    {
      "id": "904c7ef7-0ebb-4892-aeaf-658bdb3350bf",
      "name": "Sticky Note - Merge Step",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -576,
        4032
      ],
      "parameters": {
        "color": "#51B3D1",
        "width": 388,
        "height": 344,
        "content": "## Step 3 -- Merge All Intelligence\n\nCombines outputs from all 3 branches into one item\nusing combineByPosition.\n\nFields passed to the synthesizer:\n  past_incident_data   (from Branch A)\n  playbook_text        (from Branch B)\n  external_threat_data (from Branch C)"
      },
      "typeVersion": 1
    },
    {
      "id": "4f38de83-84ba-4e3c-865f-8fb69e94ca64",
      "name": "Sticky Note - Fetch Step",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3648,
        4400
      ],
      "parameters": {
        "color": "#51B3D1",
        "width": 320,
        "height": 272,
        "content": "## Step 1 -- Fetch Incident\n\nReads the active test incident row from Supabase\nby incident_id configured in Set Incident ID.\n\nOutput: full incident JSON passed to all 3 branches."
      },
      "typeVersion": 1
    },
    {
      "id": "f5d219fd-225f-41b8-9bae-ef5b72c24b24",
      "name": "Sticky Note - Step 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2608,
        3776
      ],
      "parameters": {
        "color": "#51B3D1",
        "width": 284,
        "content": "## Step 2 -- Parallel Retrieval\n3 branches run simultaneously. See labels below."
      },
      "typeVersion": 1
    },
    {
      "id": "166cc918-8a6a-470b-a354-a4f480f6997f",
      "name": "Sticky Note - Step 5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        4768
      ],
      "parameters": {
        "color": "#51B3D1",
        "width": 320,
        "height": 280,
        "content": "## Step 5 -- Validate Output\n\nChecks that the synthesizer produced all required fields:\nincident_summary, immediate_actions, output_markdown.\n\nTRUE --> Step 6 (write to Supabase)\nFALSE --> Degraded Output (no write)"
      },
      "typeVersion": 1
    },
    {
      "id": "f5194d24-dad5-4347-8d6c-5f34ce289778",
      "name": "Sticky Note - Step 6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        4032
      ],
      "parameters": {
        "color": "#51B3D1",
        "width": 280,
        "height": 260,
        "content": "## Step 6 -- Write to Supabase\n\nUpdates test_incidents_v1 row:\n  output          --> structured JSON\n  output_markdown --> 11-section report\n  run_status      --> completed"
      },
      "typeVersion": 1
    },
    {
      "id": "e4381b7d-ed85-4b47-81ac-ea9542718922",
      "name": "Loop Over Documents",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -2672,
        3008
      ],
      "parameters": {
        "options": {}
      },
      "retryOnFail": true,
      "typeVersion": 3
    },
    {
      "id": "fe131fe4-9f46-482b-a9bd-ace0dc743752",
      "name": "Recursive Character Text Splitter (Resolved Incidents)",
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "position": [
        -2336,
        2800
      ],
      "parameters": {
        "options": {},
        "chunkSize": "={{ $('Config-Ingestion').item.json.chunk_size }}",
        "chunkOverlap": "={{ $('Config-Ingestion').item.json.chunk_overlap }}"
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "9ea8cfee-247e-496e-849e-e5a68ceb2383",
      "name": "Supabase Vector Insert (Resolved Incidents)",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreSupabaseInsert",
      "position": [
        -2336,
        2400
      ],
      "parameters": {
        "queryName": "={{ $('Config-Ingestion').item.json.query_incidents }}",
        "tableName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config-Ingestion').item.json.table_resolved }}"
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "retryOnFail": true,
      "typeVersion": 1,
      "alwaysOutputData": false
    },
    {
      "id": "ffc6328b-01be-4942-a58a-04d4cb82ea25",
      "name": "Wait (To Avoid Hitting API Limits)",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1952,
        2608
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "a36e33f3-b047-4482-a256-04f4732bb9bb",
      "name": "Recursive Character Text Splitter (Playbooks Ingestion)",
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "position": [
        -1136,
        3408
      ],
      "parameters": {
        "options": {},
        "chunkSize": "={{ $('Config-Ingestion').item.json.chunk_size }}",
        "chunkOverlap": "={{ $('Config-Ingestion').item.json.chunk_overlap }}"
      },
      "retryOnFail": false,
      "typeVersion": 1
    },
    {
      "id": "8952342f-c176-40cf-a872-95575f02ccef",
      "name": "Summarizer Agent",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -1648,
        3040
      ],
      "parameters": {
        "model": "={{ $('Config-Ingestion').item.json.model_chat }}",
        "options": {
          "temperature": "={{ $('Config-Ingestion').item.json.temperature }}"
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "607d77cc-ce52-487e-b829-14e9afe469c5",
      "name": "Supabase Vector Insert (Playbooks Ingestion)",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreSupabaseInsert",
      "position": [
        -1040,
        3008
      ],
      "parameters": {
        "queryName": "={{ $('Config-Ingestion').item.json.query_playbooks }}",
        "tableName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config-Ingestion').item.json.table_playbooks }}"
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "92ad7447-6859-429a-824f-afbc612ed089",
      "name": "Report Synthesizer Agent",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -384,
        4864
      ],
      "parameters": {
        "model": "={{ $('Config').item.json.model_analysis }}",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "c4c574e5-3be9-48d5-a59b-2182ec28f80b",
      "name": "Write Runbook to DB",
      "type": "n8n-nodes-base.supabase",
      "position": [
        432,
        4336
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "=incident_id",
              "keyValue": "={{ $('Webhook').item.json.body.incident_id }}",
              "condition": "eq"
            }
          ]
        },
        "tableId": "={{ $('Config').item.json.table_test }}",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "=output",
              "fieldValue": "={{ $json.output }}"
            },
            {
              "fieldId": "=output_markdown",
              "fieldValue": "={{ $json.output.report_markdown }}"
            },
            {
              "fieldId": "=run_status",
              "fieldValue": "={{ \"completed\" }}"
            },
            {
              "fieldId": "webhook_url",
              "fieldValue": "={{ $('Webhook').item.json.webhookUrl }}"
            },
            {
              "fieldId": "execution",
              "fieldValue": "={{ $execution.id }}"
            }
          ]
        },
        "operation": "update"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "87f6fb84-ec29-4183-b905-039edbb79af2",
      "name": "Replace With Error Notification Workflow",
      "type": "n8n-nodes-base.noOp",
      "position": [
        432,
        4528
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "680c7a19-4fba-4024-99d9-8d91bb5342d9",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -3664,
        4240
      ],
      "parameters": {
        "path": "8142d48e-b633-498d-8e73-d041dff78808",
        "options": {
          "allowedOrigins": "*"
        },
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "414b364d-5ca3-4636-ae04-180a59a0415f",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        -1344,
        4928
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e72c5a65-6418-4b31-93b0-0d2803000cba",
              "name": "message",
              "type": "string",
              "value": "TIP Search Failed. Other searches unaffected."
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "55337668-8df8-4f2b-ac8f-997c9ea94cc7",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        -3280,
        4240
      ],
      "parameters": {
        "jsCode": "const crypto = require('crypto');\nconst encrypted = $input.first().json.body.encrypted_openrouter_key;\nconst ENCRYPTION_KEY = 'e6d371c137a8b45cb2f26aa651ee4890b54cd4cf47458149cbb51b5a6b4436f6'; // 64-char hex\n\nconst [ivB64, ctB64] = encrypted.split(':');\nconst iv         = Buffer.from(ivB64, 'base64');\nconst ctWithTag  = Buffer.from(ctB64, 'base64');\nconst authTag    = ctWithTag.slice(-16);       // last 16 bytes = GCM tag\nconst ciphertext = ctWithTag.slice(0, -16);\n\nconst decipher = crypto.createDecipheriv(\n  'aes-256-gcm',\n  Buffer.from(ENCRYPTION_KEY, 'hex'),\n  iv\n);\ndecipher.setAuthTag(authTag);\nconst apiKey = decipher.update(ciphertext, null, 'utf8')\n             + decipher.final('utf8');\n\nreturn [{ json: { ...$input.first().json, openrouter_api_key: apiKey } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "8837c073-9e53-4d62-a3bb-891bdadf28db",
      "name": "Threat Intel Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -1600,
        4144
      ],
      "parameters": {
        "model": "={{ $('Config').item.json.model_analysis }}",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "c0fd78df-1e0d-49dd-ac58-222a888b6dbb",
      "name": "Historical RAG Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -1856,
        5200
      ],
      "parameters": {
        "model": "={{ $('Config').item.json.model_analysis }}",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "c5e393a6-8cdf-4002-a6bb-8fa82f091d36",
      "name": "Playbook RAG Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -1568,
        4656
      ],
      "parameters": {
        "model": "={{ $('Config').item.json.model_analysis }}",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1
    },
    {
      "id": "6e88f7ae-fb2e-42e1-8ed2-47b26b19c9b6",
      "name": "Create a row",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -2944,
        4240
      ],
      "parameters": {
        "tableId": "incident_analyses",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "execution_id",
              "fieldValue": "={{ $execution.id }}"
            }
          ]
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b214be4d-44f4-438f-81b0-6df1bedd1dd5",
      "name": "Update on completion",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -32,
        4544
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "keyValue": "={{ $('Create a row').first().json.id }}",
              "condition": "eq"
            }
          ]
        },
        "tableId": "incident_analyses",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "report",
              "fieldValue": "={{ $json.output.report_markdown }}"
            },
            {
              "fieldId": "status",
              "fieldValue": "complete"
            }
          ]
        },
        "operation": "update"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "44ab3709-24cd-4e5a-a1e3-ca44540f0535",
      "name": "Config-Ingestion",
      "type": "n8n-nodes-base.set",
      "position": [
        -3552,
        3008
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "4730995e-a705-4d72-9f96-8e5b5f388f17",
              "name": "github_repo",
              "type": "string",
              "value": "deployedengineer/incident-response"
            },
            {
              "id": "a9cd6345-3ec7-4be1-8e56-ff290289554d",
              "name": "github_baseUrl",
              "type": "string",
              "value": "https://api.github.com/repos"
            },
            {
              "id": "d724a580-8b94-4736-86ba-ba729b16c6fe",
              "name": "model_chat",
              "type": "string",
              "value": "google/gemini-3.1-pro-preview"
            },
            {
              "id": "a91d0c1f-44d7-44e9-b63d-2283226de411",
              "name": "model_embedding",
              "type": "string",
              "value": "models/gemini-embedding-001"
            },
            {
              "id": "c88da39a-8099-418e-aa87-217275acaa12",
              "name": "model_analysis",
              "type": "string",
              "value": "anthropic/claude-opus-4.5"
            },
            {
              "id": "134e44c4-d70f-460a-a235-542cce3b385e",
              "name": "temperature",
              "type": "number",
              "value": 0.3
            },
            {
              "id": "76ff66b0-3b3b-435f-b1fd-f216f2ed7594",
              "name": "chunk_size",
              "type": "number",
              "value": 10000
            },
            {
              "id": "8641c57d-7230-4769-80b4-fd2fd048f441",
              "name": "chunk_overlap",
              "type": "number",
              "value": 0
            },
            {
              "id": "68ef0470-970f-4a13-94df-e4a086c4edaf",
              "name": "query_playbooks",
              "type": "string",
              "value": "match_reference_playbooks_v1"
            },
            {
              "id": "fec0bfa3-147a-4b0a-bd66-239f0830534a",
              "name": "query_incidents",
              "type": "string",
              "value": "match_resolved_incidents_v1"
            },
            {
              "id": "82640a55-43a8-4e7d-808e-2f99712fc0ab",
              "name": "table_resolved",
              "type": "string",
              "value": "resolved_incidents_v1"
            },
            {
              "id": "3bb5bfcc-2fc7-4c9e-946f-90fc368cdb1e",
              "name": "table_playbooks",
              "type": "string",
              "value": "reference_playbooks_v1"
            },
            {
              "id": "eb1088ba-166d-4f8a-bd89-cfc18ea9c721",
              "name": "table_test",
              "type": "string",
              "value": "test_incidents_v1"
            },
            {
              "id": "b96729de-4ff2-46ef-bc4a-991c8e9c9aff",
              "name": "webhook_body",
              "type": "object",
              "value": "={{ $json.body }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "10aa3397-cc2f-4014-ad14-485ea7e82073",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -2656,
        4240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "4730995e-a705-4d72-9f96-8e5b5f388f17",
              "name": "github_repo",
              "type": "string",
              "value": "deployedengineer/incident-response"
            },
            {
              "id": "a9cd6345-3ec7-4be1-8e56-ff290289554d",
              "name": "github_baseUrl",
              "type": "string",
              "value": "https://api.github.com/repos"
            },
            {
              "id": "d724a580-8b94-4736-86ba-ba729b16c6fe",
              "name": "model_chat",
              "type": "string",
              "value": "google/gemini-3.1-pro-preview"
            },
            {
              "id": "a91d0c1f-44d7-44e9-b63d-2283226de411",
              "name": "model_embedding",
              "type": "string",
              "value": "models/gemini-embedding-001"
            },
            {
              "id": "c88da39a-8099-418e-aa87-217275acaa12",
              "name": "model_analysis",
              "type": "string",
              "value": "anthropic/claude-opus-4.5"
            },
            {
              "id": "134e44c4-d70f-460a-a235-542cce3b385e",
              "name": "temperature",
              "type": "number",
              "value": 0.3
            },
            {
              "id": "76ff66b0-3b3b-435f-b1fd-f216f2ed7594",
              "name": "chunk_size",
              "type": "number",
              "value": 10000
            },
            {
              "id": "8641c57d-7230-4769-80b4-fd2fd048f441",
              "name": "chunk_overlap",
              "type": "number",
              "value": 0
            },
            {
              "id": "68ef0470-970f-4a13-94df-e4a086c4edaf",
              "name": "query_playbooks",
              "type": "string",
              "value": "match_reference_playbooks_v1"
            },
            {
              "id": "fec0bfa3-147a-4b0a-bd66-239f0830534a",
              "name": "query_incidents",
              "type": "string",
              "value": "match_resolved_incidents_v1"
            },
            {
              "id": "82640a55-43a8-4e7d-808e-2f99712fc0ab",
              "name": "table_resolved",
              "type": "string",
              "value": "resolved_incidents_v1"
            },
            {
              "id": "3bb5bfcc-2fc7-4c9e-946f-90fc368cdb1e",
              "name": "table_playbooks",
              "type": "string",
              "value": "reference_playbooks_v1"
            },
            {
              "id": "eb1088ba-166d-4f8a-bd89-cfc18ea9c721",
              "name": "table_test",
              "type": "string",
              "value": "test_incidents_v1"
            },
            {
              "id": "b96729de-4ff2-46ef-bc4a-991c8e9c9aff",
              "name": "webhook_body",
              "type": "object",
              "value": "={{ $json.body }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    }
  ],
  "active": true,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "020e4437-b2bd-4eec-9de3-61441c5796c6",
  "nodeGroups": [],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Write Runbook to DB",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Replace With Error Notification Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Historical Incidents RAG Agent",
            "type": "main",
            "index": 0
          },
          {
            "node": "Playbook Routing Agent",
            "type": "main",
            "index": 0
          },
          {
            "node": "Threat Intel Enrichment Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Merge All Intelligence",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Create a row": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config-Ingestion": {
      "main": [
        [
          {
            "node": "Get Resolved Incidents from GitHub",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search in Tavily": {
      "ai_tool": [
        [
          {
            "node": "Threat Intel Enrichment Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Summarizer Agent": {
      "ai_languageModel": [
        [
          {
            "node": "Summarize Reference Playbooks for Embeddings",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Create a row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Playbook RAG Model": {
      "ai_languageModel": [
        [
          {
            "node": "Playbook Routing Agent",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser - Playbook Router",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Report Synthesizer": {
      "main": [
        [
          {
            "node": "Update on completion",
            "type": "main",
            "index": 0
          },
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Threat Intel Model": {
      "ai_languageModel": [
        [
          {
            "node": "Structured Output Parser - Historical RAG",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Historical Incidents RAG Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Documents": {
      "main": [
        [
          {
            "node": "Get Reference Playbooks From GitHub",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Supabase Vector Insert (Resolved Incidents)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Historical RAG Model": {
      "ai_languageModel": [
        [
          {
            "node": "Threat Intel Enrichment Agent",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser - Threat Intel",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Intelligence": {
      "main": [
        [
          {
            "node": "Report Synthesizer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Playbook Routing Agent": {
      "main": [
        [
          {
            "node": "Fetch Full Playbook from Supabase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Playbook Text Extractor": {
      "main": [
        [
          {
            "node": "Merge All Intelligence",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Ingestion Branches": {
      "main": [
        [
          {
            "node": "Supabase Vector Insert (Playbooks Ingestion)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Report Synthesizer Agent": {
      "ai_languageModel": [
        [
          {
            "node": "Report Synthesizer",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser - Synthesizer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Threat Intel Data Formatter": {
      "main": [
        [
          {
            "node": "Merge All Intelligence",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Gemini Embeddings (Playbooks)": {
      "ai_embedding": [
        [
          {
            "node": "Reference Playbooks Vector Store",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Historical RAG Data Formatter": {
      "main": [
        [
          {
            "node": "Merge All Intelligence",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Threat Intel Enrichment Agent": {
      "main": [
        [
          {
            "node": "Threat Intel Data Formatter",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Test Incidents From GitHub": {
      "main": [
        [
          {
            "node": "Download Test Incidents From GitHub",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Historical Incidents RAG Agent": {
      "main": [
        [
          {
            "node": "Historical RAG Data Formatter",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Resolved Incidents Data Loader": {
      "ai_document": [
        [
          {
            "node": "Supabase Vector Insert (Resolved Incidents)",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "Reference Playbooks Data Loader": {
      "ai_document": [
        [
          {
            "node": "Supabase Vector Insert (Playbooks Ingestion)",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "Resolved Incidents Vector Store": {
      "ai_tool": [
        [
          {
            "node": "Historical Incidents RAG Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Reference Playbooks Vector Store": {
      "ai_tool": [
        [
          {
            "node": "Playbook Routing Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Full Playbook from Supabase": {
      "main": [
        [
          {
            "node": "Playbook Text Extractor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Embeddings (Historical RAG)": {
      "ai_embedding": [
        [
          {
            "node": "Resolved Incidents Vector Store",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Get Resolved Incidents from GitHub": {
      "main": [
        [
          {
            "node": "Download Resolved Incidents From GitHub",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait (To Avoid Hitting API Limits)": {
      "main": [
        [
          {
            "node": "Loop Over Documents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Test Incidents From GitHub": {
      "main": [
        [
          {
            "node": "Create a Row of Test Incident on Supabase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Reference Playbooks From GitHub": {
      "main": [
        [
          {
            "node": "Download Reference Playbooks From GitHub",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Ingestion Branches",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Prepare Markdown Document & Metadata": {
      "main": [
        [
          {
            "node": "Loop Over Documents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Embeddings (Resolved Incidents)": {
      "ai_embedding": [
        [
          {
            "node": "Supabase Vector Insert (Resolved Incidents)",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser - Synthesizer": {
      "ai_outputParser": [
        [
          {
            "node": "Report Synthesizer",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Download Resolved Incidents From GitHub": {
      "main": [
        [
          {
            "node": "Prepare Markdown Document & Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Embeddings (Playbooks Ingestion)": {
      "ai_embedding": [
        [
          {
            "node": "Supabase Vector Insert (Playbooks Ingestion)",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser - Threat Intel": {
      "ai_outputParser": [
        [
          {
            "node": "Threat Intel Enrichment Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Download Reference Playbooks From GitHub": {
      "main": [
        [
          {
            "node": "Merge Ingestion Branches",
            "type": "main",
            "index": 1
          },
          {
            "node": "Summarize Reference Playbooks for Embeddings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser - Historical RAG": {
      "ai_outputParser": [
        [
          {
            "node": "Historical Incidents RAG Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser - Playbook Router": {
      "ai_outputParser": [
        [
          {
            "node": "Playbook Routing Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Supabase Vector Insert (Resolved Incidents)": {
      "main": [
        [
          {
            "node": "Wait (To Avoid Hitting API Limits)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarize Reference Playbooks for Embeddings": {
      "main": [
        [
          {
            "node": "Merge Ingestion Branches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase Vector Insert (Playbooks Ingestion)": {
      "main": [
        [
          {
            "node": "Get Test Incidents From GitHub",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Recursive Character Text Splitter (Resolved Incidents)": {
      "ai_textSplitter": [
        [
          {
            "node": "Resolved Incidents Data Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    },
    "Recursive Character Text Splitter (Playbooks Ingestion)": {
      "ai_textSplitter": [
        [
          {
            "node": "Reference Playbooks Data Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    }
  }
}