Keel ntfy: an alert server for one site, as a layer on Keel Web (decision 0063)
  • Shell 99.3%
  • Makefile 0.7%
Find a file
Marcos Méndez fb4f8e4a6e
All checks were successful
tests / stack (push) Successful in 1m54s
tests / appliance (push) Successful in 12s
tests / package (push) Has been skipped
tests / tests (push) Successful in 53s
security / security (push) Successful in 1m13s
Merge pull request 'chore: stable 1 carries keel 0.23.55 and the syncthing overlay' (#4) from chore/stable-1-keel-0.23.55 into master
2026-10-25 14:34:12 -03:00
.forgejo/workflows feat: the ntfy layer on web, an alert server for one site (decision 0063) 2026-10-22 21:32:14 +00:00
conf.d fix: port 80 serves only the ACME challenge and a 301 to HTTPS, and ntfy-client.service stays off (19.0-2) 2026-10-22 23:55:41 +00:00
keel feat: the ntfy layer on web, an alert server for one site (decision 0063) 2026-10-22 21:32:14 +00:00
overlay fix: port 80 serves only the ACME challenge and a 301 to HTTPS, and ntfy-client.service stays off (19.0-2) 2026-10-22 23:55:41 +00:00
plan feat: the ntfy layer on web, an alert server for one site (decision 0063) 2026-10-22 21:32:14 +00:00
tests fix: port 80 serves only the ACME challenge and a 301 to HTTPS, and ntfy-client.service stays off (19.0-2) 2026-10-22 23:55:41 +00:00
.gitignore feat: the ntfy layer on web, an alert server for one site (decision 0063) 2026-10-22 21:32:14 +00:00
changelog chore: stable 1 carries keel 0.23.55 and the syncthing overlay 2026-10-25 17:19:30 +00:00
COVERAGE.md feat: the ntfy layer on web, an alert server for one site (decision 0063) 2026-10-22 21:32:14 +00:00
LICENSE Initial commit 2026-10-22 18:13:07 -03:00
Makefile feat: the ntfy layer on web, an alert server for one site (decision 0063) 2026-10-22 21:32:14 +00:00
README.rst fix: port 80 serves only the ACME challenge and a 301 to HTTPS, and ntfy-client.service stays off (19.0-2) 2026-10-22 23:55:41 +00:00

Keel ntfy
=========

`ntfy`_ is a server that sends a message to a phone. This layer is the alert
server of Keel Linux for one site (handbook decision 0063). It is built on
the ``web`` layer:

    bt-layer ntfy --parent web

The chain is Core, Web and ntfy. The server is the ``ntfy`` package of
Debian 13, version 2.11. Keel has no package of its own for it.

Why a server of its own
-----------------------

An alert must not depend on what it reports. A server on a pair would
depend on the VIP, etcd, the mesh and the replication, which are the parts
that it must report on. So each site runs one node of one, outside the
cluster that it watches. keel sends each alert to every server in its list.
A phone subscribes to the same topic on all of them.

What the layer adds to Web
--------------------------

- ``ntfy.service`` on ``127.0.0.1:2586``, with the configuration
  ``/etc/ntfy/server.yml``. The server trusts the proxy, denies all access by
  default, keeps no attachment and contacts no other server. Its state is in
  ``/var/lib/ntfy``.
- A route in each include directory of Keel Web. Nginx terminates HTTPS and
  proxies to the loopback. It does not buffer, and it waits 1 hour for a
  subscription. Port 80 does not serve ntfy: it serves the ACME HTTP-01
  challenge, and answers 301 to HTTPS for every other request.
- A preset that keeps ``ntfy-client.service`` of the Debian package off.
- The route in ``keel-front.d`` takes the request before Anubis. Anubis
  does not challenge ntfy, because the clients are apps and keel. Coraza
  still inspects the request.
- The first boot hook ``45ntfy``. It writes ``base-url`` from
  ``app.options.base_url``, starts the server and makes two accounts. The
  administrator has the role ``admin`` and the password
  ``secrets.app_password``. The publisher can only write, to any topic.
- The manifest ``ntfy.yaml``. It declares the process, a health check of
  ``/v1/health``, the option ``base_url`` and the secret
  ``publisher_password``.

The image has no account, no token and no password. The user database does
not exist in the image.

The token
---------

ntfy makes the token. It cannot take a value that the description chose.
So the token is not a secret of the instance description. The first boot
writes it to ``/etc/keel/secrets/ntfy_publish_token``. Only root can read
the file. Copy the token to ``monitor.notify.ntfy.token`` of each node that
sends alerts to this server. A later run of the hook keeps the token.

Who may read
------------

The publisher cannot read. The administrator reads and writes every topic.
A person who reads gets an account with read access to the topics of their
tenant (decision 0063, point 4). Make it on the machine, as the service
user:

    NTFY_CONFIG_FILE=/etc/ntfy/server.yml setpriv --reuid=_ntfy --regid=_ntfy \
        --init-groups /usr/bin/ntfy user add NAME

A later step makes this a part of the description.

The mode
--------

The node is a simple installation. In that mode the ``vip``, ``etcd``,
``wireguard``, ``crowdsec``, ``coraza`` and ``anubis`` overlays are all
disabled. Manifest version 1 does not let a child change the state of an
overlay that it inherits, so ``ntfy.yaml`` lists no overlay. Write
``installation.mode: simple`` in the description, as
``keel/instance.example.yaml`` does. A cloud mode would enable the mesh and
etcd, and ``keel spec validate`` does not refuse it.

Tests
-----

See ``tests/README.md`` and ``COVERAGE.md``.

.. _ntfy: https://ntfy.sh