{
  "id": "fgZqLNO7d78wPuFS",
  "meta": {
    "builderVariant": "mcp",
    "aiBuilderAssisted": true
  },
  "name": "Wazuh Alert Intelligence, Notification & Reporting",
  "tags": [],
  "nodes": [
    {
      "id": "685130d3-2fe0-40d0-9cf9-cc190292226f",
      "name": "Run workflow manually",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        0,
        160
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "6f547050-f011-4bdd-9dea-845eb5bd3d28",
      "name": "Use manual reporting mode",
      "type": "n8n-nodes-base.set",
      "position": [
        240,
        160
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "run-mode-manual",
              "name": "runMode",
              "type": "string",
              "value": "manual_report"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "4d9f37df-7afd-4b01-a224-d8b644c45185",
      "name": "Configure alert intelligence workflow",
      "type": "n8n-nodes-base.set",
      "position": [
        528,
        528
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "organization-name",
              "name": "organizationName",
              "type": "string",
              "value": "Example Organization"
            },
            {
              "id": "organization-key",
              "name": "organizationKey",
              "type": "string",
              "value": "example-organization"
            },
            {
              "id": "indexer-url",
              "name": "wazuhIndexerUrl",
              "type": "string",
              "value": "https://wazuh-indexer.example.internal:9200"
            },
            {
              "id": "gotenberg-url",
              "name": "gotenbergUrl",
              "type": "string",
              "value": "http://gotenberg:3000"
            },
            {
              "id": "sender-name",
              "name": "senderName",
              "type": "string",
              "value": "Wazuh Security Intelligence"
            },
            {
              "id": "sender-email",
              "name": "senderEmail",
              "type": "string",
              "value": "security@example.com"
            },
            {
              "id": "critical-min",
              "name": "criticalLevelMin",
              "type": "number",
              "value": 12
            },
            {
              "id": "high-min",
              "name": "highLevelMin",
              "type": "number",
              "value": 10
            },
            {
              "id": "medium-min",
              "name": "mediumLevelMin",
              "type": "number",
              "value": 7
            },
            {
              "id": "low-min",
              "name": "lowLevelMin",
              "type": "number",
              "value": 3
            },
            {
              "id": "immediate-lookback",
              "name": "immediateLookbackMinutes",
              "type": "number",
              "value": 15
            },
            {
              "id": "manual-test-lookback",
              "name": "manualTestLookbackDays",
              "type": "number",
              "value": 30
            },
            {
              "id": "technical-limit",
              "name": "technicalEvidenceLimit",
              "type": "number",
              "value": 100
            },
            {
              "id": "generate-pdf",
              "name": "generatePdf",
              "type": "boolean",
              "value": false
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "8782615e-10ea-4c3f-9006-9256d7c76511",
      "name": "Check for urgent alerts every 5 minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        400
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes"
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "e0330028-c520-42e2-a3bf-d9545dd02b60",
      "name": "Use immediate notification mode",
      "type": "n8n-nodes-base.set",
      "position": [
        240,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "run-mode-immediate",
              "name": "runMode",
              "type": "string",
              "value": "immediate"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "99bd9e80-a2ee-4f79-9946-4909382f483e",
      "name": "Build weekly reports",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        640
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 7
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "ec15471c-29d9-4b3d-aa14-603643838a08",
      "name": "Use weekly reporting mode",
      "type": "n8n-nodes-base.set",
      "position": [
        240,
        640
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "run-mode-weekly",
              "name": "runMode",
              "type": "string",
              "value": "weekly"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "7fd905c5-d00c-4c34-8900-d1da4ba85456",
      "name": "Build monthly reports",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        880
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months",
              "triggerAtHour": 7
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "5835e845-7396-4fa7-82d8-9d31d82feeb6",
      "name": "Use monthly reporting mode",
      "type": "n8n-nodes-base.set",
      "position": [
        240,
        880
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "run-mode-monthly",
              "name": "runMode",
              "type": "string",
              "value": "monthly"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a52c1364-e5bf-45c7-ac46-84f350d80e9e",
      "name": "Load enabled delivery profiles",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        768,
        528
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "organizationKey",
              "keyValue": "={{ $(\"Configure alert intelligence workflow\").first().json.organizationKey }}"
            },
            {
              "keyName": "enabled",
              "condition": "isTrue"
            }
          ]
        },
        "matchType": "allConditions",
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "x2D4WxDNA6gIdbNN",
          "cachedResultName": "wazuh_alert_delivery_profiles"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "5af5e65f-9003-4c53-a894-4ec64e7ae572",
      "name": "Prepare bounded Wazuh alert query",
      "type": "n8n-nodes-base.code",
      "position": [
        1008,
        528
      ],
      "parameters": {
        "jsCode": "const config = $('Configure alert intelligence workflow').first().json;\nconst now = new Date();\nlet from = new Date(now);\n\nif (config.runMode === 'immediate') from.setMinutes(from.getMinutes() - config.immediateLookbackMinutes);\nelse if (config.runMode === 'weekly') from.setDate(from.getDate() - 7);\nelse if (config.runMode === 'monthly' || config.runMode === 'manual_report') from.setDate(from.getDate() - 30);\nelse if (config.runMode === 'immediate_test') from.setDate(from.getDate() - config.manualTestLookbackDays);\n\nconst threatDetection = {\n  bool: {\n    should: [\n      { exists: { field: 'rule.mitre.tactic' } },\n      { exists: { field: 'rule.mitre.technique' } },\n    ],\n    minimum_should_match: 1,\n  },\n};\nconst fileIntegrity = {\n  bool: {\n    should: [\n      { term: { 'rule.groups': 'syscheck' } },\n      { term: { 'rule.groups': 'fim' } },\n    ],\n    minimum_should_match: 1,\n  },\n};\nconst configurationAssessment = { term: { 'rule.groups': 'sca' } };\nconst vulnerabilityDetection = { term: { 'rule.groups': 'vulnerability-detector' } };\nconst authentication = {\n  bool: {\n    should: [\n      { term: { 'rule.groups': 'authentication_success' } },\n      { term: { 'rule.groups': 'authentication_failed' } },\n      { term: { 'rule.groups': 'authentication_failures' } },\n      { term: { 'rule.groups': 'pam' } },\n      { term: { 'rule.groups': 'sshd' } },\n    ],\n    minimum_should_match: 1,\n  },\n};\nconst operational = {\n  bool: {\n    should: [\n      { term: { 'rule.groups': 'ossec' } },\n      { term: { 'rule.groups': 'docker' } },\n      { term: { 'rule.groups': 'rootcheck' } },\n      { term: { 'rule.groups': 'agentless' } },\n    ],\n    minimum_should_match: 1,\n  },\n};\nconst compliance = {\n  bool: {\n    should: [\n      { exists: { field: 'rule.pci_dss' } },\n      { exists: { field: 'rule.hipaa' } },\n      { exists: { field: 'rule.gdpr' } },\n      { exists: { field: 'rule.nist_800_53' } },\n      { exists: { field: 'rule.tsc' } },\n      { exists: { field: 'rule.cis' } },\n      { exists: { field: 'rule.soc_2' } },\n    ],\n    minimum_should_match: 1,\n  },\n};\nconst recognizedContext = {\n  bool: {\n    should: [\n      threatDetection,\n      fileIntegrity,\n      configurationAssessment,\n      vulnerabilityDetection,\n      authentication,\n      operational,\n      compliance,\n    ],\n    minimum_should_match: 1,\n  },\n};\n\nreturn [{\n  json: {\n    runMode: config.runMode,\n    periodStart: from.toISOString(),\n    periodEnd: now.toISOString(),\n    query: {\n      size: config.technicalEvidenceLimit,\n      track_total_hits: true,\n      query: ['immediate', 'immediate_test'].includes(config.runMode)\n        ? {\n            bool: {\n              filter: [\n                { range: { timestamp: { gte: from.toISOString(), lte: now.toISOString() } } },\n                { range: { 'rule.level': { gte: config.highLevelMin } } },\n              ],\n            },\n          }\n        : { range: { timestamp: { gte: from.toISOString(), lte: now.toISOString() } } },\n      sort: [{ timestamp: { order: 'desc', unmapped_type: 'date' } }],\n      aggs: {\n        by_level: { terms: { field: 'rule.level', size: 20 } },\n        top_rules: {\n          terms: { field: 'rule.id', size: 25 },\n          aggs: {\n            sample: {\n              top_hits: {\n                size: 1,\n                _source: ['timestamp', 'rule', 'agent', 'data', 'decoder', 'location'],\n              },\n            },\n          },\n        },\n        top_agents: { terms: { field: 'agent.name', size: 15, missing: 'unassigned' } },\n        use_case_coverage: {\n          filters: {\n            filters: {\n              threat_detection: threatDetection,\n              file_integrity_monitoring: fileIntegrity,\n              security_configuration_assessment: configurationAssessment,\n              vulnerability_detection: vulnerabilityDetection,\n              authentication_monitoring: authentication,\n              system_operational_monitoring: operational,\n              compliance_context: compliance,\n              other_unclassified: { bool: { must_not: [recognizedContext] } },\n            },\n          },\n        },\n        threat_mitre: {\n          filter: threatDetection,\n          aggs: {\n            tactics: { terms: { field: 'rule.mitre.tactic', size: 15 } },\n            techniques: { terms: { field: 'rule.mitre.technique', size: 15 } },\n          },\n        },\n        event_trend: {\n          date_histogram: {\n            field: 'timestamp',\n            fixed_interval: ['immediate', 'immediate_test'].includes(config.runMode) ? '1m' : '1d',\n            min_doc_count: 0,\n          },\n        },\n      },\n    },\n  },\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a70db907-e638-43d3-8870-788f6d8d741f",
      "name": "Query Wazuh alert evidence",
      "type": "n8n-nodes-base.httpRequest",
      "maxTries": 3,
      "position": [
        1248,
        528
      ],
      "parameters": {
        "url": "={{ $(\"Configure alert intelligence workflow\").first().json.wazuhIndexerUrl.replace(/\\/$/, \"\") + \"/wazuh-alerts*/_search\" }}",
        "method": "POST",
        "options": {
          "timeout": 30000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "allowUnauthorizedCerts": true
        },
        "jsonBody": "={{ $json.query }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "id": "140b9407-89fc-46ff-af15-0e54c6b79e20",
      "name": "Build deterministic security context",
      "type": "n8n-nodes-base.code",
      "position": [
        1632,
        224
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst config = $('Configure alert intelligence workflow').first().json;\nconst queryMeta = $('Prepare bounded Wazuh alert query').first().json;\nconst hits = response.hits?.hits || [];\nconst total = Number(response.hits?.total?.value ?? response.hits?.total ?? hits.length);\nconst aggs = response.aggregations || {};\nconst severityBuckets = aggs.by_level?.buckets || [];\n\nconst countRange = (min, max = Infinity) => severityBuckets\n  .filter((bucket) => Number(bucket.key) >= min && Number(bucket.key) <= max)\n  .reduce((sum, bucket) => sum + Number(bucket.doc_count || 0), 0);\n\nconst counts = {\n  critical: countRange(config.criticalLevelMin),\n  high: countRange(config.highLevelMin, config.criticalLevelMin - 1),\n  medium: countRange(config.mediumLevelMin, config.highLevelMin - 1),\n  low: countRange(config.lowLevelMin, config.mediumLevelMin - 1),\n};\n\nconst normalizeGroups = (rule = {}) => (Array.isArray(rule.groups) ? rule.groups : [rule.groups])\n  .filter(Boolean)\n  .map((group) => String(group).toLowerCase());\nconst hasAny = (groups, candidates) => candidates.some((candidate) => groups.includes(candidate));\nconst classifyRule = (rule = {}) => {\n  const groups = normalizeGroups(rule);\n  const categories = [];\n  if ((rule.mitre?.tactic?.length || 0) > 0 || (rule.mitre?.technique?.length || 0) > 0) categories.push('Threat Detection');\n  if (hasAny(groups, ['syscheck', 'fim'])) categories.push('File Integrity Monitoring');\n  if (groups.includes('sca')) categories.push('Security Configuration Assessment');\n  if (groups.includes('vulnerability-detector')) categories.push('Vulnerability Detection');\n  if (hasAny(groups, ['authentication_success', 'authentication_failed', 'authentication_failures', 'pam', 'sshd'])) categories.push('Authentication Monitoring');\n  if (hasAny(groups, ['ossec', 'docker', 'rootcheck', 'agentless'])) categories.push('System & Operational Monitoring');\n  if (['pci_dss', 'hipaa', 'gdpr', 'nist_800_53', 'tsc', 'cis', 'soc_2'].some((field) => (rule[field]?.length || 0) > 0)) categories.push('Compliance Context');\n  return categories.length ? categories : ['Other / Unclassified'];\n};\n\nconst topRules = (aggs.top_rules?.buckets || []).map((bucket) => {\n  const source = bucket.sample?.hits?.hits?.[0]?._source || {};\n  return {\n    id: String(bucket.key),\n    count: Number(bucket.doc_count || 0),\n    description: source.rule?.description || 'No description',\n    level: Number(source.rule?.level || 0),\n    groups: source.rule?.groups || [],\n    useCases: classifyRule(source.rule || {}),\n  };\n});\n\nconst topAgents = (aggs.top_agents?.buckets || []).map((bucket) => ({\n  name: String(bucket.key),\n  count: Number(bucket.doc_count || 0),\n}));\n\nconst labels = {\n  threat_detection: 'Threat Detection',\n  file_integrity_monitoring: 'File Integrity Monitoring',\n  security_configuration_assessment: 'Security Configuration Assessment',\n  vulnerability_detection: 'Vulnerability Detection',\n  authentication_monitoring: 'Authentication Monitoring',\n  system_operational_monitoring: 'System & Operational Monitoring',\n  compliance_context: 'Compliance Context',\n  other_unclassified: 'Other / Unclassified',\n};\nconst coverageBuckets = aggs.use_case_coverage?.buckets || {};\nconst useCaseCoverage = Object.entries(labels).map(([key, name]) => {\n  const count = Number(coverageBuckets[key]?.doc_count || 0);\n  return {\n    key,\n    name,\n    count,\n    percentage: total > 0 ? Number(((count / total) * 100).toFixed(1)) : 0,\n  };\n});\n\nconst threatMitreCount = Number(aggs.threat_mitre?.doc_count || 0);\nconst tactics = (aggs.threat_mitre?.tactics?.buckets || []).map((bucket) => ({\n  name: String(bucket.key),\n  count: Number(bucket.doc_count || 0),\n}));\nconst techniques = (aggs.threat_mitre?.techniques?.buckets || []).map((bucket) => ({\n  name: String(bucket.key),\n  count: Number(bucket.doc_count || 0),\n}));\n\nconst evidence = hits.map((hit) => {\n  const source = hit._source || {};\n  const agent = source.agent?.name || source.agent?.id || 'unassigned';\n  const ruleId = String(source.rule?.id || '');\n  const timestamp = source.timestamp || '';\n  const location = source.location || '';\n  return {\n    eventId: String(hit._id || source.id || [timestamp, agent, ruleId, location].join('|')),\n    timestamp,\n    agent,\n    ruleId,\n    level: Number(source.rule?.level || 0),\n    description: source.rule?.description || '',\n    useCases: classifyRule(source.rule || {}),\n    tactics: source.rule?.mitre?.tactic || [],\n    techniques: source.rule?.mitre?.technique || [],\n    location,\n    decoder: source.decoder?.name || '',\n    sourceIp: source.data?.srcip || source.data?.src_ip || source.srcip || '',\n    destinationIp: source.data?.dstip || source.data?.dst_ip || source.dstip || '',\n    user: source.data?.srcuser || source.data?.dstuser || source.data?.user || '',\n  };\n});\n\nconst reportMode = ['manual_report', 'weekly', 'monthly'].includes(config.runMode);\nconst immediateMode = ['immediate', 'immediate_test'].includes(config.runMode);\nconst urgentEvents = evidence.filter((event) => event.level >= config.highLevelMin);\n\nreturn [{\n  json: {\n    runMode: config.runMode,\n    periodStart: queryMeta.periodStart,\n    periodEnd: queryMeta.periodEnd,\n    total,\n    counts,\n    topRules,\n    topAgents,\n    useCaseCoverage,\n    useCaseCoverageIsNonExclusive: true,\n    threatMitreCount,\n    threatMitrePercentage: total > 0 ? Number(((threatMitreCount / total) * 100).toFixed(1)) : 0,\n    tactics,\n    techniques,\n    evidence,\n    reportMode,\n    immediateMode,\n    urgentEvents,\n    shouldContinue: reportMode || (immediateMode && urgentEvents.length > 0),\n    aiContext: {\n      reportingWindow: { start: queryMeta.periodStart, end: queryMeta.periodEnd },\n      totalAlerts: total,\n      severityCounts: counts,\n      wazuhUseCaseCoverage: useCaseCoverage,\n      coverageInterpretation: 'Use-case counts are non-exclusive because one Wazuh event can support multiple security contexts.',\n      topRules: topRules.slice(0, 12).map(({ id, count, description, level, groups, useCases }) => ({ id, count, description, level, groups, useCases })),\n      threatDetectionMitre: {\n        mappedAlertCount: threatMitreCount,\n        percentageOfAllAlerts: total > 0 ? Number(((threatMitreCount / total) * 100).toFixed(1)) : 0,\n        tactics: tactics.slice(0, 12),\n        techniques: techniques.slice(0, 12),\n      },\n      dataHandling: 'Only aggregate metrics, rule summaries, Wazuh use-case coverage, and MITRE context for Threat Detection are provided. Asset names and complete event records are excluded.',\n    },\n  },\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "d1c6cb08-6e4c-4e68-a1fe-b4c9b64c8fdf",
      "name": "Route reports and immediate alerts",
      "type": "n8n-nodes-base.if",
      "position": [
        1952,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "is-report",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.deliveryType }}",
              "rightValue": "report"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f12bf846-64ab-4951-a266-c71d30499418",
      "name": "Convert report HTML to index file",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        2560,
        368
      ],
      "parameters": {
        "options": {
          "encoding": "utf8",
          "fileName": "index.html"
        },
        "operation": "toText",
        "sourceProperty": "reportHtml"
      },
      "typeVersion": 1.1
    },
    {
      "id": "e6c7fc48-4437-4849-9d4a-1dfcdc374dfb",
      "name": "Render report PDF with Gotenberg",
      "type": "n8n-nodes-base.httpRequest",
      "maxTries": 3,
      "position": [
        2800,
        368
      ],
      "parameters": {
        "url": "={{ $(\"Configure alert intelligence workflow\").first().json.gotenbergUrl.replace(/\\/$/, \"\") + \"/forms/chromium/convert/html\" }}",
        "method": "POST",
        "options": {
          "timeout": 120000,
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "reportPdf"
            }
          }
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "name": "files",
              "parameterType": "formBinaryData",
              "inputDataFieldName": "data"
            }
          ]
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "id": "56022da3-9523-4e9b-bbec-da9a1fe82f9f",
      "name": "Send HTML report with PDF attachment",
      "type": "n8n-nodes-base.emailSend",
      "maxTries": 3,
      "position": [
        3280,
        368
      ],
      "parameters": {
        "html": "={{ $(\"Build role-based deliveries\").item.json.emailHtml }}",
        "options": {
          "fileAttachments": "reportPdf",
          "appendAttribution": false
        },
        "subject": "={{ $(\"Build role-based deliveries\").item.json.subject }}",
        "toEmail": "={{ $(\"Build role-based deliveries\").item.json.recipientEmail }}",
        "fromEmail": "={{ $(\"Configure alert intelligence workflow\").first().json.senderName + \" <\" + $(\"Configure alert intelligence workflow\").first().json.senderEmail + \">\" }}"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.1,
      "waitBetweenTries": 5000
    },
    {
      "id": "d5909367-b150-4f9e-a0f4-2d52007682a8",
      "name": "Log report delivery",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3520,
        368
      ],
      "parameters": {
        "columns": {
          "value": {
            "runId": "={{ $(\"Build role-based deliveries\").item.json.runId }}",
            "sentAt": "={{ $now.toISO() }}",
            "severity": "={{ $(\"Build role-based deliveries\").item.json.severity }}",
            "periodEnd": "={{ $(\"Build role-based deliveries\").item.json.periodEnd }}",
            "deliveryKey": "={{ $(\"Build role-based deliveries\").item.json.deliveryKey }}",
            "periodStart": "={{ $(\"Build role-based deliveries\").item.json.periodStart }}",
            "deliveryType": "={{ $(\"Build role-based deliveries\").item.json.deliveryType }}",
            "deliveryStatus": "sent",
            "recipientEmail": "={{ $(\"Build role-based deliveries\").item.json.recipientEmail }}",
            "audienceProfile": "={{ $(\"Build role-based deliveries\").item.json.audienceProfile }}",
            "metadata_object": "={{ JSON.stringify({ executionId: $execution.id }) }}",
            "organizationKey": "={{ $(\"Build role-based deliveries\").item.json.organizationKey }}",
            "providerMessageId": "={{ $json.messageId || $json.response || \"accepted\" }}"
          },
          "schema": [
            {
              "id": "deliveryKey",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "deliveryKey",
              "defaultMatch": false
            },
            {
              "id": "runId",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "runId",
              "defaultMatch": false
            },
            {
              "id": "organizationKey",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "organizationKey",
              "defaultMatch": false
            },
            {
              "id": "recipientEmail",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "recipientEmail",
              "defaultMatch": false
            },
            {
              "id": "deliveryType",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "deliveryType",
              "defaultMatch": false
            },
            {
              "id": "audienceProfile",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "audienceProfile",
              "defaultMatch": false
            },
            {
              "id": "severity",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "severity",
              "defaultMatch": false
            },
            {
              "id": "periodStart",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "periodStart",
              "defaultMatch": false
            },
            {
              "id": "periodEnd",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "periodEnd",
              "defaultMatch": false
            },
            {
              "id": "deliveryStatus",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "deliveryStatus",
              "defaultMatch": false
            },
            {
              "id": "providerMessageId",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "providerMessageId",
              "defaultMatch": false
            },
            {
              "id": "errorSummary",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "errorSummary",
              "defaultMatch": false
            },
            {
              "id": "sentAt",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "sentAt",
              "defaultMatch": false
            },
            {
              "id": "metadata_object",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "metadata_object",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "eFRljEojwfT812EE",
          "cachedResultName": "wazuh_alert_delivery_log"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "f2b72615-d005-4f45-962c-49b8a40ad7e7",
      "name": "Send immediate operational notification",
      "type": "n8n-nodes-base.emailSend",
      "maxTries": 3,
      "position": [
        2592,
        880
      ],
      "parameters": {
        "html": "={{ $(\"Build role-based deliveries\").item.json.emailHtml }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $(\"Build role-based deliveries\").item.json.subject }}",
        "toEmail": "={{ $(\"Build role-based deliveries\").item.json.recipientEmail }}",
        "fromEmail": "={{ $(\"Configure alert intelligence workflow\").first().json.senderName + \" <\" + $(\"Configure alert intelligence workflow\").first().json.senderEmail + \">\" }}"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.1,
      "waitBetweenTries": 5000
    },
    {
      "id": "45d04284-b6a1-43c2-baa7-642fbf17fd87",
      "name": "Log immediate notification delivery",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        2832,
        880
      ],
      "parameters": {
        "columns": {
          "value": {
            "runId": "={{ $(\"Build role-based deliveries\").item.json.runId }}",
            "sentAt": "={{ $now.toISO() }}",
            "severity": "={{ $(\"Build role-based deliveries\").item.json.severity }}",
            "periodEnd": "={{ $(\"Build role-based deliveries\").item.json.periodEnd }}",
            "deliveryKey": "={{ $(\"Build role-based deliveries\").item.json.deliveryKey }}",
            "periodStart": "={{ $(\"Build role-based deliveries\").item.json.periodStart }}",
            "deliveryType": "={{ $(\"Build role-based deliveries\").item.json.deliveryType }}",
            "errorSummary": "",
            "deliveryStatus": "sent",
            "recipientEmail": "={{ $(\"Build role-based deliveries\").item.json.recipientEmail }}",
            "audienceProfile": "={{ $(\"Build role-based deliveries\").item.json.audienceProfile }}",
            "metadata_object": "={{ JSON.stringify({ executionId: $execution.id, eventId: $(\"Build role-based deliveries\").item.json.eventId, ruleId: $(\"Build role-based deliveries\").item.json.ruleId, agent: $(\"Build role-based deliveries\").item.json.agent }) }}",
            "organizationKey": "={{ $(\"Build role-based deliveries\").item.json.organizationKey }}",
            "providerMessageId": "={{ $json.messageId || $json.response || \"accepted\" }}"
          },
          "schema": [
            {
              "id": "deliveryKey",
              "type": "string",
              "display": true,
              "displayName": "deliveryKey"
            },
            {
              "id": "runId",
              "type": "string",
              "display": true,
              "displayName": "runId"
            },
            {
              "id": "organizationKey",
              "type": "string",
              "display": true,
              "displayName": "organizationKey"
            },
            {
              "id": "recipientEmail",
              "type": "string",
              "display": true,
              "displayName": "recipientEmail"
            },
            {
              "id": "deliveryType",
              "type": "string",
              "display": true,
              "displayName": "deliveryType"
            },
            {
              "id": "audienceProfile",
              "type": "string",
              "display": true,
              "displayName": "audienceProfile"
            },
            {
              "id": "severity",
              "type": "string",
              "display": true,
              "displayName": "severity"
            },
            {
              "id": "periodStart",
              "type": "date",
              "display": true,
              "displayName": "periodStart"
            },
            {
              "id": "periodEnd",
              "type": "date",
              "display": true,
              "displayName": "periodEnd"
            },
            {
              "id": "deliveryStatus",
              "type": "string",
              "display": true,
              "displayName": "deliveryStatus"
            },
            {
              "id": "providerMessageId",
              "type": "string",
              "display": true,
              "displayName": "providerMessageId"
            },
            {
              "id": "errorSummary",
              "type": "string",
              "display": true,
              "displayName": "errorSummary"
            },
            {
              "id": "sentAt",
              "type": "date",
              "display": true,
              "displayName": "sentAt"
            },
            {
              "id": "metadata_object",
              "type": "string",
              "display": true,
              "displayName": "metadata_object"
            }
          ],
          "mappingMode": "defineBelow"
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "eFRljEojwfT812EE",
          "cachedResultName": "wazuh_alert_delivery_log"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "ce5ecdaf-899f-4a3c-a617-59b0f06aaa20",
      "name": "Workflow overview and setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -832,
        -48
      ],
      "parameters": {
        "color": "#F3FF4D",
        "width": 680,
        "height": 2676,
        "content": "## Turn Wazuh alerts into AI-enriched notifications and role-based reports\n\n**Wazuh** is an open-source SIEM and XDR platform supporting Threat Detection, File Integrity Monitoring, Security Configuration Assessment, Vulnerability Detection, Authentication Monitoring, operational monitoring, and compliance. Wazuh collects and indexes the evidence; n8n converts it into immediate notifications and audience-specific reports.\n\n### How it works\n\n\u2192 Manual, weekly, monthly, and five-minute monitoring modes share one bounded Wazuh Indexer query. Immediate modes restrict the query to Critical and High rule levels.\n\n\u2192 Severity counts, reporting windows, Wazuh use-case coverage, recipient selection, cadence, and delivery policy are deterministic. Gemini cannot change them.\n\n\u2192 Use-case coverage is **non-exclusive**. One alert can support multiple contexts, so category counts are not expected to sum to the total.\n\n\u2192 MITRE ATT&CK tactics and techniques are calculated only from the **Threat Detection subset** containing a Wazuh MITRE mapping. Other Wazuh use cases remain visible in their own categories.\n\n\u2192 Gemini receives minimized aggregates, rule summaries, use-case coverage, and MITRE context. Asset names and complete events are excluded. Validated JSON adds narrative, technical observations, recommendations, and limitations.\n\n\u2192 Delivery profiles explicitly select the recipient, audience (**Executive**, **Technical**, or **Combined**), cadence, and allowed immediate severities.\n\n\u2192 All report audiences use one email-safe visual system based on responsive presentation tables. This preserves compact severity cards in Gmail and common email clients while adapting content depth to the selected audience.\n\n\u2192 Executive reports emphasize posture, leadership context, coverage, and priority actions. Technical reports add rules, assets, MITRE context, observations, and evidence. Combined reports append the complete technical analysis to the executive view.\n\n\u2192 Immediate notifications are event-driven: each email contains exactly one Critical or High Wazuh event. They do not include total-alert metrics, severity distributions, trends, or reporting-period summaries.\n\n\u2192 Complete HTML email is the default. Optional PDF attachments render the same HTML through Gotenberg only when `generatePdf=true`.\n\n\u2192 Every successful report or immediate alert is recorded in the delivery log Data Table. Before an immediate email is sent, its organization/profile/event delivery key is checked against the same table so a previously delivered Wazuh event is not sent twice.\n\n### Setup\n\n\u2192 Create **wazuh_alert_delivery_profiles** with: `profileKey`, `organizationKey`, `recipientEmail`, `recipientName`, `audienceProfile`, `receiveImmediateAlerts`, `immediateSeverities_object`, `receiveReports`, `reportCadence`, `timezone`, and `enabled`.\n\n\u2192 Store immediate severities as a JSON string, for example `[\"critical\",\"high\"]`.\n\n\u2192 Create **wazuh_alert_delivery_log** with the columns mapped in the logging nodes.\n\n\u2192 Add a read-only Wazuh Indexer Basic Auth credential limited to `wazuh-alerts*`, an SMTP credential, and a Google Gemini API credential.\n\n\u2192 Replace deployment-specific values in **Configure alert intelligence workflow**.\n\n\u2192 Keep **Use manual reporting mode** set to `manual_report` for on-demand validation. Use `immediate_test` temporarily to send only the newest eligible event with a `[TEST]` subject, then restore `manual_report`.\n\n\u2192 Verify Executive, Technical, and Combined HTML emails and the delivery log before activating required schedules.\n\n\u2192 For unattended schedules, connect a published n8n Error Trigger workflow that notifies the workflow owner when a production execution fails.\n\n### Customization\n\n\u2192 Assign different addresses or distribution lists to delivery profiles. The workflow never infers a recipient role.\n\n\u2192 Adjust severity thresholds, cadence, lookback windows, evidence limits, immediate severity lists, and deterministic group-to-use-case mappings.\n\n\u2192 The shared visual shell can be branded centrally in **Build role-based deliveries** without changing audience routing or evidence logic.\n\n\u2192 `generatePdf=false` needs no document service. For `true`, deploy Gotenberg privately, configure `gotenbergUrl`, and verify the attached PDF matches the HTML email.\n\n\u2192 Add Slack, Microsoft Teams, or webhook channels after the delivery builder while preserving explicit routing rules.\n\n### Security\n\n\u2192 Store secrets only in n8n Credentials. Never paste passwords, tokens, or API keys into nodes or Data Tables.\n\n\u2192 Use least-privilege, read-only Wazuh access. Review data residency and external-AI policies before enabling Gemini.\n\n\u2192 Gemini enriches narrative only. Analysts must validate recommendations before containment, suppression, tuning, decoder changes, or automated response.\n\n\u2192 The template does not silently create or select an organization-specific failure channel; configure and test the Error Trigger workflow before production use."
      },
      "typeVersion": 1
    },
    {
      "id": "a0006438-1b56-4f08-aec8-0d964d7b6740",
      "name": "Collection and least privilege",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 1540,
        "height": 1176,
        "content": "### 1 - Trigger, configure, and collect\nManual and scheduled modes share one bounded query. Credentials stay in n8n Credentials, and the Wazuh account has read-only access only to **wazuh-alerts***."
      },
      "typeVersion": 1
    },
    {
      "id": "8940f7a4-ea90-4186-a639-a9382c4e9277",
      "name": "Deterministic analysis and routing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1520,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 892,
        "height": 1176,
        "content": "### 2 - Classify, enrich, and route\nWazuh evidence is represented through non-exclusive use cases: Threat Detection, File Integrity Monitoring, Security Configuration Assessment, Vulnerability Detection, Authentication Monitoring, System & Operational Monitoring, Compliance Context, and Other. MITRE ATT&CK is calculated only for the Threat Detection subset. Gemini receives minimized aggregates and never selects recipients or changes severity."
      },
      "typeVersion": 1
    },
    {
      "id": "1b99f4b1-ef2c-4576-8da2-b89f5f510742",
      "name": "PDF reporting pipeline",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2480,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 1260,
        "height": 648,
        "content": "### 3 - Deliver HTML and optionally attach PDF\nComplete HTML email is the default and needs no document service. When **generatePdf=true**, a private Gotenberg service renders the same HTML and the PDF is attached."
      },
      "typeVersion": 1
    },
    {
      "id": "170c6ad4-dae2-46e6-b496-2772288cc9b0",
      "name": "Immediate notification pipeline",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2480,
        640
      ],
      "parameters": {
        "color": 7,
        "width": 572,
        "height": 488,
        "content": "### 4 - Immediate operational notification\nOnly enabled profiles receive immediate notifications for their configured severities. Use **immediate_test** manually to validate this branch with a clear TEST subject prefix."
      },
      "typeVersion": 1
    },
    {
      "id": "0e838936-4a39-4850-b457-2ebc2a8d5e81",
      "name": "Apply meaningful PDF filenames",
      "type": "n8n-nodes-base.code",
      "position": [
        3040,
        368
      ],
      "parameters": {
        "jsCode": "const reports = $('Build role-based deliveries').all().filter((item) => item.json.deliveryType === 'report');\nreturn $input.all().map((item, index) => {\n if (item.binary?.reportPdf) item.binary.reportPdf.fileName = reports[index]?.json.fileName || 'wazuh-security-report.pdf';\n return item;\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "cc14b369-12c1-46e6-8176-1e70bd07f58f",
      "name": "Continue only when delivery is needed",
      "type": "n8n-nodes-base.if",
      "position": [
        1632,
        528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "delivery-needed",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ String($(\"Build deterministic security context\").first().json.shouldContinue) }}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "193e666b-3690-4dbd-a6e5-442bc0bb22f4",
      "name": "Generate structured security insight with Gemini",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1840,
        528
      ],
      "parameters": {
        "text": "={{ \"You are a cybersecurity reporting assistant. Analyze only the minimized Wazuh summary supplied below.\\nThe payload contains aggregate metrics, rule summaries, and MITRE context only; asset names and complete event records are excluded.\\nDo not invent incidents, assets, business impact, root causes, or remediation facts.\\nSeverity counts, recipient profiles, cadence, routing, and delivery policy are authoritative and must not be changed.\\nIf evidence is insufficient, state that explicitly. Recommendations require analyst validation before containment, suppression, or rule changes.\\nReturn raw JSON only. No markdown fences or prose outside the JSON.\\n\\nMINIMIZED WAZUH SUMMARY:\\n\" + $(\"Build deterministic security context\").first().json.aiContext.toJsonString() }}",
        "batching": {
          "batchSize": 1,
          "delayBetweenBatches": 0
        },
        "messages": {
          "messageValues": [
            {
              "message": "Use only the minimized Wazuh summary. Never change deterministic severity, recipients, cadence, routing, or delivery policy. Never recommend automatic containment or suppression without analyst validation. Return the exact structured JSON required by the parser."
            }
          ]
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9
    },
    {
      "id": "cd112615-8edb-41fd-9902-0eeb259f33d2",
      "name": "Gemini security analysis model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1776,
        800
      ],
      "parameters": {
        "options": {
          "temperature": 0.2,
          "maxOutputTokens": 2500
        },
        "modelName": "models/gemini-3.1-flash-lite"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "ce824ce0-8249-401c-b031-4ff5df91cc4a",
      "name": "Validate Gemini security insight",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1904,
        800
      ],
      "parameters": {
        "prompt": "Instructions:\n--------------\n{instructions}\n--------------\nCompletion:\n--------------\n{completion}\n--------------\nError:\n--------------\n{error}\n--------------\nRepair the completion so it satisfies the schema exactly. Preserve only claims supported by the supplied evidence. Return raw JSON only. DO NOT wrap the output in a markdown code block.",
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "{\"type\":\"object\",\"properties\":{\"executiveSummary\":{\"type\":\"string\"},\"riskNarrative\":{\"type\":\"string\"},\"technicalObservations\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},\"minItems\":1,\"maxItems\":6},\"recommendedActions\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"priority\":{\"type\":\"string\",\"enum\":[\"Immediate\",\"High\",\"Planned\"]},\"action\":{\"type\":\"string\"},\"rationale\":{\"type\":\"string\"}},\"required\":[\"priority\",\"action\",\"rationale\"]},\"minItems\":1,\"maxItems\":6},\"limitations\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},\"maxItems\":4}},\"required\":[\"executiveSummary\",\"riskNarrative\",\"technicalObservations\",\"recommendedActions\",\"limitations\"]}",
        "customizeRetryPrompt": true
      },
      "typeVersion": 1.3
    },
    {
      "id": "913230b0-5403-49c4-b951-217b99a19bc0",
      "name": "Repair invalid Gemini output",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1904,
        976
      ],
      "parameters": {
        "options": {
          "temperature": 0,
          "maxOutputTokens": 2500
        },
        "modelName": "models/gemini-3.1-flash-lite"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "a219af3e-09d5-4bdb-9130-d291bcfcd044",
      "name": "Build role-based deliveries",
      "type": "n8n-nodes-base.code",
      "position": [
        2176,
        528
      ],
      "parameters": {
        "jsCode": "const context = $('Build deterministic security context').first().json;\nconst config = $('Configure alert intelligence workflow').first().json;\nconst profiles = $('Load enabled delivery profiles').all().map((item) => item.json);\nconst chainResult = $input.first().json;\nconst ai = chainResult.output?.output || chainResult.output || chainResult;\nconst counts = context.counts;\n\nconst esc = (value) => String(value ?? '').replace(/[&<>\"']/g, (character) => ({\n  '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n})[character]);\nconst pct = (value, total = context.total) => total > 0\n  ? Math.round((Number(value || 0) / total) * 1000) / 10\n  : 0;\nconst rows = (items, renderer, empty = 'No data available', colspan = 5) => items.length\n  ? items.map(renderer).join('')\n  : `<tr><td colspan=\"${colspan}\" class=\"empty\">${empty}</td></tr>`;\nconst list = (items, renderer) => items?.length\n  ? `<ul class=\"clean-list\">${items.map(renderer).join('')}</ul>`\n  : '<p class=\"muted\">No additional observation was produced.</p>';\n\nconst attention = counts.critical > 0\n  ? { label: 'IMMEDIATE ATTENTION', color: '#b42318', background: '#fef3f2', border: '#fecdca' }\n  : counts.high > 0\n    ? { label: 'ELEVATED ATTENTION', color: '#9a6700', background: '#fff8e7', border: '#f4c95d' }\n    : { label: 'STABLE ACTIVITY', color: '#067647', background: '#ecfdf3', border: '#abefc6' };\nconst generatedAt = new Date().toISOString();\n\nconst commonStyle = `<style>\n@page{size:A4;margin:14mm}\nbody{margin:0;padding:0;background:#eef2f6;color:#172033;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:1.55}\ntable{border-spacing:0;border-collapse:collapse}\n.shell{width:100%;max-width:760px;margin:0 auto;background:#fff}\n.section{padding:0 34px 28px}\n.section-title{font-size:19px;line-height:26px;font-weight:700;color:#172033;margin:0}\n.section-rule{width:42px;height:4px;background:#0aa9b8;font-size:0;line-height:0;margin:10px 0 18px}\n.data-table{width:100%;border:1px solid #d8e0ea;margin:0}\n.data-table th{background:#f3f6f9;color:#41516a;font-size:11px;line-height:16px;text-transform:uppercase;letter-spacing:.4px;padding:10px;border:1px solid #d8e0ea;text-align:left}\n.data-table td{color:#25334a;font-size:12px;line-height:17px;padding:10px;border:1px solid #d8e0ea;vertical-align:top}\n.empty{color:#66758a;text-align:center}\n.muted{color:#66758a}\n.clean-list{margin:0;padding-left:20px;color:#25334a}\n.clean-list li{padding:0 0 8px}\n.page{page-break-before:always;padding-top:20px}\n.footer{padding:22px 34px 30px;color:#748197;font-size:10px;line-height:15px;border-top:1px solid #e4e9f0}\n@media only screen and (max-width:620px){.shell{width:100%!important}.section{padding-left:18px!important;padding-right:18px!important}.mobile-block{display:block!important;width:100%!important}.mobile-center{text-align:center!important}.hide-mobile{display:none!important}}\n</style>`;\n\nconst header = (title, subtitle) => `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">${commonStyle}</head><body><table role=\"presentation\" width=\"100%\" style=\"width:100%;background:#eef2f6\"><tr><td align=\"center\" style=\"padding:24px 10px\"><table role=\"presentation\" class=\"shell\" width=\"760\" style=\"width:760px;max-width:760px;background:#fff;border-top:7px solid #0aa9b8;border-radius:14px 14px 0 0\"><tr><td style=\"background:#0f1d31;padding:32px 34px 30px\"><table role=\"presentation\" width=\"100%\"><tr><td width=\"56\" valign=\"top\"><div style=\"width:46px;height:46px;line-height:46px;background:#0aa9b8;border-radius:12px;color:#fff;text-align:center;font-size:17px;font-weight:700\">WS</div></td><td valign=\"top\"><div style=\"color:#91a2b9;font-size:11px;line-height:16px;letter-spacing:1.3px;text-transform:uppercase\">Wazuh Security Intelligence</div><div style=\"color:#fff;font-size:27px;line-height:34px;font-weight:700;padding-top:4px\">${esc(title)}</div><div style=\"color:#c5cfdd;font-size:12px;line-height:18px;padding-top:8px\">${esc(config.organizationName)} &nbsp;&bull;&nbsp; ${esc(subtitle)}</div></td><td width=\"178\" valign=\"top\" align=\"right\" class=\"hide-mobile\"><span style=\"display:inline-block;color:${attention.color};background:${attention.background};border:1px solid ${attention.border};border-radius:18px;padding:7px 12px;font-size:10px;line-height:14px;font-weight:700\">${attention.label}</span></td></tr></table></td></tr>`;\n\nconst close = (message) => `<tr><td class=\"footer\">${esc(message)}<br>Reporting window: ${esc(context.periodStart)} to ${esc(context.periodEnd)} &nbsp;&bull;&nbsp; Generated ${esc(generatedAt)}</td></tr></table></td></tr></table></body></html>`;\n\nconst activityHero = `<tr><td style=\"padding:26px 34px 20px\"><table role=\"presentation\" width=\"100%\" style=\"background:#f4f7fa;border-radius:14px\"><tr><td style=\"padding:22px 24px\"><div style=\"color:#65758d;font-size:11px;line-height:16px;text-transform:uppercase;letter-spacing:.8px;font-weight:700\">Security activity analyzed</div><div style=\"color:#172033;font-size:38px;line-height:44px;font-weight:700\">${context.total.toLocaleString('en-US')}</div></td><td align=\"right\" style=\"padding:22px 24px;color:#65758d;font-size:12px;line-height:18px\">Wazuh alerts<br>across ${context.useCaseCoverage.length} security contexts</td></tr></table></td></tr>`;\n\nconst severityCell = (label, value, color, border = true) => `<td width=\"25%\" align=\"center\" style=\"width:25%;padding:18px 8px;border-right:${border ? '1px solid #dbe3ec' : '0'}\"><div style=\"color:#65758d;font-size:11px;line-height:16px;font-weight:700;text-transform:uppercase\">${label}</div><div style=\"color:${color};font-size:29px;line-height:36px;font-weight:700\">${Number(value || 0).toLocaleString('en-US')}</div></td>`;\nconst severityStrip = `<tr><td style=\"padding:0 34px 30px\"><table role=\"presentation\" width=\"100%\" style=\"border-top:1px solid #e4e9f0;border-bottom:1px solid #e4e9f0\"><tr>${severityCell('Critical', counts.critical, '#b42318')}${severityCell('High', counts.high, '#d9480f')}${severityCell('Medium', counts.medium, '#c17b00')}${severityCell('Low', counts.low, '#008577', false)}</tr></table></td></tr>`;\n\nconst sectionTitle = (title) => `<div class=\"section-title\">${esc(title)}</div><div class=\"section-rule\">&nbsp;</div>`;\nconst callout = (content, tone = 'amber') => {\n  const colors = tone === 'blue'\n    ? { bg: '#eef4ff', border: '#6172d6', text: '#27364f' }\n    : tone === 'red'\n      ? { bg: '#fef3f2', border: '#d92d20', text: '#5c211d' }\n      : { bg: '#fff8e7', border: '#f2b01e', text: '#543b0a' };\n  return `<div style=\"background:${colors.bg};border-left:5px solid ${colors.border};padding:16px 18px;color:${colors.text};border-radius:0 10px 10px 0\">${content}</div>`;\n};\n\nconst coverageBars = context.useCaseCoverage.slice(0, 8).map((item) => {\n  const width = Math.max(2, Math.min(100, Number(item.percentage || 0)));\n  return `<tr><td width=\"34%\" style=\"padding:7px 10px 7px 0;color:#25334a;font-size:12px\">${esc(item.name)}</td><td width=\"54%\" style=\"padding:7px 10px\"><table role=\"presentation\" width=\"100%\" style=\"background:#e8edf3;border-radius:7px\"><tr><td width=\"${width}%\" style=\"width:${width}%;height:10px;background:#0aa9b8;border-radius:7px;font-size:0;line-height:0\">&nbsp;</td><td style=\"font-size:0;line-height:0\">&nbsp;</td></tr></table></td><td width=\"12%\" align=\"right\" style=\"padding:7px 0;color:#25334a;font-size:12px;font-weight:700\">${item.percentage}%</td></tr>`;\n}).join('');\nconst coverageSection = `<tr><td class=\"section\">${sectionTitle('Wazuh use-case coverage')}<div class=\"muted\" style=\"font-size:11px;margin:-10px 0 10px\">Non-exclusive coverage: one alert may support multiple security contexts.</div><table role=\"presentation\" width=\"100%\">${coverageBars || '<tr><td class=\"empty\">No Wazuh use-case context was identified.</td></tr>'}</table></td></tr>`;\n\nconst actionRows = rows(ai.recommendedActions || [], (action) => `<tr><td style=\"font-weight:700;color:#41516a;white-space:nowrap\">${esc(action.priority)}</td><td>${esc(action.action)}</td><td>${esc(action.rationale)}</td></tr>`, 'No recommended action was produced.', 3);\nconst actionsSection = `<tr><td class=\"section\">${sectionTitle('Priority actions')}<table class=\"data-table\"><tr><th>Priority</th><th>Action</th><th>Rationale</th></tr>${actionRows}</table></td></tr>`;\nconst limitationsSection = `<tr><td class=\"section\">${sectionTitle('Evidence limitations')}${list(ai.limitations || [], (item) => `<li>${esc(item)}</li>`)}</td></tr>`;\n\nconst executiveBody = () => `${header('Executive Security Report', 'Leadership view')}${activityHero}${severityStrip}<tr><td class=\"section\">${sectionTitle('Executive risk snapshot')}${callout(`<strong>${pct(counts.critical + counts.high)}% of observed alerts are Critical or High.</strong> ${esc(ai.executiveSummary)}`)}</td></tr><tr><td class=\"section\">${sectionTitle('Risk narrative')}${callout(esc(ai.riskNarrative), 'blue')}</td></tr>${coverageSection}<tr><td class=\"section\"><table role=\"presentation\" width=\"100%\"><tr><td width=\"50%\" valign=\"top\" style=\"padding-right:8px\"><div style=\"border:1px solid #d8e0ea;background:#f7f9fc;padding:16px;min-height:128px\"><div style=\"font-size:15px;font-weight:700;margin-bottom:10px\">Leadership focus</div>${list((ai.recommendedActions || []).slice(0, 3), (action) => `<li>${esc(action.action)}</li>`)}</div></td><td width=\"50%\" valign=\"top\" style=\"padding-left:8px\"><div style=\"border:1px solid #d8e0ea;background:#f7f9fc;padding:16px;min-height:128px\"><div style=\"font-size:15px;font-weight:700;margin-bottom:8px\">MITRE visibility</div><div style=\"font-size:28px;line-height:34px;font-weight:700;color:#6941c6\">${context.threatMitrePercentage}%</div><div class=\"muted\" style=\"font-size:11px\">${context.threatMitreCount.toLocaleString('en-US')} alerts include mapped Threat Detection context. Other Wazuh use cases remain reported separately.</div></div></td></tr></table></td></tr>${actionsSection}${limitationsSection}${close('Wazuh metrics remain authoritative. Gemini enriches narrative and recommendations only; analyst validation is required before response, suppression, or tuning.')}`;\n\nconst topRulesSection = `<tr><td class=\"section\">${sectionTitle('Top Wazuh rules across all use cases')}<table class=\"data-table\"><tr><th>Rule</th><th>Description</th><th>Level</th><th>Count</th><th>Use case(s)</th></tr>${rows(context.topRules, (rule) => `<tr><td>${esc(rule.id)}</td><td>${esc(rule.description)}</td><td>${rule.level}</td><td>${Number(rule.count || 0).toLocaleString('en-US')}</td><td>${esc(rule.useCases.join(', '))}</td></tr>`)}</table></td></tr>`;\nconst agentsSection = `<tr><td class=\"section\">${sectionTitle('Most affected assets')}<table class=\"data-table\"><tr><th>Asset / agent</th><th>Events</th></tr>${rows(context.topAgents.slice(0, 10), (agent) => `<tr><td>${esc(agent.name)}</td><td>${Number(agent.count || 0).toLocaleString('en-US')}</td></tr>`, 'No affected assets were returned.', 2)}</table></td></tr>`;\nconst mitreSection = `<tr><td class=\"section\">${sectionTitle('Threat Detection \u2014 MITRE ATT&CK context')}<p class=\"muted\" style=\"font-size:11px\">${context.threatMitreCount.toLocaleString('en-US')} alerts (${context.threatMitrePercentage}% of all alerts) contain a Wazuh MITRE ATT&amp;CK mapping. MITRE is shown only for this Threat Detection subset; other Wazuh use cases remain represented separately.</p><table role=\"presentation\" width=\"100%\"><tr><td width=\"50%\" valign=\"top\" style=\"padding-right:8px\"><table class=\"data-table\"><tr><th>Tactic</th><th>Mapped alerts</th></tr>${rows(context.tactics, (item) => `<tr><td>${esc(item.name)}</td><td>${item.count}</td></tr>`, 'No tactics mapped.', 2)}</table></td><td width=\"50%\" valign=\"top\" style=\"padding-left:8px\"><table class=\"data-table\"><tr><th>Technique</th><th>Mapped alerts</th></tr>${rows(context.techniques, (item) => `<tr><td>${esc(item.name)}</td><td>${item.count}</td></tr>`, 'No techniques mapped.', 2)}</table></td></tr></table></td></tr>`;\nconst observationsSection = `<tr><td class=\"section\">${sectionTitle('AI-assisted technical observations')}${callout(list(ai.technicalObservations || [], (item) => `<li>${esc(item)}</li>`), 'blue')}</td></tr>`;\nconst evidenceSection = `<tr><td class=\"section\">${sectionTitle('Recent evidence sample')}<table class=\"data-table\"><tr><th>Time</th><th>Agent</th><th>Rule</th><th>Level</th><th>Use case(s)</th><th>Description</th></tr>${rows(context.evidence, (event) => `<tr><td>${esc(event.timestamp)}</td><td>${esc(event.agent)}</td><td>${esc(event.ruleId)}</td><td>${event.level}</td><td>${esc(event.useCases.join(', '))}</td><td>${esc(event.description)}</td></tr>`, 'No recent evidence was returned.', 6)}</table></td></tr>`;\n\nconst technicalBody = (isAppendix = false) => `${isAppendix ? '<tr><td class=\"section page\">' + sectionTitle('Technical appendix') + '</td></tr>' : header('Technical Security Report', 'SOC and engineering view')}${isAppendix ? '' : activityHero + severityStrip}${coverageSection}${topRulesSection}${mitreSection}${observationsSection}${evidenceSection}${actionsSection}${limitationsSection}`;\nconst technical = () => `${technicalBody()}${close('This report is evidence-oriented. Validate context before changing rules, decoders, thresholds, suppressions, or response actions.')}`;\nconst combined = () => `${header('Combined Security Intelligence Report', 'Leadership summary and technical evidence')}${activityHero}${severityStrip}<tr><td class=\"section\">${sectionTitle('Executive summary')}${callout(esc(ai.executiveSummary), 'blue')}</td></tr><tr><td class=\"section\">${sectionTitle('Risk narrative')}${callout(esc(ai.riskNarrative))}</td></tr>${actionsSection}${technicalBody(true)}${close('Wazuh supplies the evidence; Gemini enriches the narrative; n8n applies explicit routing and presentation profiles.')}`;\n\nconst immediate = (event, severity, testPrefix) => {\n  const severityTheme = severity === 'critical'\n    ? { color: '#b42318', background: '#fef3f2', border: '#fecdca' }\n    : { color: '#d9480f', background: '#fff4ed', border: '#ffd6ae' };\n  const detailRow = (label, value) => value\n    ? `<tr><td width=\"30%\" style=\"color:#65758d;font-size:11px;line-height:16px;text-transform:uppercase;font-weight:700;padding:10px;border-bottom:1px solid #e4e9f0\">${esc(label)}</td><td style=\"color:#25334a;font-size:13px;line-height:19px;padding:10px;border-bottom:1px solid #e4e9f0\">${esc(value)}</td></tr>`\n    : '';\n  const eventHeader = `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">${commonStyle}</head><body><table role=\"presentation\" width=\"100%\" style=\"width:100%;background:#eef2f6\"><tr><td align=\"center\" style=\"padding:24px 10px\"><table role=\"presentation\" class=\"shell\" width=\"760\" style=\"width:760px;max-width:760px;background:#fff;border-top:7px solid ${severityTheme.color};border-radius:14px 14px 0 0\"><tr><td style=\"background:#0f1d31;padding:32px 34px 30px\"><table role=\"presentation\" width=\"100%\"><tr><td width=\"56\" valign=\"top\"><div style=\"width:46px;height:46px;line-height:46px;background:${severityTheme.color};border-radius:12px;color:#fff;text-align:center;font-size:17px;font-weight:700\">WS</div></td><td valign=\"top\"><div style=\"color:#91a2b9;font-size:11px;line-height:16px;letter-spacing:1.3px;text-transform:uppercase\">Wazuh Security Alert</div><div style=\"color:#fff;font-size:25px;line-height:32px;font-weight:700;padding-top:4px\">${esc(event.description || `Rule ${event.ruleId}`)}</div><div style=\"color:#c5cfdd;font-size:12px;line-height:18px;padding-top:8px\">${esc(config.organizationName)} &nbsp;&bull;&nbsp; Individual event notification</div></td><td width=\"120\" valign=\"top\" align=\"right\" class=\"hide-mobile\"><span style=\"display:inline-block;color:${severityTheme.color};background:${severityTheme.background};border:1px solid ${severityTheme.border};border-radius:18px;padding:7px 12px;font-size:10px;line-height:14px;font-weight:700\">${testPrefix}${severity.toUpperCase()}</span></td></tr></table></td></tr>`;\n  const details = [\n    detailRow('Event time', event.timestamp),\n    detailRow('Wazuh event ID', event.eventId),\n    detailRow('Agent / asset', event.agent),\n    detailRow('Rule', event.ruleId),\n    detailRow('Rule level', event.level),\n    detailRow('Description', event.description),\n    detailRow('Location', event.location),\n    detailRow('Decoder', event.decoder),\n    detailRow('Source IP', event.sourceIp),\n    detailRow('Destination IP', event.destinationIp),\n    detailRow('User', event.user),\n    detailRow('Wazuh use case(s)', event.useCases?.join(', ')),\n    detailRow('MITRE tactic(s)', event.tactics?.join(', ')),\n    detailRow('MITRE technique(s)', event.techniques?.join(', ')),\n  ].join('');\n  const nextSteps = severity === 'critical'\n    ? ['Validate the event and affected asset immediately.', 'Correlate the source, user, and related events in Wazuh.', 'Escalate through the incident-response process if malicious activity is confirmed.']\n    : ['Validate the event and affected asset according to the SOC triage process.', 'Review related Wazuh events for the same source, user, rule, or asset.', 'Escalate if corroborating evidence increases the assessed risk.'];\n  const eventFooter = `<tr><td class=\"footer\">This message contains one Critical or High Wazuh event. Validate the evidence before containment, suppression, tuning, or response.<br>Event timestamp: ${esc(event.timestamp || 'Unavailable')} &nbsp;&bull;&nbsp; Event ID: ${esc(event.eventId)}</td></tr></table></td></tr></table></body></html>`;\n  return `${eventHeader}<tr><td style=\"padding:26px 34px 22px\"><div style=\"background:${severityTheme.background};border-left:5px solid ${severityTheme.color};padding:16px 18px;color:#25334a;border-radius:0 10px 10px 0\"><strong>${severity.toUpperCase()} Wazuh event detected.</strong><br>This notification represents one specific alert, not an aggregate or reporting-period summary.</div></td></tr><tr><td class=\"section\">${sectionTitle('Alert evidence')}<table role=\"presentation\" width=\"100%\" style=\"border:1px solid #d8e0ea\">${details}</table></td></tr><tr><td class=\"section\">${sectionTitle('Recommended triage')}${list(nextSteps, (item) => `<li>${esc(item)}</li>`)}</td></tr>${eventFooter}`;\n};\n\nconst deliveries = [];\nif (context.reportMode) {\n  const eligibleProfiles = profiles.filter((profile) => {\n    const receivesReports = profile.receiveReports ?? profile.receivePdfReports;\n    const correctCadence = config.runMode === 'manual_report' || profile.reportCadence === config.runMode;\n    return profile.enabled && receivesReports && correctCadence;\n  });\n  for (const profile of eligibleProfiles) {\n    const audience = String(profile.audienceProfile || 'combined').toLowerCase();\n    const html = audience === 'executive' ? executiveBody() : audience === 'technical' ? technical() : combined();\n    deliveries.push({ json: {\n      deliveryKey: `${config.organizationKey}|${profile.profileKey}|report|${context.periodEnd}`,\n      runId: $execution.id || 'manual',\n      organizationKey: config.organizationKey,\n      recipientEmail: profile.recipientEmail,\n      recipientName: profile.recipientName || '',\n      deliveryType: 'report',\n      audienceProfile: audience,\n      severity: '',\n      periodStart: context.periodStart,\n      periodEnd: context.periodEnd,\n      subject: `[${config.organizationName}] ${audience.charAt(0).toUpperCase() + audience.slice(1)} Wazuh Security Report`,\n      emailHtml: html,\n      reportHtml: html,\n      fileName: `wazuh-${audience}-security-report.pdf`,\n    } });\n  }\n}\n\nif (context.immediateMode && context.urgentEvents?.length > 0) {\n  const urgentEvents = config.runMode === 'immediate_test'\n    ? context.urgentEvents.slice(0, 1)\n    : context.urgentEvents;\n  for (const profile of profiles.filter((item) => item.enabled && item.receiveImmediateAlerts)) {\n    let allowed = [];\n    try { allowed = JSON.parse(profile.immediateSeverities_object || '[]'); } catch {}\n    for (const event of urgentEvents) {\n      const severity = event.level >= config.criticalLevelMin ? 'critical' : 'high';\n      if (!allowed.includes(severity)) continue;\n      const testPrefix = config.runMode === 'immediate_test' ? '[TEST] ' : '';\n      deliveries.push({ json: {\n        deliveryKey: `${config.organizationKey}|${profile.profileKey}|alert|${event.eventId}`,\n        runId: $execution.id || 'manual',\n        organizationKey: config.organizationKey,\n        recipientEmail: profile.recipientEmail,\n        recipientName: profile.recipientName || '',\n        deliveryType: 'immediate_alert',\n        audienceProfile: profile.audienceProfile || 'technical',\n        severity,\n        eventId: event.eventId,\n        ruleId: event.ruleId,\n        agent: event.agent,\n        periodStart: event.timestamp || context.periodStart,\n        periodEnd: event.timestamp || context.periodEnd,\n        subject: `${testPrefix}[${severity.toUpperCase()}] Wazuh rule ${event.ruleId || 'unknown'} on ${event.agent || 'unassigned'}`,\n        emailHtml: immediate(event, severity, testPrefix),\n        reportHtml: '',\n        fileName: '',\n      } });\n    }\n  }\n}\n\nreturn deliveries;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f0643d91-a831-454f-9825-204ae990852e",
      "name": "Attach optional PDF?",
      "type": "n8n-nodes-base.if",
      "position": [
        2160,
        144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "attach-pdf",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ String($(\"Configure alert intelligence workflow\").first().json.generatePdf) }}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "50426f88-9c10-4ef4-9a92-8cd8bf884503",
      "name": "Send role-based HTML report",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        2560,
        160
      ],
      "parameters": {
        "html": "={{ $(\"Build role-based deliveries\").item.json.emailHtml }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $(\"Build role-based deliveries\").item.json.subject }}",
        "toEmail": "={{ $(\"Build role-based deliveries\").item.json.recipientEmail }}",
        "fromEmail": "={{ $(\"Configure alert intelligence workflow\").first().json.senderName + \" <\" + $(\"Configure alert intelligence workflow\").first().json.senderEmail + \">\" }}"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "8fa80c5b-87b2-4a69-b2a1-232f81b62566",
      "name": "Send only previously unseen alerts",
      "type": "n8n-nodes-base.dataTable",
      "notes": "Suppresses a recipient-specific alert when its stable Wazuh event delivery key already exists in the audit log.",
      "position": [
        2352,
        880
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "deliveryKey",
              "keyValue": "={{ $json.deliveryKey }}"
            }
          ]
        },
        "matchType": "allConditions",
        "operation": "rowNotExists",
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "eFRljEojwfT812EE",
          "cachedResultName": "wazuh_alert_delivery_log"
        }
      },
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "a9ca8a3e-a526-4705-a33c-d2f360b19bf5",
  "nodeGroups": [],
  "connections": {
    "Attach optional PDF?": {
      "main": [
        [
          {
            "node": "Convert report HTML to index file",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send role-based HTML report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build weekly reports": {
      "main": [
        [
          {
            "node": "Use weekly reporting mode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build monthly reports": {
      "main": [
        [
          {
            "node": "Use monthly reporting mode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run workflow manually": {
      "main": [
        [
          {
            "node": "Use manual reporting mode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use manual reporting mode": {
      "main": [
        [
          {
            "node": "Configure alert intelligence workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use weekly reporting mode": {
      "main": [
        [
          {
            "node": "Configure alert intelligence workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Wazuh alert evidence": {
      "main": [
        [
          {
            "node": "Build deterministic security context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use monthly reporting mode": {
      "main": [
        [
          {
            "node": "Configure alert intelligence workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build role-based deliveries": {
      "main": [
        [
          {
            "node": "Route reports and immediate alerts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send role-based HTML report": {
      "main": [
        [
          {
            "node": "Log report delivery",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Repair invalid Gemini output": {
      "ai_languageModel": [
        [
          {
            "node": "Validate Gemini security insight",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Apply meaningful PDF filenames": {
      "main": [
        [
          {
            "node": "Send HTML report with PDF attachment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini security analysis model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate structured security insight with Gemini",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Load enabled delivery profiles": {
      "main": [
        [
          {
            "node": "Prepare bounded Wazuh alert query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use immediate notification mode": {
      "main": [
        [
          {
            "node": "Configure alert intelligence workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Render report PDF with Gotenberg": {
      "main": [
        [
          {
            "node": "Apply meaningful PDF filenames",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Gemini security insight": {
      "ai_outputParser": [
        [
          {
            "node": "Generate structured security insight with Gemini",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Convert report HTML to index file": {
      "main": [
        [
          {
            "node": "Render report PDF with Gotenberg",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare bounded Wazuh alert query": {
      "main": [
        [
          {
            "node": "Query Wazuh alert evidence",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route reports and immediate alerts": {
      "main": [
        [
          {
            "node": "Attach optional PDF?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send only previously unseen alerts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send only previously unseen alerts": {
      "main": [
        [
          {
            "node": "Send immediate operational notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build deterministic security context": {
      "main": [
        [
          {
            "node": "Continue only when delivery is needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send HTML report with PDF attachment": {
      "main": [
        [
          {
            "node": "Log report delivery",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure alert intelligence workflow": {
      "main": [
        [
          {
            "node": "Load enabled delivery profiles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Continue only when delivery is needed": {
      "main": [
        [
          {
            "node": "Generate structured security insight with Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for urgent alerts every 5 minutes": {
      "main": [
        [
          {
            "node": "Use immediate notification mode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send immediate operational notification": {
      "main": [
        [
          {
            "node": "Log immediate notification delivery",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate structured security insight with Gemini": {
      "main": [
        [
          {
            "node": "Build role-based deliveries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}