feat: the ntfy layer on web, an alert server for one site (decision 0063) #1

Merged
navigator merged 1 commit from feat/ntfy-layer into master 2026-10-22 18:38:31 -03:00
Owner

What

The layer keel-ntfy, built on web (handbook decision 0063, PR 125). bt-layer ntfy --parent web.

  • The server is the ntfy 2.11 package of Debian 13. It listens on 127.0.0.1:2586. The default access is deny-all. There is no attachment and no upstream server.
  • Nginx of Keel Web terminates HTTPS. The proxy does not buffer and waits 1 hour.
  • The route in keel-front.d is a regular expression location. It wins over the location / that sends requests to Anubis, so Anubis never sees ntfy. Coraza still inspects the request.
  • The first boot hook 45ntfy writes base-url, makes the administrator and the publisher, and writes the token of the publisher to /etc/keel/secrets/ntfy_publish_token (root, mode 0600). The image has no account, no token and no user database.

Points for the maintainer

  1. Overlays. Decision 0063 says the manifest turns off the vip, etcd and syncthing overlays in every mode. Manifest version 1 does not allow it. keel manifest validate answers overlays.etcd: already carried by core (rule 16, keel/manifest/resolve.py). So ntfy.yaml lists no overlay. In simple mode every one of vip, etcd, wireguard, crowdsec, coraza and anubis is disabled, and syncthing is not in the chain. cloud_simple and cloud_advanced would enable the mesh and etcd, and keel spec validate does not refuse them. The example description says installation.mode: simple. A real fix needs a decision on manifest version 2 or a rule in keel spec validate.
  2. Token. ntfy 2.11 makes its tokens and has no setting for a chosen value. So the token is not a secret of the instance description. The hook mints it. The secret publisher_password (generate: required) is the password of the publisher account. A person copies the token to monitor.notify.ntfy.token of each sending node.
  3. Anubis. The drop-in directory of the Anubis policy (common#63) is open and not in the archive (keel-overlay-anubis 0.1.1). So the route in keel-front.d is the exemption. tests/stack.bats proves it with a stub of Anubis, and fails when the file is missing.
  4. Coraza. Not tested. The boot test runs simple mode, where Coraza is off. A publish body in a cloud mode may need a CRS drop-in. Add one only when a test shows a false positive.

Tests

  • bats tests/ and tests/coverage.sh at 100 percent of the shell this layer writes.
  • tests/stack.bats: nginx, the keel-web sites, the real ntfy server and the real hook. 12 tests: access rules, stream, WebSocket, front behind Anubis. They also ran as a fake root in a sandbox. In CI the job stack starts ntfy itself (KEEL_NTFY_STACK_DIRECT=1), so the unit and its sandbox run in the boot test only.
  • tests/boot-test.sh: for the image build per merge (decision 0061). Not run yet. No layer of this appliance exists.

Open

The job tests / appliance runs with allow_unpublished until the first layer is published.

## What The layer keel-ntfy, built on `web` (handbook decision 0063, PR 125). `bt-layer ntfy --parent web`. - The server is the `ntfy` 2.11 package of Debian 13. It listens on 127.0.0.1:2586. The default access is `deny-all`. There is no attachment and no upstream server. - Nginx of Keel Web terminates HTTPS. The proxy does not buffer and waits 1 hour. - The route in `keel-front.d` is a regular expression location. It wins over the `location /` that sends requests to Anubis, so Anubis never sees ntfy. Coraza still inspects the request. - The first boot hook `45ntfy` writes `base-url`, makes the administrator and the publisher, and writes the token of the publisher to `/etc/keel/secrets/ntfy_publish_token` (root, mode 0600). The image has no account, no token and no user database. ## Points for the maintainer 1. **Overlays.** Decision 0063 says the manifest turns off the vip, etcd and syncthing overlays in every mode. Manifest version 1 does not allow it. `keel manifest validate` answers `overlays.etcd: already carried by core` (rule 16, `keel/manifest/resolve.py`). So `ntfy.yaml` lists no overlay. In `simple` mode every one of vip, etcd, wireguard, crowdsec, coraza and anubis is disabled, and syncthing is not in the chain. `cloud_simple` and `cloud_advanced` would enable the mesh and etcd, and `keel spec validate` does not refuse them. The example description says `installation.mode: simple`. A real fix needs a decision on manifest version 2 or a rule in `keel spec validate`. 2. **Token.** ntfy 2.11 makes its tokens and has no setting for a chosen value. So the token is not a secret of the instance description. The hook mints it. The secret `publisher_password` (generate: required) is the password of the publisher account. A person copies the token to `monitor.notify.ntfy.token` of each sending node. 3. **Anubis.** The drop-in directory of the Anubis policy (common#63) is open and not in the archive (keel-overlay-anubis 0.1.1). So the route in `keel-front.d` is the exemption. `tests/stack.bats` proves it with a stub of Anubis, and fails when the file is missing. 4. **Coraza.** Not tested. The boot test runs `simple` mode, where Coraza is off. A publish body in a cloud mode may need a CRS drop-in. Add one only when a test shows a false positive. ## Tests - `bats tests/` and `tests/coverage.sh` at 100 percent of the shell this layer writes. - `tests/stack.bats`: nginx, the keel-web sites, the real ntfy server and the real hook. 12 tests: access rules, stream, WebSocket, front behind Anubis. They also ran as a fake root in a sandbox. In CI the job `stack` starts ntfy itself (KEEL_NTFY_STACK_DIRECT=1), so the unit and its sandbox run in the boot test only. - `tests/boot-test.sh`: for the image build per merge (decision 0061). Not run yet. No layer of this appliance exists. ## Open The job `tests / appliance` runs with `allow_unpublished` until the first layer is published.
Add the layer keel-ntfy, built on the web layer. The server is the ntfy
package of Debian 13. It listens on 127.0.0.1:2586 and denies all access by
default. Nginx of Keel Web terminates HTTPS in front of it, with no buffer
and a read timeout of 1 hour. The route in keel-front.d takes the request
before Anubis, so Anubis does not challenge ntfy.

The image holds no account and no token. The first boot hook 45ntfy writes
base-url, makes the administrator and the publisher from the instance
description, and writes the token of the publisher to
/etc/keel/secrets/ntfy_publish_token, readable by root only.

The manifest lists no overlay. Manifest version 1 does not let a child
change the state of an inherited overlay, so the node must be a simple
installation, where the vip, etcd, wireguard, crowdsec, coraza and anubis
overlays are disabled.

Tests: bats with kcov at 100 percent, a stack test with the real nginx,
ntfy and hook, and a boot test for the image build.
navigator force-pushed feat/ntfy-layer from 79225be71e to 97281e26e2
Some checks failed
tests / appliance (pull_request) Successful in 9s
tests / stack (pull_request) Failing after 3m21s
security / security (pull_request) Failing after 1m32s
tests / package (pull_request) Successful in 6s
tests / tests (pull_request) Successful in 1m5s
2026-10-22 18:14:31 -03:00
Compare
navigator force-pushed feat/ntfy-layer from 97281e26e2
Some checks failed
tests / appliance (pull_request) Successful in 9s
tests / stack (pull_request) Failing after 3m21s
security / security (pull_request) Failing after 1m32s
tests / package (pull_request) Successful in 6s
tests / tests (pull_request) Successful in 1m5s
to 7b24d89ee9
Some checks failed
tests / stack (pull_request) Waiting to run
tests / appliance (pull_request) Waiting to run
security / security (pull_request) Failing after 1m33s
tests / tests (pull_request) Successful in 1m18s
tests / package (pull_request) Successful in 10s
2026-10-22 18:15:24 -03:00
Compare
navigator force-pushed feat/ntfy-layer from 7b24d89ee9
Some checks failed
tests / stack (pull_request) Waiting to run
tests / appliance (pull_request) Waiting to run
security / security (pull_request) Failing after 1m33s
tests / tests (pull_request) Successful in 1m18s
tests / package (pull_request) Successful in 10s
to 51641c8f82
Some checks failed
security / security (pull_request) Successful in 2m5s
tests / appliance (pull_request) Successful in 12s
tests / stack (pull_request) Failing after 3m15s
tests / package (pull_request) Successful in 8s
tests / tests (pull_request) Successful in 1m21s
2026-10-22 18:20:23 -03:00
Compare
navigator force-pushed feat/ntfy-layer from 51641c8f82
Some checks failed
security / security (pull_request) Successful in 2m5s
tests / appliance (pull_request) Successful in 12s
tests / stack (pull_request) Failing after 3m15s
tests / package (pull_request) Successful in 8s
tests / tests (pull_request) Successful in 1m21s
to ed466bfd19
Some checks failed
tests / package (pull_request) Successful in 5s
tests / appliance (pull_request) Successful in 9s
tests / tests (pull_request) Successful in 47s
security / security (pull_request) Successful in 1m7s
tests / stack (pull_request) Failing after 3m14s
2026-10-22 18:28:29 -03:00
Compare
navigator force-pushed feat/ntfy-layer from ed466bfd19
Some checks failed
tests / package (pull_request) Successful in 5s
tests / appliance (pull_request) Successful in 9s
tests / tests (pull_request) Successful in 47s
security / security (pull_request) Successful in 1m7s
tests / stack (pull_request) Failing after 3m14s
to 48764c3f43
All checks were successful
tests / package (pull_request) Successful in 5s
tests / appliance (pull_request) Successful in 9s
tests / tests (pull_request) Successful in 47s
security / security (pull_request) Successful in 1m1s
tests / stack (pull_request) Successful in 1m15s
2026-10-22 18:32:16 -03:00
Compare
navigator merged commit 3742ec224c into master 2026-10-22 18:38:31 -03:00
navigator deleted branch feat/ntfy-layer 2026-10-22 18:38:32 -03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Keel-Linux/keel-ntfy!1
No description provided.