Name the push verification entry absolutely, from both sides

A JMAP push subscription stays silent until the client echoes back a
verification code. When the code arrives with no tab open, the service
worker leaves it in the cache for the next tab to collect.

Both sides named that entry relatively, and a relative key is resolved
against the URL of whoever is asking. The worker lives at <base>/sw.js, so
it wrote under <base>/; a tab at /mail/inbox/abc looked under
/mail/inbox/. They agreed only when the open page happened to be the root,
which is why this survived: the case that works is the one people try
first.

The failure is quiet in the worst way. A subscription that never gets its
code back simply never delivers, which is indistinguishable from push not
working at all -- there is no error anywhere to notice.

Both sides now build the key from the mount: the worker from the BASE it
already derives from its own location, the page through withBase. Found
while adding BASE_PATH, where the two disagree at every route rather than
only at deep ones; left alone then because it was pre-existing and
unrelated to that change.
This commit is contained in:
2026-09-02 01:01:11 -07:00
parent 13a6bcd66b
commit 44b676c55d
4 changed files with 69 additions and 3 deletions
+6
View File
@@ -1038,6 +1038,12 @@ would have quietly ended the WCAG AA claim two sections down.
run. Where the server also implements `emailpush`, the payload carries the
sender, subject and preview; without it the notification says only that mail
arrived. Offered only on a device you said was yours.
- The verification code a subscription needs is handed to an open tab, or left
in the browser's cache under a key **anchored to where the app is mounted**
for the next tab to collect. Both sides name it absolutely: a relative key is
resolved against the URL of whoever asks, so the worker at `<base>/sw.js` and
a tab at `/mail/inbox/…` were naming two different entries, and agreed only
when the open page happened to be the root.
- **The subscription is renewed on every app start**, because a JMAP push
subscription expires — seven days is the ceiling — and re-registering before
it lapses is the client's job. Renewal can only happen with a page open: