What went wrong
In ntfy, one account can keep several live subscriptions to the same topic: different browser tabs, devices, or streaming transports can all remain connected at once. When an administrator revokes access or deletes the account, changing the stored permission is only half of the job. Every connection that still carries the old authority must also be ended.
The helper responsible for that teardown, CancelSubscriberUser, iterated over the topic's subscribers and looked for the specified user. It cancelled the first match, then returned immediately. That control flow quietly treated “one user” as if it meant “one connection”. Any other matching streams were left alive.
The access-reset and user-deletion paths both relied on this helper. As a result, the durable account state and the in-memory connection state could disagree: the user was no longer authorised, while some sessions created under the previous policy were still receiving messages.
Argo surfaced the pattern during a source review. I followed the relevant call paths, checked the behaviour by hand, and opened a public pull request with a small repair and a regression test for two subscriptions belonging to the same user.
Why it mattered
This was not a way for an unauthenticated stranger to enter a private topic. It required a user who had already established valid subscriptions before their access was revoked or their account was deleted. The failure happened during the transition from authorised to unauthorised.
That boundary still matters. The remaining live streams could continue receiving future messages on the topic even though a new connection from the same user should no longer have been accepted. Disconnecting one tab or device could therefore give an administrator the impression that revocation had succeeded while another connection remained active.
Argo records the issue as High because a remote account could continue receiving future messages after an explicit access revocation or account deletion. This is an Argo assessment, not an upstream score: ntfy places the change in the Security section of its release notes but publishes neither a formal severity rating nor an affected-version range. The article also covers only the live-subscription path established by the public fix, not adjacent delivery mechanisms discussed separately in the original report.
How it was fixed
The essential repair was to remove the early return and continue through the subscriber collection, cancelling every stream whose user ID matched. The regression test gives one user two subscriptions on the same topic and verifies that both are ended.
The original pull request was closed after the maintainer incorporated the repair into a broader change for propagating access changes across ntfy's experimental cluster. That upstream work also rechecked subscriptions when access state changed on another node. For the narrower bug described here, its explicit contract is simple: CancelSubscriberUser ends all of a user's streams on the topic, not only the first.
The correction shipped in ntfy server 2.34.0 on 24 October 2026. The release notes name the previous behaviour, link the original report, and credit @gigioneggiando.
After the patch
Revocation is a state transition, not just a database update. A system may represent the same authority in stored permissions, caches, tokens, sessions, subscriptions, and work already in progress. Removing it from one representation does not make the others disappear.
A useful review question is therefore: “How many live things can this one identity own?” Whenever the answer may be more than one, teardown code should be checked for first-match searches, early returns, and singular APIs hiding plural state. Successful revocation means invalidating every active representation within the boundary the system promises—not merely finding one of them.