# Notification Flow ## Overview The Notification Flow describes how StellaOps delivers alerts and notifications to users through multiple channels including email, Slack, Microsoft Teams, and webhooks. Notifications are triggered by system events such as scan completions, policy violations, new advisories, and scheduled reports. **Business Value**: Timely notifications ensure security teams are immediately aware of critical issues without constantly monitoring dashboards. ## Actors | Actor | Type | Role | |-------|------|------| | Event Source | Service | Emits triggering events | | Scheduler | Service | Manages scheduled notifications | | Notify | Service | Routes and delivers notifications | | Channel Adapters | Components | Email, Slack, Teams, Webhook | | User | Human | Receives notifications | ## Prerequisites - Notification channels configured in tenant settings - Channel credentials (SMTP, Slack webhook, Teams connector) - User notification preferences set ## Supported Channels | Channel | Protocol | Configuration | |---------|----------|---------------| | Email | SMTP/SMTPS | `smtp.host`, `smtp.port`, `smtp.user`, `smtp.password` | | Slack | Webhook | `slack.webhook_url` | | Microsoft Teams | Connector | `teams.webhook_url` | | Webhook | HTTP POST | `webhook.url`, `webhook.headers` | | PagerDuty | API | `pagerduty.routing_key` | ## Flow Diagram ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Notification Flow │ └─────────────────────────────────────────────────────────────────────────────────┘ ┌──────────┐ ┌───────────┐ ┌────────┐ ┌─────────────────────────────────────────┐ │ Event │ │ Scheduler │ │ Notify │ │ Channel Adapters │ │ Source │ │ │ │ │ │ Email │ Slack │ Teams │ Webhook │ PD │ └────┬─────┘ └─────┬─────┘ └───┬────┘ └────┬───┴───┬───┴───┬───┴────┬────┴──┬──┘ │ │ │ │ │ │ │ │ │ Emit event │ │ │ │ │ │ │ │─────────────────────────>│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Match │ │ │ │ │ │ │ │ rules │ │ │ │ │ │ │ │───┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │<──┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Route to │ │ │ │ │ │ │ │ channels │ │ │ │ │ │ │ │───────────>│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Send │ │ │ │ │ │ │ │ email │ │ │ │ │ │ │ │──┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │<─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │───────────────────>│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Post │ │ │ │ │ │ │ │ msg │ │ │ │ │ │ │ │──┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │<─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │───────────────────────────>│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Post │ │ │ │ │ │ │ │ card │ │ │ │ │ │ │ │──┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │<─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ Log │ │ │ │ │ │ │ │ delivery │ │ │ │ │ │ │ │───┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │<──┘ │ │ │ │ │ │ │ │ │ │ │ │ │ ``` ## Step-by-Step ### 1. Event Emission Various services emit notification-triggering events: ```json { "event_type": "scan.complete", "event_id": "evt-123456", "timestamp": "2024-12-29T10:30:00Z", "tenant_id": "acme-corp", "payload": { "scan_id": "scan-7f3a9b2c-...", "image": "docker.io/library/nginx:1.25", "verdict": "FAIL", "violations": 3, "severity_breakdown": { "critical": 1, "high": 2 } } } ``` ### 2. Notification Rule Matching Notify service matches event against configured rules: ```yaml # Notification Rules rules: - name: critical-scan-failure description: Alert on critical vulnerabilities event_type: scan.complete conditions: - field: payload.verdict operator: eq value: FAIL - field: payload.severity_breakdown.critical operator: gt value: 0 channels: - type: slack config_ref: security-team-slack urgency: high - type: email config_ref: security-leads - type: pagerduty config_ref: on-call-rotation - name: daily-scan-summary description: Daily summary of all scans event_type: scheduled.daily_summary channels: - type: email config_ref: security-distribution ``` ### 3. Channel Routing Notify determines which channels to activate: | Rule Match | Channel | Priority | |------------|---------|----------| | critical-scan-failure | slack | HIGH | | critical-scan-failure | email | HIGH | | critical-scan-failure | pagerduty | HIGH | ### 4. Message Rendering Each channel adapter renders the message in appropriate format: #### Email Template ```html

🚨 Critical Vulnerability Detected

A scan has failed with critical vulnerabilities.

Imagedocker.io/library/nginx:1.25
VerdictFAIL
Critical1
High2

View Details

``` #### Slack Block Kit ```json { "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "🚨 Critical Vulnerability Detected" } }, { "type": "section", "fields": [ {"type": "mrkdwn", "text": "*Image:*\n`nginx:1.25`"}, {"type": "mrkdwn", "text": "*Verdict:*\n:x: FAIL"}, {"type": "mrkdwn", "text": "*Critical:*\n1"}, {"type": "mrkdwn", "text": "*High:*\n2"} ] }, { "type": "actions", "elements": [ { "type": "button", "text": {"type": "plain_text", "text": "View Details"}, "url": "https://console.stellaops.local/scans/scan-7f3a9b2c-..." } ] } ] } ``` #### Teams Adaptive Card ```json { "type": "AdaptiveCard", "version": "1.4", "body": [ { "type": "TextBlock", "text": "🚨 Critical Vulnerability Detected", "weight": "bolder", "size": "large" }, { "type": "FactSet", "facts": [ {"title": "Image", "value": "nginx:1.25"}, {"title": "Verdict", "value": "FAIL"}, {"title": "Critical", "value": "1"}, {"title": "High", "value": "2"} ] } ], "actions": [ { "type": "Action.OpenUrl", "title": "View Details", "url": "https://console.stellaops.local/scans/scan-7f3a9b2c-..." } ] } ``` ### 5. Delivery Each adapter delivers to its channel: | Channel | Protocol | Retry Policy | |---------|----------|--------------| | Email | SMTP | 3 retries, exponential backoff | | Slack | HTTPS POST | 3 retries, 1s/2s/4s | | Teams | HTTPS POST | 3 retries, 1s/2s/4s | | Webhook | HTTPS POST | 5 retries, configurable | | PagerDuty | HTTPS POST | 5 retries, 2s/4s/8s/16s/32s | ### 6. Delivery Logging Notify logs delivery status to `notify.delivery_log`: ```json { "delivery_id": "dlv-789abc", "event_id": "evt-123456", "channel": "slack", "status": "delivered", "attempts": 1, "delivered_at": "2024-12-29T10:30:02Z", "response": {"ok": true} } ``` ## Data Contracts ### Notification Event Schema ```typescript interface NotificationEvent { event_type: string; event_id: string; timestamp: string; tenant_id: string; payload: Record; metadata?: { source_service: string; correlation_id?: string; }; } ``` ### Channel Configuration Schema ```typescript interface ChannelConfig { type: 'email' | 'slack' | 'teams' | 'webhook' | 'pagerduty'; name: string; enabled: boolean; config: EmailConfig | SlackConfig | TeamsConfig | WebhookConfig | PagerDutyConfig; } interface EmailConfig { smtp_host: string; smtp_port: number; smtp_user?: string; smtp_password?: string; smtp_tls: boolean; from_address: string; to_addresses: string[]; } interface SlackConfig { webhook_url: string; channel?: string; username?: string; icon_emoji?: string; } interface WebhookConfig { url: string; method: 'POST' | 'PUT'; headers?: Record; auth?: { type: 'basic' | 'bearer' | 'api_key'; credentials: string; }; } ``` ## Event Types | Event Type | Source | Description | |------------|--------|-------------| | `scan.complete` | Scanner | Scan finished with results | | `scan.failed` | Scanner | Scan execution failed | | `policy.violation` | Policy | Policy rule triggered | | `advisory.new` | Concelier | New advisory ingested | | `advisory.update` | Concelier | Advisory modified | | `vex.issued` | VexLens | New VEX statement | | `exception.expiring` | Policy | Exception about to expire | | `scheduled.daily_summary` | Scheduler | Daily digest | | `scheduled.weekly_report` | Scheduler | Weekly report | ## Error Handling | Error | Recovery | |-------|----------| | SMTP connection failed | Retry with backoff, queue for later | | Slack webhook 429 | Respect Retry-After header | | Teams connector 502 | Retry up to 3 times | | Webhook timeout | Retry with increased timeout | | Invalid recipient | Skip recipient, log error | ## Observability ### Metrics | Metric | Type | Labels | |--------|------|--------| | `notify_events_received_total` | Counter | `event_type` | | `notify_deliveries_total` | Counter | `channel`, `status` | | `notify_delivery_latency_ms` | Histogram | `channel` | | `notify_retries_total` | Counter | `channel`, `reason` | ### Key Log Events | Event | Level | Fields | |-------|-------|--------| | `notify.event.received` | INFO | `event_type`, `event_id` | | `notify.rule.matched` | DEBUG | `rule_name`, `channels` | | `notify.delivery.attempt` | DEBUG | `channel`, `attempt` | | `notify.delivery.success` | INFO | `channel`, `delivery_id` | | `notify.delivery.failed` | WARN | `channel`, `error` | ## Related Flows - [Scan Submission Flow](02-scan-submission-flow.md) - Triggers scan notifications - [Advisory Drift Re-scan Flow](11-advisory-drift-rescan-flow.md) - Advisory notifications - [Exception Approval Workflow](17-exception-approval-workflow.md) - Exception notifications