Name the push verification entry absolutely, from both sides #213

Closed
opened 2026-09-02 08:01:26 +00:00 by jcoffey-dev · 0 comments
Owner

Fixes the latent bug the BASE_PATH work (#201) spotted and correctly left alone.

What was wrong

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

Both sides named that entry relatively, and a relative cache key is resolved against the URL of whoever is asking:

resolves to
worker at <base>/sw.js <base>/ihasmail-push-verification
tab at /mail/inbox/abc /mail/inbox/ihasmail-push-verification

They agreed only when the open page happened to be the root — which is why it survived this long: that is the case anyone tries first.

Why it matters more than it looks

The failure is quiet in the worst way. A subscription that never gets its code back simply never delivers. There is no error, nothing logged, nothing to notice — it is indistinguishable from push not working at all.

The fix

Both sides now build the key from the mount: the worker from the BASE it already derives from self.location, the page through withBase. Same string, whatever route is open.

I also checked the cache name while I was here — worker caches.open(VERSION) and page caches.open("ihasmail-v2") — and those already match. The key was the only mismatch.

Testing

4 tests pinning the shape of the key rather than the plumbing: absolute, stable, anchored to the mount, and carrying no route in it. The plumbing itself needs a service worker and a real push, which no test runner has.

Suite green: web 864 across 88 files, server 122, typecheck clean.

Not verified end to end

This needs a real push subscription, a closed tab and a reopened one — none of which I can drive here. The reasoning and the key shape are sound, but the handshake itself is unexercised.

Merged 2026-09-02 as coffey-labs/ihasmail@4570df6eab

Rebuilt from: git history, session transcript.

Fixes the latent bug the BASE_PATH work (#201) spotted and correctly left alone. ## What was wrong A JMAP push subscription stays silent until the client echoes back a verification code. When that code arrives with **no tab open**, the service worker leaves it in the browser cache for the next tab to collect. Both sides named that entry **relatively**, and a relative cache key is resolved against the URL of whoever is asking: | | resolves to | |---|---| | worker at `<base>/sw.js` | `<base>/ihasmail-push-verification` | | tab at `/mail/inbox/abc` | `/mail/inbox/ihasmail-push-verification` | They agreed only when the open page happened to be the **root** — which is why it survived this long: that is the case anyone tries first. ## Why it matters more than it looks The failure is quiet in the worst way. A subscription that never gets its code back simply never delivers. There is no error, nothing logged, nothing to notice — it is indistinguishable from push not working at all. ## The fix Both sides now build the key from the mount: the worker from the `BASE` it already derives from `self.location`, the page through `withBase`. Same string, whatever route is open. I also checked the cache *name* while I was here — worker `caches.open(VERSION)` and page `caches.open("ihasmail-v2")` — and those already match. The key was the only mismatch. ## Testing 4 tests pinning the shape of the key rather than the plumbing: absolute, stable, anchored to the mount, and carrying no route in it. The plumbing itself needs a service worker and a real push, which no test runner has. Suite green: web 864 across 88 files, server 122, typecheck clean. ## Not verified end to end This needs a real push subscription, a closed tab and a reopened one — none of which I can drive here. The reasoning and the key shape are sound, but the handshake itself is unexercised. **Merged** 2026-09-02 as coffey-labs/ihasmail@4570df6eab4e <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.