Skip to content
heimnull.comInstall

Reference

Notifications

Notification channels (email, Slack, Teams, Telegram, ntfy, webhooks), routing events to them, digests, rate limits and the webhook format.

Configure → Notifications has the channels, the routing of events to them, and recent messages. Admins change it.

Type Settings
Email SMTP host, port, encryption (none, STARTTLS, or implicit TLS), username and password (empty for none), from address, recipients (comma-separated). Messages are HTML with a plain-text part; attack alerts include a chart of the target’s traffic
Slack An incoming webhook URL. Mattermost and other Slack-compatible webhooks work too
Microsoft Teams A Workflows webhook URL. Messages are Adaptive Cards with an “Open in Heimnull” button
Telegram A bot token, and chat IDs (comma-separated, for example -1001234567890 or @channelname)
ntfy Server (empty for https://ntfy.sh), topic, and an optional access token. On ntfy.sh anyone who knows a topic can subscribe: use a hard-to-guess name, or a token
Webhook A URL that receives a JSON POST per message, and an optional signing key

Every channel has Messages per minute (default 20) and Enabled. Webhook URLs, tokens and passwords are stored encrypted, and errors never show a URL, since for Slack and Teams the URL is the secret.

Send test sends a sample attack alert through the channel, so you see the real layout. Preview emails opens every email template with a made-up event.

Event When
attack_detected An attack started (or an observed anomaly became an attack)
attack_ended An attack ended
attack_observed A baseline anomaly in observe mode. Not routed anywhere until you route it
mitigation_applied A blackhole or FlowSpec rule was announced, with what each router received
mitigation_extended A mitigation was extended
mitigation_withdrawn A mitigation was withdrawn or expired
mitigation_refused A mitigation was refused by the safety checks, the cap on active mitigations or the emergency withdraw-all
bgp_session A BGP session went up or down
exporter_silent An exporter stopped sending (after its “silent after” time), and when it recovers
ingest_problem Drops or ClickHouse insert failures, at most every 10 minutes while they last, and a recovery
dataset_failed A dataset couldn’t be refreshed
system_health Heimnull’s own health: disk, ClickHouse memory and merges, an outdated compose file or ClickHouse configuration, SNMP failing. See System

Routing maps events to channels, globally or per hostgroup. For an event in a hostgroup that has its own routes, those replace the global ones; events with no hostgroup (BGP, exporters, ingest, datasets, health) use the global routes.

  • Coalescing. The first event of a kind goes out at once. Further events with the same key within the coalescing window (Settings → Notifications, default 30 seconds) wait and go as one digest. Attacks are grouped per hostgroup and direction, mitigations per hostgroup, and BGP, exporter, ingest and dataset events each by kind.
  • Rate limit. Each channel sends at most its messages per minute; when more are waiting, the last message carries the rest as one digest.
  • Retries. A failed send is retried with growing delays (10 seconds, doubling, up to 10 minutes between tries) and given up after 8 attempts. Delivery is at least once: a restart at the wrong moment can repeat a message.

Under Settings → Notifications:

  • Base URL: where the links in messages point, for example https://heimnull.example.net. Each message links to its attack, mitigation or host page.
  • Server name: put into every subject, as in Heimnull (Dallas): …. Empty uses the base URL’s host. It tells several Heimnull servers apart in one chat.

Each message is a POST with a JSON body:

{
"source": "heimnull",
"subject": "Heimnull (Dallas): Attack detected: 203.0.113.10 (DNS amplification, 4.2 Gbps)",
"text": "…the plain-text message…",
"link": "https://heimnull.example.net/attacks/1234",
"events": [
{ "type": "attack_detected", "at": "2026-10-19T14:02:11Z", "data": { "…": "the event's details" } }
]
}

events holds one entry per event in the message (several in a digest), each with its type, time and details.

With a signing key, each request carries X-Heimnull-Timestamp and X-Heimnull-Signature: sha256=<hex>, the HMAC-SHA256 with your key of the timestamp, a ., and the raw body. To verify, compute the same over what you received, compare in constant time, and reject old timestamps.