Blog
RamzesBy Krzysztof Grajek

Introducing Ramzes: one endpoint for your services' notifications

Ramzes is a self-hosted notification router written in Rust. Services send events to one endpoint; routes decide who is told, where and how often. Built and tested, not released yet.

The Ramzes overview dashboard

Ramzes is a self-hosted notification router. Your services send events to one HTTP endpoint, and routes decide who is told, on which channel and how often.

Before anything else, the status, in the project’s own words:

Status. All of the design is built and tested, but nothing is released yet, and no channel or source has been run against its live service: request formats follow each service’s documentation and are pinned by tests.

So this is an introduction, not a release announcement. There is no published image or binary yet; you build it from a checkout.

The problem

In a system of several services, each one that needs to tell a person something ends up with its own Slack webhook call, its own SMTP settings and its own idea of when to stay quiet. That leaves you with:

  • credentials for chat and email spread over many services;
  • no one place that shows what was sent, to whom, and whether it arrived;
  • no way to quiet a noisy sender without deploying it again;
  • nothing that notices a message went unanswered and tells someone else.

With Ramzes a service only says what happened: a topic, a title, a severity, some labels. Who is told, on which channel, how often and in what words is configuration in Ramzes, changed in one place and recorded when it changes.

What it does

  • Routes. Match on topic, severity, source, labels or an expression over the payload, such as payload.amount > 100 and labels.env == 'prod'. The route tester shows what every route would do with an event, including routes you have not saved yet.
  • Noise control. Per route: dedup, grouping, quiet hours in a time zone and a rate limit. Duplicates are counted instead of repeated, and bursts are grouped into one message. Silences mute matching events on every route.
  • Escalation and acknowledgement. Tell someone else when nobody acknowledges in time. Acknowledge from the UI, the API, a link in the message or a button in Slack.
  • Sources. A JSON API for your own services, and webhook addresses for Alertmanager, Grafana, GitHub, GitLab, Sentry and any other JSON, mapped in the UI.
  • Channels. Slack (webhook or app), Microsoft Teams, Discord, email, ntfy, Telegram, Pushover, PagerDuty, Opsgenie, a signed webhook, and your own connector written as YAML.
  • Delivery. An event is acknowledged only after it is stored. Deliveries retry with backoff, a connection that keeps failing is paused, and failures can be sent again in bulk.
  • A UI for what happened. An overview with charts, a live feed, the journey of each event, open notifications, failures and silences. For every event you can see which routes matched and what each delivery attempt answered.
  • Configuration as data. Every change is kept with a diff and can be rolled back; ramzes export and ramzes import move a configuration between instances.

The Ramzes feed: every received event with severity, topic, source, route and outcome The feed: every event, where it went and what happened.

How it is built

Ramzes is one Rust binary with the web UI inside it. It stores its data in a SQLite file next to it by default, or in Postgres if you point it at one. The test suite runs on both: make test runs the Rust tests on SQLite, and make test-pg runs the same tests on SQLite and on a throwaway Postgres in Docker. There are also lint, UI unit tests and browser tests against the real binary.

A Ramzes notification detail for a disk alert, with deliveries and events One notification, its deliveries and the events behind it.

What it is not

The docs are explicit about what Ramzes is not, and that matters as much as the feature list:

  • Not a product-notification platform. It is for messages to the people who run a system. It has no end-user accounts, subscriber preferences, unsubscribe links or per-recipient inboxes.
  • Not a message broker. There is no publish/subscribe and no consumer protocol. Events come in over HTTP and leave as messages to channels.
  • Not an on-call scheduler. An escalation chain tells other connections after a delay. There are no rotas, shifts or calendars. For paging, Ramzes can send to PagerDuty or Opsgenie.
  • Not highly available. It runs as one instance and does not coordinate several.

Everyone who can sign in is an administrator, so it fits a team that trusts its members with the whole configuration. Roles, single sign-on and an audit log are not built; the Limits page lists those gaps and the hard limits on ingest, delivery and storage.

Try it from a checkout

There is no published image yet, so Ramzes is built from a checkout. With Docker:

git clone https://github.com/softberries/ramzes.git && cd ramzes
docker compose -f deploy/compose.yaml up --build

Open http://localhost:7269. The setup screen creates your account, a source, a connection and a first route. From source instead (Rust 1.98 and Node 24):

make build
./target/release/ramzes

Then send an event with the source key the setup screen gave you:

curl -X POST http://localhost:7269/v1/events \
  -H "Authorization: Bearer <source key>" \
  -H "Content-Type: application/json" \
  -d '{"topic": "orders.failed", "title": "Order 7 failed", "severity": "error",
       "labels": {"env": "prod"}, "payload": {"order": 7}}'

The answer is 202 once the event is stored.

What “tested” means here

Every connector’s request format follows that service’s documentation and is pinned by tests, but none has been run against the live service yet. The Verified claims page says exactly what was checked and how. If you run Ramzes against a real Slack, Teams or PagerDuty account, an issue saying whether it worked is welcome; see CONTRIBUTING.md in the repository before opening a pull request.

The core is licensed under the AGPLv3. A commercial edition is planned; write to hello@softberries.dev.