Notifications
One sentence first, because everything else on this page exists to make it true: when Vigil decides to alert you, that decision is durable, and the message is delivered at least once or recorded as failed where you can see it.
Ten channel providers, one pipeline
A channel is a provider, its settings, its encrypted credentials and the event classes it subscribes to. Every provider rides the same transactional outbox: durable enqueue in the transaction that decided to alert, leases, retries with jittered backoff, idempotency keys derived from the cause, and a per-channel rate limit. No provider has a queue of its own.
| Provider | Transport | Credential you supply |
|---|---|---|
| Slack | Incoming webhook | the webhook URL |
| Discord | Channel webhook | the webhook URL |
| Microsoft Teams | Workflows webhook, Adaptive Card payload | the workflow URL |
| Telegram | Bot API sendMessage | bot token and chat id |
| Google Chat | Space incoming webhook | the webhook URL |
| Gotify | POST /message with X-Gotify-Key | application token |
| ntfy | JSON publish, ntfy.sh or your server | token or user + password, optional |
| Webhook | Signed JSON POST, X-Vigil-Signature | HMAC signing secret |
| SMTP | Own client: STARTTLS/TLS, AUTH PLAIN | your mail server, password optional |
| Resend | POST /emails with Idempotency-Key | API key |
Three of those rows are contracts worth reading twice. Teams is the Power Automate Workflows webhook: Microsoft disabled the old Office 365 connector URLs in May 2026, and Vigil never speaks the retired MessageCard format. Every credential is yours: there is no Vigil-funded relay, account or hosted service behind any provider. And Gotify, ntfy and the signed webhook may point at servers on your own network; cloud metadata and link-local space are refused wherever a URL points or resolves, and redirects are never followed.
Event classes
A channel subscribes to classes, and every event belongs to exactly one, so one logical event cannot reach the same channel twice: monitor down/up, incident lifecycle, certificate and domain expiry (a carve-out of down/up by monitor kind - the certificate channel hears about certificates, the 3am pager does not), and in the commercial edition recovery results and remote-probe quorum. Recovery notifies outcomes only: success once verified, failure once the chain is exhausted. Quorum events fire on the transition, not once per round.
Credentials and errors
Channel secrets are stored encrypted (AES-256-GCM) and are never sent back to a browser; the editor only shows which secret fields are set. Delivery errors are scrubbed of secret values and URL query strings before they are stored, and the delivery history shows provider, event, destination, attempts, final status and that redacted error. SMTP refuses to authenticate without TLS and certificate verification has no off switch.
Retries, honestly
Delivery is at-least-once: six attempts, exponential jittered backoff, a provider's Retry-After honored, permanent rejections (a 404, a revoked credential) marked failed immediately instead of hiding behind a queue. The unavoidable window is a crash after a provider accepted a message but before the row records it; where the provider offers idempotency (Resend), the retry collapses on their side. The wording the product uses everywhere: at most one message per logical notification where the provider cooperates, at least one everywhere.
The full engineering document, including the outbox schema and the
exact guarantee wording, ships in the repository as
docs/NOTIFICATIONS.md.