Released Open Source · Self-hosted

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.

Community v0.4.3 · alpha · Go binary or signed image · SQLite or PostgreSQL · AGPL-3.0-only

open your first incident
# 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.

Events: unified list across incidents, business events, and audit.
Deliveries: per-channel attempts with retries, dead-letter, and replay.

Architecture

The delivery pipeline

The happy path is deliberately short. Everything interesting happens in the failure path underneath it.

Source

Application

Ingest

AlertLoop API

Persist

Event storage

Route

Routing rules

Queue

Delivery queue

Send

Workers

Channels

Email / Webhook / Telegram

When a delivery fails

Delivery attempt fails: SMTP timeout
Delivery status: recorded as an attempt, separate from event state
Retry: exponential backoff, a worker picks the job up again
Dead letter: attempts exhausted, the job is parked, not lost
Replay: re-queued from the admin console once the channel is back

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.

status: firing. The incident opens, routed channels are notified
firing again, same dedupe_key: the open incident is updated in place
severity, message, payload and last_seen_at move; nothing new is sent
status: resolved. resolved_at is recorded, the dedupe_key is freed
Recovery notice, with the outage duration, to those same channels

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 channel whose alert dead-lettered is skipped, because it never learned there was a problem. 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.4.3 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), 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. 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. On by default; notify_on_resolve turns it off.

Event lifecycle

Events move through new, acknowledged, resolved, muted and escalated, with a manual escalate action.

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.

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.

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.

Admin console

A React console (Overview, Events, Event detail, Deliveries with dead-letter replay) embedded in the binary and served at /admin, protected by a single admin token; Community has no user accounts.

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.

Monit integration

A shell adapter that turns Monit checks into AlertLoop events: bounded retries, a documented exit-code contract, no secret in process arguments or logs, eleven 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 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.

Planned

Pro Self-hosted

Planned. Not built. May include multiple projects and workspaces, RBAC, escalation policies and on-call schedules, SDKs for PHP, Go and JavaScript/Node.js, a WhatsApp channel, and per-project and per-event-type retention policies managed from the console.

Commercial licence · pricing not set

Planned

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

The Community edition is the only thing you can download and run today.

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.4.3 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.

Community · v0.4.3 released Alpha · pre-1.0 Documentation · README and Swagger Pro · planned Enterprise · planned

Roadmap

What shipped, what I'm doing now, what comes next

Phases marked as shipped are in the released binary. The rest are intentions.

  1. Phase 0 · Shipped

    Product definition

    Product brief, business model, licensing model, technical architecture, API draft and deployment model, all decided before the first line of Go.

  2. Phase 1 · Shipped in v0.1.0

    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.

  3. Phase 2 · Shipped in v0.1.1 → v0.3.1

    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.

  4. Phase 3 · Shipped in v0.4.0 → v0.4.3

    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.

  5. Phase 4 · In progress

    Toward a stable 1.0

    1.0 means a compatibility contract I am willing to keep: an API, a configuration format and an upgrade path that do not move under a running installation. A documentation site is the other open item.

  6. Phase 5 · Planned

    Pro edition

    Multiple projects and workspaces, RBAC, SDKs for PHP, Go and JavaScript/Node.js, a WhatsApp channel, escalation policies and on-call schedules, and retention policies per project and per event type.

  7. Phase 6 · Planned

    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.

View all notes

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.