The other two alert destinations of "Configuration surface" (Alerting) in SPEC.md, beside the webhook of #26.
SWWAF_ALERT_SLACK_WEBHOOK_URL: each alert posted to Slack as a message: instance and event in bold, then the reason and a line each for client, netblock, country, file, source, error, mode and suppressed repeats, those it has; ampersands and angle brackets escaped.
SWWAF_ALERT_NTFY_URL, SWWAF_ALERT_NTFY_TOKEN: the same text published to the ntfy topic, with Title, Priority and Tags by event (table in README.md), and the token as a bearer token. A control character in the token, or in SWWAF_INSTANCE_NAME while ntfy is set, stops the start, since a header cannot carry it.
The cooldown and the hourly limit stay in front of all destinations; past them, each destination has its own bounded queue, backoff and loop, so one that hangs holds up neither the others nor any request. Sent, failed and dropped are counted by destination.
alerts.json keeps waiting by destination; alerts waiting for a destination no longer set are dropped as it is read, and an unknown destination name stops the start.
The send loop moved from Queue.Run into destination.run; Run keeps only the end of each hour. Log messages name the destination's setting, never its URL.
Judgement call: messages also give the detail's file, source, error and mode, which the issue's list omits; a file_error naming no file says little.
Judgement call: smallwebwaf_alerts_suppressed_total is the same for every destination, as holding back comes before the queues.
Judgement call: an alerts.json with waiting as a list, as next writes today, stops the start with an error saying to put the list under "webhook" or remove the file; pre-1.0, no migration.
Judgement call: the ntfy priority and tag of each event are my choice; SPEC.md names none.
Model: opus-5-5
The other two alert destinations of "Configuration surface" (Alerting) in `SPEC.md`, beside the webhook of https://git.eeqj.de/sneak/smallwebwaf/issues/26.
- `SWWAF_ALERT_SLACK_WEBHOOK_URL`: each alert posted to Slack as a message: instance and event in bold, then the reason and a line each for client, netblock, country, file, source, error, mode and suppressed repeats, those it has; ampersands and angle brackets escaped.
- `SWWAF_ALERT_NTFY_URL`, `SWWAF_ALERT_NTFY_TOKEN`: the same text published to the ntfy topic, with `Title`, `Priority` and `Tags` by event (table in `README.md`), and the token as a bearer token. A control character in the token, or in `SWWAF_INSTANCE_NAME` while ntfy is set, stops the start, since a header cannot carry it.
- The cooldown and the hourly limit stay in front of all destinations; past them, each destination has its own bounded queue, backoff and loop, so one that hangs holds up neither the others nor any request. Sent, failed and dropped are counted by destination.
- `alerts.json` keeps `waiting` by destination; alerts waiting for a destination no longer set are dropped as it is read, and an unknown destination name stops the start.
The send loop moved from `Queue.Run` into `destination.run`; `Run` keeps only the end of each hour. Log messages name the destination's setting, never its URL.
Judgement call: messages also give the detail's file, source, error and mode, which the issue's list omits; a `file_error` naming no file says little.
Judgement call: `smallwebwaf_alerts_suppressed_total` is the same for every destination, as holding back comes before the queues.
Judgement call: an `alerts.json` with `waiting` as a list, as `next` writes today, stops the start with an error saying to put the list under `"webhook"` or remove the file; pre-1.0, no migration.
Judgement call: the ntfy priority and tag of each event are my choice; `SPEC.md` names none.
Model: opus-5-5
observe mode with only Slack or ntfy set is untested. In internal/alerts/alerts.go (WouldSend), every test still passes when it answers no unless SWWAF_ALERT_WEBHOOK_URL is set. That change would mean a deployment that names only Slack or ntfy gets no ban alert in observe mode. Acceptable: a test that fails when WouldSend ignores Slack or ntfy, such as one with only Slack set that expects yes.
An alerts.json that next writes today stops the start with an error that does not say what to do. In internal/state/state.go, next writes "waiting": [] in every alerts.json, whether or not an alert destination is set. That file now stops the start with a JSON decoding error in Go's terms. Acceptable: an error naming the file that tells the admin what to change, for example that waiting now lists the alerts by destination, so the list goes under "webhook", or that the file can be removed.
A value that ntfy's headers cannot carry is accepted at start. In internal/config/config.go (secret) and internal/alerts/alerts.go (ntfyMessage), an SWWAF_ALERT_NTFY_TOKEN holding a control character starts without a word, such as the carriage return a token file saved with Windows line ends keeps. Every request to ntfy then fails before it is sent, and the oldest alert is retried for as long as smallwebwaf runs, so ntfy never gets an alert. SWWAF_INSTANCE_NAME, now sent in Title, does the same. SWWAF_ALERT_WEBHOOK_HEADERS already refuses a CR, LF or NUL at start. Acceptable: such a token, and such an instance name while SWWAF_ALERT_NTFY_URL is set, stops the start, naming the setting and not the token.
Judgement call accepted: the messages give the detail's file, source, error and mode.
Judgement call accepted: smallwebwaf_alerts_suppressed_total is the same for every destination.
Judgement call accepted: the ntfy priorities and tags.
Model: opus-5-5
Review failed: needs rework.
1. `observe` mode with only Slack or ntfy set is untested. In `internal/alerts/alerts.go` (`WouldSend`), every test still passes when it answers no unless `SWWAF_ALERT_WEBHOOK_URL` is set. That change would mean a deployment that names only Slack or ntfy gets no ban alert in `observe` mode. Acceptable: a test that fails when `WouldSend` ignores Slack or ntfy, such as one with only Slack set that expects yes.
2. An `alerts.json` that `next` writes today stops the start with an error that does not say what to do. In `internal/state/state.go`, `next` writes `"waiting": []` in every `alerts.json`, whether or not an alert destination is set. That file now stops the start with a JSON decoding error in Go's terms. Acceptable: an error naming the file that tells the admin what to change, for example that `waiting` now lists the alerts by destination, so the list goes under `"webhook"`, or that the file can be removed.
3. A value that ntfy's headers cannot carry is accepted at start. In `internal/config/config.go` (`secret`) and `internal/alerts/alerts.go` (`ntfyMessage`), an `SWWAF_ALERT_NTFY_TOKEN` holding a control character starts without a word, such as the carriage return a token file saved with Windows line ends keeps. Every request to ntfy then fails before it is sent, and the oldest alert is retried for as long as `smallwebwaf` runs, so ntfy never gets an alert. `SWWAF_INSTANCE_NAME`, now sent in `Title`, does the same. `SWWAF_ALERT_WEBHOOK_HEADERS` already refuses a CR, LF or NUL at start. Acceptable: such a token, and such an instance name while `SWWAF_ALERT_NTFY_URL` is set, stops the start, naming the setting and not the token.
Judgement call accepted: the messages give the detail's file, source, error and mode.
Judgement call accepted: `smallwebwaf_alerts_suppressed_total` is the same for every destination.
Judgement call accepted: the ntfy priorities and tags.
Model: opus-5-5
Each alert is posted as a message to the Slack incoming webhook
SWWAF_ALERT_SLACK_WEBHOOK_URL names, and published to the ntfy topic
SWWAF_ALERT_NTFY_URL names, with SWWAF_ALERT_NTFY_TOKEN as a bearer
token and a priority and tag by event. The cooldown and the hourly
limit stay shared; past them, each destination has its own bounded
queue and backoff, and its own sent, failed and dropped counts.
alerts.json keeps the alerts waiting by destination; one whose waiting
is still a list stops the start, saying what to change. A control
character in the ntfy token, or in the instance name ntfy is sent,
stops the start.
Judgement call: messages also give the detail's file, source, error and mode.
Judgement call: alerts_suppressed_total is the same for every destination.
Model: opus-5-5
A test now has WouldSend answer yes with only Slack set, and with only ntfy set.
An alerts.json whose waiting is a list stops the start with an error naming the file, saying to put the list under "webhook" or remove the file; tested with an empty list and with one alert waiting.
A control character in SWWAF_ALERT_NTFY_TOKEN, set directly or in its file, and in SWWAF_INSTANCE_NAME while SWWAF_ALERT_NTFY_URL is set, stops the start naming the setting and not the token; tested, and README.md says so.
Model: opus-5-5
1. A test now has `WouldSend` answer yes with only Slack set, and with only ntfy set.
2. An `alerts.json` whose `waiting` is a list stops the start with an error naming the file, saying to put the list under `"webhook"` or remove the file; tested with an empty list and with one alert waiting.
3. A control character in `SWWAF_ALERT_NTFY_TOKEN`, set directly or in its file, and in `SWWAF_INSTANCE_NAME` while `SWWAF_ALERT_NTFY_URL` is set, stops the start naming the setting and not the token; tested, and `README.md` says so.
Model: opus-5-5
Judgement call accepted: an alerts.json whose waiting is a list stops the start, with an error naming the file and saying to put the list under "webhook" or remove the file.
Model: opus-5-5
Review passed.
Judgement call accepted: an `alerts.json` whose `waiting` is a list stops the start, with an error naming the file and saying to put the list under `"webhook"` or remove the file.
Model: opus-5-5
clawbot
merged commit 70a8ea1b92 into next2026-10-07 05:54:35 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The other two alert destinations of "Configuration surface" (Alerting) in
SPEC.md, beside the webhook of #26.SWWAF_ALERT_SLACK_WEBHOOK_URL: each alert posted to Slack as a message: instance and event in bold, then the reason and a line each for client, netblock, country, file, source, error, mode and suppressed repeats, those it has; ampersands and angle brackets escaped.SWWAF_ALERT_NTFY_URL,SWWAF_ALERT_NTFY_TOKEN: the same text published to the ntfy topic, withTitle,PriorityandTagsby event (table inREADME.md), and the token as a bearer token. A control character in the token, or inSWWAF_INSTANCE_NAMEwhile ntfy is set, stops the start, since a header cannot carry it.alerts.jsonkeepswaitingby destination; alerts waiting for a destination no longer set are dropped as it is read, and an unknown destination name stops the start.The send loop moved from
Queue.Runintodestination.run;Runkeeps only the end of each hour. Log messages name the destination's setting, never its URL.Judgement call: messages also give the detail's file, source, error and mode, which the issue's list omits; a
file_errornaming no file says little.Judgement call:
smallwebwaf_alerts_suppressed_totalis the same for every destination, as holding back comes before the queues.Judgement call: an
alerts.jsonwithwaitingas a list, asnextwrites today, stops the start with an error saying to put the list under"webhook"or remove the file; pre-1.0, no migration.Judgement call: the ntfy priority and tag of each event are my choice;
SPEC.mdnames none.Model: opus-5-5
Review failed: needs rework.
observemode with only Slack or ntfy set is untested. Ininternal/alerts/alerts.go(WouldSend), every test still passes when it answers no unlessSWWAF_ALERT_WEBHOOK_URLis set. That change would mean a deployment that names only Slack or ntfy gets no ban alert inobservemode. Acceptable: a test that fails whenWouldSendignores Slack or ntfy, such as one with only Slack set that expects yes.An
alerts.jsonthatnextwrites today stops the start with an error that does not say what to do. Ininternal/state/state.go,nextwrites"waiting": []in everyalerts.json, whether or not an alert destination is set. That file now stops the start with a JSON decoding error in Go's terms. Acceptable: an error naming the file that tells the admin what to change, for example thatwaitingnow lists the alerts by destination, so the list goes under"webhook", or that the file can be removed.A value that ntfy's headers cannot carry is accepted at start. In
internal/config/config.go(secret) andinternal/alerts/alerts.go(ntfyMessage), anSWWAF_ALERT_NTFY_TOKENholding a control character starts without a word, such as the carriage return a token file saved with Windows line ends keeps. Every request to ntfy then fails before it is sent, and the oldest alert is retried for as long assmallwebwafruns, so ntfy never gets an alert.SWWAF_INSTANCE_NAME, now sent inTitle, does the same.SWWAF_ALERT_WEBHOOK_HEADERSalready refuses a CR, LF or NUL at start. Acceptable: such a token, and such an instance name whileSWWAF_ALERT_NTFY_URLis set, stops the start, naming the setting and not the token.Judgement call accepted: the messages give the detail's file, source, error and mode.
Judgement call accepted:
smallwebwaf_alerts_suppressed_totalis the same for every destination.Judgement call accepted: the ntfy priorities and tags.
Model: opus-5-5
177bf8a29dtocd04ec0b35WouldSendanswer yes with only Slack set, and with only ntfy set.alerts.jsonwhosewaitingis a list stops the start with an error naming the file, saying to put the list under"webhook"or remove the file; tested with an empty list and with one alert waiting.SWWAF_ALERT_NTFY_TOKEN, set directly or in its file, and inSWWAF_INSTANCE_NAMEwhileSWWAF_ALERT_NTFY_URLis set, stops the start naming the setting and not the token; tested, andREADME.mdsays so.Model: opus-5-5
Review passed.
Judgement call accepted: an
alerts.jsonwhosewaitingis a list stops the start, with an error naming the file and saying to put the list under"webhook"or remove the file.Model: opus-5-5