AlertLoop
A self-hosted event and notification center for software teams.
Collect important application events, route notifications and keep delivery failures visible and recoverable, on your own infrastructure, with no third-party service in the path.
# Open an incident on a self-hosted AlertLoop
curl -X POST http://localhost:8080/v1/events \
-H "X-API-Key: $ALERTLOOP_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "incident",
"status": "firing",
"severity": "critical",
"source": "feeds_worker",
"message": "Feed processing failed",
"dedupe_key": "feeds:developer:15:flats"
}'
# Sending it again updates the open incident
# instead of notifying twice. Send
# "status": "resolved" and the same channels
# hear that it is over, and how long it lasted.
The problem
Important events should not disappear in logs
Applications generate incidents, business events and operational warnings. Without a shared delivery pipeline, these events remain scattered across logs, emails and one-off integrations.
01
Nobody sees it
A failed nightly job writes one line to a log file. The line is correct, structured and read by no one until a customer complains.
02
Every service invents delivery
Each application grows its own SMTP block, its own webhook sender and its own idea of what a retry means. None of them agree.
03
Failures fail silently
A notification that never arrived leaves no trace. There is no attempt history, no dead letter, and nothing to replay once the channel is back.
Product overview
One place events arrive, and one pipeline that delivers them
Applications POST events to a single API. AlertLoop stores them, deduplicates them, decides by rule which channels should hear about each one, retries what fails and keeps the failures where you can find and replay them. It ships as one CGO-free Go binary or a signed container image, with an embedded React admin console and Swagger UI, backed by SQLite for a demo or PostgreSQL in production.
Architecture
The delivery pipeline
The happy path is deliberately short. Everything interesting happens in the failure path underneath it.
Source
ApplicationIngest
AlertLoop APIPersist
Event storageRoute
Routing rulesQueue
Delivery queueSend
WorkersChannels
Email / Telegram / Chat / Push / WebhookWhen a delivery fails
Event state (new, acknowledged, resolved, muted, escalated) and delivery state are kept apart on purpose. An incident can be resolved while its email notification is still failing. Those are two different facts, and collapsing them into one status field is how delivery problems become invisible.
Incident lifecycle
A problem that starts, lasts, and ends
A check that fails every minute should not notify every minute, and a problem that is over should say so. The reporting source sends a status and AlertLoop keeps the incident.
Resolving is not re-routed: routing ran once at ingestion, and the audience for “it is fixed” is the audience of “it is broken”. A recovery never arrives before its alert: it waits while the alert is retried, and on a channel where the alert dead-lettered, it waits until the alert is replayed and sent. Repeating a resolve does not notify twice, and notify_on_resolve: false turns recovery notices off entirely. A request with no status behaves as it always did, with dedupe_key as a plain idempotency key.
Core capabilities
What the Community edition does
Everything listed here is in the released v0.8.0 binary. Nothing on this list is planned, partial or aspirational, and nothing on it is held back for a paid edition.
Event ingestion
An Events API with API-key auth and scopes (ingest, read, full), an ingest key optionally limited to its own sources, documented by a live OpenAPI contract and Swagger UI.
Three event families
incident, business_event and audit: an operational failure and a signed contract are not the same thing and should not share a shape.
Firing and resolved
status: firing updates the open incident carrying the same dedupe_key instead of storing a duplicate, and queues nothing new unless a raised severity routes it to a channel that has not had the alert. status: resolved closes it and frees the key, so the same failure recurring opens a new incident.
Recovery notifications
Closing an incident tells the channels that received the alert how long the outage lasted, and tells nobody else. A recovery goes out only after its alert has been delivered. On by default; notify_on_resolve turns it off.
Event lifecycle
Incidents move through new, acknowledged, resolved, muted and escalated, with a manual escalate action. Business and audit events are records and keep the state they arrived in.
Idempotent ingestion
A dedupe_key makes repeated sends safe. A worker that retries its own failure does not produce five identical incidents.
Routing rules
Per-event channel selection by type, severity, min_severity, source and category. First match wins, an empty channel list stores an event without delivering it, and default catches the rest. POST /v1/routing/preview dry-runs the table without creating anything.
Email delivery
SMTP with STARTTLS or implicit TLS. AlertLoop refuses to send in plaintext when TLS was requested and is unavailable.
Webhook delivery
Outbound webhooks are HMAC-signed, so the receiving system can verify that the payload came from your AlertLoop, and carry kind at the top level so a receiver can close its own ticket instead of opening a second one.
Telegram delivery
The same retry and dead-letter path as email and webhooks, plus a per-channel proxy (http, https, socks5, socks5h) for hosts that cannot reach api.telegram.org directly.
Chat and push delivery
Slack (the same type serves Mattermost and Rocket.Chat), Microsoft Teams, Discord, ntfy and Pushover, several of each, with routing and recovery notices like the others. With public_url set, every notification links to its event in the admin console.
Retries, dead letter, replay
A delivery worker with exponential backoff, a dead-letter queue for exhausted jobs and a replay endpoint to send them again.
Fallback channel
A channel can name a fallback. An alert that exhausts its retries there is sent once to the fallback, opening with the channel that failed and its error, and gets its own recovery notice. One hop, no chains.
Delivery visibility
Full delivery-attempt history, stored separately from event state, each attempt recording whether it announced an alert or a recovery, with cursor-paginated list endpoints and a ?kind= filter. GET /v1/stats reports open incidents, the oldest delivery due and recent dead letters, so AlertLoop itself can be monitored.
Admin console
A React console (Overview, Events with message search, Event detail, Deliveries with dead-letter replay, Users) embedded in the binary and served at /admin. People sign in with a login and password; Community accounts have one role, admin.
One configuration file
A single YAML file is the only place AlertLoop is configured. Secrets stay out of it through ${VAR} references filled from the environment at startup; a missing variable with no default stops the process instead of leaving the setting empty. A key AlertLoop does not read stops the process too, and the error names that key and its line, so a typo in a key cannot leave the setting on its default unnoticed. Without an admin token or API keys the server does not start.
Monit integration
A shell adapter that turns Monit checks into AlertLoop events: bounded retries, a local spool that sends an event later when AlertLoop cannot be reached, a documented exit-code contract, no secret in process arguments or logs, fifteen example rules validated in CI, and installers that never overwrite an existing configuration. AlertLoop does not monitor servers itself; Monit does that and reports here.
Self-hosted deployment
One CGO-free binary (server, worker or all mode) or a multi-arch image at ghcr.io/golovanov-dev/alertloop, signed with cosign and carrying an SBOM, with Docker Compose profiles and a systemd unit. Structured logs, built-in rate limiting, a backup script with a systemd timer, and configurable retention, 30 days by default.
Editions
One edition exists. Two are planned.
AlertLoop starts self-hosted. Every edition on this page, released or planned, runs on your own infrastructure.
Community
The self-hosted core product for developers and small teams: Events API and Swagger/OpenAPI, incident lifecycle and recovery notices, routing rules, admin console with user accounts, Email / Telegram / Slack / Teams / Discord / ntfy / Pushover / Webhook delivery with a Telegram proxy, retries, dead letter, replay and a fallback channel, Monit integration, SQLite and PostgreSQL.
Free · Self-hosted · AGPL-3.0-only
Pro Self-hosted
Planned. Not built. May include multiple projects and workspaces, RBAC, escalation policies and on-call schedules, a WhatsApp channel, and per-project and per-event-type retention policies managed from the console.
Commercial licence · pricing not set
Enterprise
Planned. Not built. May include an on-prem commercial licence, SSO, audit log search, export and compliance features, HA deployment, a Helm chart, custom adapters and support.
Commercial licence · pricing not set
My role
What I own on this project
Product definition, architecture, implementation direction, technical review and release management.
What a correct failure path looks like and what is allowed into a release are my calls. AI agents write a large share of the code inside those decisions.
Current status
Current state of the product
Community edition v0.8.0 is released, still an alpha: prebuilt binaries, a signed container image, Docker Compose and systemd, SQLite and PostgreSQL. Documentation is the repository README plus the Swagger UI a running instance serves. The commercial Pro and Enterprise editions are planned, not built.
Roadmap
What shipped, what I'm doing now, what comes next
Phases marked as shipped are in the released binary. The rest are intentions.
-
Product definition
Product brief, business model, licensing model, technical architecture, API draft and deployment model, all decided before the first line of Go.
-
Community backend MVP
Events API with API-key auth, SQLite and PostgreSQL, dedupe-key idempotency, event lifecycle actions, Email, HMAC-signed Webhook and Telegram channels, retry worker, dead-letter replay, OpenAPI/Swagger, admin console, Docker Compose demo and prebuilt binary releases.
-
Production self-hosted
v0.1.1 hardened the container deployment: the API port publishes on loopback only, containers drop all capabilities and run on a read-only root filesystem. v0.2.0 added routing rules and a per-channel Telegram proxy, both free in Community. v0.3.0 made a single YAML file the only place AlertLoop is configured, with ${VAR} references keeping secrets out of it.
-
Incident lifecycle and the first integration
Incidents that open on firing, stay updated while the problem lasts and close on resolved, with a recovery notice carrying the outage duration. A Monit adapter, the first inbound integration and free in Community. A signed multi-arch image on GHCR. PostgreSQL integration tests, an upgrade test against a real v0.1.0 database, and operations documentation.
-
Configuration that means what it says
v0.5.x made AlertLoop's own logs readable as plain files and made the documented Compose start fail when AlertLoop does not come up. v0.6.0 made a configuration key AlertLoop does not read stop the start instead of being ignored, stopped the API from being served without a credential, and made sure a recovery never arrives before its alert.
-
No silent loss
v0.7.0 made AlertLoop itself monitorable through GET /v1/stats, let an ingest key be limited to its own sources, and added message search and a backup script with a timer. v0.8.0 replaced the admin token in the console with user accounts, let a channel hand an alert it could not deliver to a fallback channel, added Slack (also Mattermost and Rocket.Chat), Microsoft Teams, Discord, ntfy and Pushover, and made the Monit adapter keep an event it could not send and deliver it later.
-
Toward a stable 1.0
1.0 means a compatibility contract I am willing to keep: an API, a configuration format, a console sign-in and an upgrade path that do not move under a running installation.
-
Pro edition
Multiple projects and workspaces, RBAC, a WhatsApp channel, escalation policies and on-call schedules, and retention policies per project and per event type.
-
Enterprise
Commercial licence tooling, SSO, audit log, HA deployment documentation, a Helm chart and custom adapters.
Related notes
The decisions behind AlertLoop
Written while the work was still fresh, including the parts that did not go to plan.
Run it yourself, or tell me where it breaks.
AlertLoop is free, self-hosted and open source. If you deploy it and something is wrong (a channel that fails, a failure path that isn't recoverable, a missing capability), tell me.