Alerting
Seerflow ships with a complete notification router. Every alert is matched against a list of routing rules (first match wins) and dispatched to one or more delivery targets. Each target has its own rate limit, deduplication semantics, and optional quiet hours.
Alert │ ▼Dedup window (default 15 min, per alert_type override available) │ ▼Routing rule match (alert_type · rule_name · entity_type · severity range) │ ▼Per-channel dispatch (immediate or digest, with quiet-hours suppression) │ ▼DeliveryTarget ── webhook · email · sms · telegram · whatsapp · pagerduty · otlpAll channels are configured under alerting: in seerflow.yaml. Channels not listed are inert — there is no implicit “send everywhere” behaviour.
Webhooks (Slack / Teams / generic)
Section titled “Webhooks (Slack / Teams / generic)”alerting: webhooks: - url: ${SLACK_WEBHOOK_URL} format: slack # slack | teams | json min_severity: 4 # only fire for severity >= 4 (high+) - url: ${TEAMS_WEBHOOK_URL} format: teams - url: https://example.com/seerflow format: jsonThe json format posts the raw alert payload. slack and teams render channel-native cards including the alert title, severity, rule name, top entities, and a link to the dashboard alert detail (alerting.dashboard_url) when configured.
PagerDuty
Section titled “PagerDuty”alerting: pagerduty_routing_key: ${SEERFLOW_PD_KEY}Triggers Events API v2 with severity mapped from the OCSF severity ID. The dedup_key is reused as the PagerDuty incident key, so deduplication is consistent across both sides.
Email (SMTP)
Section titled “Email (SMTP)”alerting: email_targets: - name: oncall-mail smtp_host: smtp.example.com smtp_port: 587 use_starttls: true smtp_user: ${SMTP_USER} smtp_password: ${SMTP_PASSWORD} from_address: seerflow@example.com to_addresses: - oncall@example.com - secops@example.com min_severity: 4 max_per_minute: 30 # token-bucket cap; omit for unlimitedsmtp_user and smtp_password are read at startup; rotate by restarting the process.
SMS (Twilio)
Section titled “SMS (Twilio)”alerting: sms_targets: - name: oncall-sms account_sid: ${TWILIO_SID} auth_token: ${TWILIO_TOKEN} from_number: "+15551234567" to_numbers: - "+15557654321" min_severity: 5 rate_per_second: 1.0 burst: 3Use min_severity: 5 (critical) to keep SMS reserved for the loudest alerts.
Telegram
Section titled “Telegram”alerting: telegram_targets: - name: oncall-telegram bot_token: ${TELEGRAM_BOT_TOKEN} chat_id: "-1001234567890" # group id (negative) or user id min_severity: 4 rate_per_second: 30.0 burst: 30Messages are rendered as MarkdownV2 and respect Telegram’s 30-msg/s burst limit by default.
WhatsApp (Cloud API)
Section titled “WhatsApp (Cloud API)”alerting: whatsapp_targets: - name: oncall-whatsapp phone_number_id: "1234567890" template_name: seerflow_alert language_code: en access_token: ${WA_ACCESS_TOKEN} to_numbers: - "+15551234567" min_severity: 5 rate_per_second: 10.0 burst: 20The template must be pre-approved in WhatsApp Business Manager. The target carries an internal circuit breaker that opens after sustained 5xx errors and resumes once the API recovers.
OTLP alert export
Section titled “OTLP alert export”Forward every alert (post-dedup) to an OpenTelemetry collector as a span. Useful for fan-out into Tempo / Jaeger / OTel-aware SIEMs.
alerting: otlp_endpoint: otel-collector.internal:4317 otlp_protocol: grpc # grpc | http otlp_export_interval_seconds: 5 # otlp_tls: true # default: derived from URL scheme # otlp_tls_ca_file: /etc/ssl/private-ca.pem # otlp_mtls_cert_file: /etc/ssl/seerflow.crt # otlp_mtls_key_file: /etc/ssl/seerflow.keyCustom CA / mTLS files are PEM paths read once at startup. Rotation requires a restart (same semantics as Go tls.Config / Java KeyStore).
Deduplication
Section titled “Deduplication”Seerflow keeps a dedup_key → last_fire_ts map per alert. Alerts with the same key inside the window are suppressed and bump a dedup_count.
alerting: dedup_window_seconds: 900 # default 15 min dedup_window_overrides: - ["correlation", 300] # kill-chain alerts fire faster - ["ueba", 1800] # UEBA: at most twice per hourRouting rules
Section titled “Routing rules”First-match-wins evaluation. Each rule has a match block (all predicates AND together) and a notify list dispatched in parallel.
alerting: routing_rules: - match: alert_type: ["sigma", "correlation"] min_severity: 5 notify: - channel: oncall-sms - channel: oncall-telegram - channel: oncall-mail mode: digest # batch into one mail digest_window_minutes: 15
- match: rule_name: "ssh_brute_force*" # fnmatch glob entity_type: ipv4 notify: - channel: oncall-telegram
- match: alert_type: ueba notify: - channel: oncall-mail mode: digest digest_window_minutes: 60
default_routing: action: notify # notify | drop notify: - channel: oncall-mail mode: digest digest_window_minutes: 30Match predicates
Section titled “Match predicates”| Predicate | Type | Notes |
|---|---|---|
alert_type | string or list | sigma, ml, correlation, ueba, ioc |
rule_name | string (glob) | fnmatch.fnmatchcase — e.g. "ssh_*" |
entity_type | string or list | ipv4, ipv6, user, host, process, file, domain, url |
min_severity / max_severity | int | OCSF severity ID (0–6) |
Notify modes
Section titled “Notify modes”| Mode | When to use |
|---|---|
immediate (default) | High-signal alerts you want pushed as they fire |
digest | Lower-signal alerts coalesced into a single message every digest_window_minutes |
Quiet hours
Section titled “Quiet hours”Per-channel suppression windows. Severity-aware: alerts at or above min_severity still break through.
alerting: quiet_hours_by_channel: - ["oncall-sms", { start: "22:00", end: "07:00", min_severity: 6 }] - ["oncall-telegram", { start: "00:00", end: "06:00", min_severity: 5 }]Times are UTC. Windows that cross midnight (e.g. 22:00 → 07:00) are handled correctly.
Dashboard URL
Section titled “Dashboard URL”Add a dashboard_url so message renderers can deep-link back to the alert detail page.
alerting: dashboard_url: https://seerflow.example.comSlack, Teams, email, and Telegram bodies will then include a “View in Seerflow” link to {dashboard_url}/#alert=<id>.
Per-channel rate limits
Section titled “Per-channel rate limits”Every channel uses a token-bucket rate limiter. The defaults are conservative; tune rate_per_second and burst if you hit the bucket and start seeing send delays in the logs.
| Channel | Default rate_per_second | Default burst |
|---|---|---|
n/a (use max_per_minute) | n/a | |
| SMS | 1.0 | 3 |
| Telegram | 30.0 | 30 |
| 10.0 | 20 |
WhatsApp additionally maintains an internal circuit breaker that opens after sustained upstream failure.