The ntfy section recommended a server-wide setting for one topic's sake and then described only what it did to that topic. Applied to a real server, auth-default-access: deny-all closes every other topic too — anything that was subscribing anonymously stops, including the web app and the phone app on topics that have nothing to do with dejima.
One unreadable topic denies the whole connection
This is the part that makes a single missing entry look like a broken server. The apps subscribe to all of a server's topics over one request — /topic-a,topic-b,topic-c/ws — and ntfy authorises it by looping the list and returning 403 for the entire request on the first topic the account cannot read:
So one stray subscription with no grant takes down alerts and notifications together, both of which were configured correctly.
The symptom points at the wrong component
A denied subscribe fails the WebSocket upgrade, which the clients report as WebSocketNotSupportedException or websocket upgrade failed with HTTP 403 — which reads as a broken reverse proxy rather than as the ACL doing what it was told. Publishing keeps working throughout, because that path carries a token, so a test alert arriving proves nothing about whether anyone can read it.
Also stated, because it is the natural worry
An everyone grant does cover a logged-in user, on topics where that user has no entry of their own. authorizeTopicAccess resolves permissions from the specific user's rows andeveryone's, preferring the user's own and then the longer topic pattern. So logging in to read the alert topic does not cost you the anonymous grant on dejima's — which is the thing the previous revision left a reader to guess at.
Placement
The warning sits between the server.yml block and the commands, so a reader meets it before running them rather than after — a test holds it in that order. And the grant block hands the reading account read-only on "*" rather than on the alert topic alone: the narrow version is exactly the configuration that took everything else down, so it should not be the one somebody pastes.
Added
Grant the other topics back, with a wildcard where there are several. Log the subscribers in, and where that setting lives in each app. curl commands that separate the ACL from the proxy and then find a stray, without involving WebSockets at all. And the ?auth= query parameter, since a browser cannot set an Authorization header on a WebSocket — its value is base64 of the literal Basic followed by base64 of user:pass, verified by decoding ntfy's own example.
Behaviour checked against ntfy's source rather than inferred: the docs do not cover either the multi-topic authorisation loop or the everyone-versus-authenticated-user precedence.
Tested: 83 passing.
**Summary generated by LLM agent**
The ntfy section recommended a server-wide setting for one topic's sake and then described only what it did to that topic. Applied to a real server, `auth-default-access: deny-all` closes every other topic too — anything that was subscribing anonymously stops, including the web app and the phone app on topics that have nothing to do with dejima.
## One unreadable topic denies the whole connection
This is the part that makes a single missing entry look like a broken server. The apps subscribe to all of a server's topics over one request — `/topic-a,topic-b,topic-c/ws` — and ntfy authorises it by looping the list and returning 403 for the entire request on the first topic the account cannot read:
```go
for _, t := range topics {
if err := s.userManager.Authorize(u, t.ID, perm); err != nil {
return errHTTPForbidden.With(t)
}
}
```
So one stray subscription with no grant takes down alerts and notifications together, both of which were configured correctly.
## The symptom points at the wrong component
A denied subscribe fails the WebSocket upgrade, which the clients report as `WebSocketNotSupportedException` or `websocket upgrade failed with HTTP 403` — which reads as a broken reverse proxy rather than as the ACL doing what it was told. Publishing keeps working throughout, because that path carries a token, so a test alert arriving proves nothing about whether anyone can read it.
## Also stated, because it is the natural worry
An `everyone` grant does cover a logged-in user, on topics where that user has no entry of their own. `authorizeTopicAccess` resolves permissions from the specific user's rows *and* `everyone`'s, preferring the user's own and then the longer topic pattern. So logging in to read the alert topic does not cost you the anonymous grant on dejima's — which is the thing the previous revision left a reader to guess at.
## Placement
The warning sits between the `server.yml` block and the commands, so a reader meets it before running them rather than after — a test holds it in that order. And the grant block hands the reading account `read-only` on `"*"` rather than on the alert topic alone: the narrow version is exactly the configuration that took everything else down, so it should not be the one somebody pastes.
## Added
Grant the other topics back, with a wildcard where there are several. Log the subscribers in, and where that setting lives in each app. `curl` commands that separate the ACL from the proxy and then find a stray, without involving WebSockets at all. And the `?auth=` query parameter, since a browser cannot set an `Authorization` header on a WebSocket — its value is base64 of the literal `Basic ` followed by base64 of `user:pass`, verified by decoding ntfy's own example.
Behaviour checked against ntfy's source rather than inferred: the docs do not cover either the multi-topic authorisation loop or the `everyone`-versus-authenticated-user precedence.
Tested: 83 passing.
The page recommended a server-wide setting for one topic's sake and described
only the effect on that topic. Applied to a real server it closes every other
one, and one unreadable topic denies the rest along with it: the apps subscribe
to all of a server's topics over a single request, and ntfy authorises that by
looping the list and returning 403 for the whole request on the first topic the
account cannot read. Nothing reports which one. So a stray subscription takes
down alerts and notifications together, both correctly configured, and it reads
as the server being broken.
The symptom hides it further. A denied subscribe fails the WebSocket upgrade,
which the apps report as WebSocketNotSupportedException or a 403 on the upgrade
- which points at the reverse proxy. Publishing keeps working the whole time,
since that path carries a token, so a test alert arriving proves nothing about
whether anybody can read it.
The warning sits between the setting and the commands, where a reader meets it
before running them rather than after, and a test holds it in that order. The
grant block gives the reading account read on "*" rather than on the alert
topic alone - the narrow version is the configuration that took everything
else down, so the block somebody pastes should not be it.
Also stated, because it is the natural worry and the answer is not obvious: an
everyone grant does cover a logged-in user, on topics where that user has no
entry of their own. Logging in to read the alert topic does not cost you the
anonymous grant on dejima's.
Behaviour read from ntfy's source. The documentation covers neither the
multi-topic authorisation loop nor the everyone-versus-authenticated precedence.
dejima-agent
changed title from Say what deny-all does to the rest of the server to Warn that deny-all takes unrelated notifications down with it2026-08-29 00:43:26 -07:00
branderson
approved these changes 2026-08-29 01:00:23 -07: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.
Summary generated by LLM agent
The ntfy section recommended a server-wide setting for one topic's sake and then described only what it did to that topic. Applied to a real server,
auth-default-access: deny-allcloses every other topic too — anything that was subscribing anonymously stops, including the web app and the phone app on topics that have nothing to do with dejima.One unreadable topic denies the whole connection
This is the part that makes a single missing entry look like a broken server. The apps subscribe to all of a server's topics over one request —
/topic-a,topic-b,topic-c/ws— and ntfy authorises it by looping the list and returning 403 for the entire request on the first topic the account cannot read:So one stray subscription with no grant takes down alerts and notifications together, both of which were configured correctly.
The symptom points at the wrong component
A denied subscribe fails the WebSocket upgrade, which the clients report as
WebSocketNotSupportedExceptionorwebsocket upgrade failed with HTTP 403— which reads as a broken reverse proxy rather than as the ACL doing what it was told. Publishing keeps working throughout, because that path carries a token, so a test alert arriving proves nothing about whether anyone can read it.Also stated, because it is the natural worry
An
everyonegrant does cover a logged-in user, on topics where that user has no entry of their own.authorizeTopicAccessresolves permissions from the specific user's rows andeveryone's, preferring the user's own and then the longer topic pattern. So logging in to read the alert topic does not cost you the anonymous grant on dejima's — which is the thing the previous revision left a reader to guess at.Placement
The warning sits between the
server.ymlblock and the commands, so a reader meets it before running them rather than after — a test holds it in that order. And the grant block hands the reading accountread-onlyon"*"rather than on the alert topic alone: the narrow version is exactly the configuration that took everything else down, so it should not be the one somebody pastes.Added
Grant the other topics back, with a wildcard where there are several. Log the subscribers in, and where that setting lives in each app.
curlcommands that separate the ACL from the proxy and then find a stray, without involving WebSockets at all. And the?auth=query parameter, since a browser cannot set anAuthorizationheader on a WebSocket — its value is base64 of the literalBasicfollowed by base64 ofuser:pass, verified by decoding ntfy's own example.Behaviour checked against ntfy's source rather than inferred: the docs do not cover either the multi-topic authorisation loop or the
everyone-versus-authenticated-user precedence.Tested: 83 passing.
5cd5fba7d6to8c142897d88c142897d8tob2e578c7e1Say what deny-all does to the rest of the serverto Warn that deny-all takes unrelated notifications down with it