I run Variant C behind a proxy that authenticates devices before anything reaches the bridge. Two
things combine so that a lost session leaves the installed PWA with nothing usable on screen and no
way back in. I hit this in my own deployment and had to build a workaround for it, which is really
the point of this report: the workaround is ugly in a way that suggests the shape upstream is
missing.
The service worker answers every navigation from the precache
registerRoute(new NavigationRoute(createHandlerBoundToURL("/index.html"), { denylist: [/^\/api\//] }));
Every navigation except one under /api/ is served from the precached app shell without touching
the network. Confirmed in the built bundle, not just the source: web/dist/sw.js on 0.14.2 contains
denylist:[/^\/api\//].
So a sign-in or enrolment page served by the proxy at / is invisible to an installed PWA. It loads
the cached Collie UI instead, and there is no address bar to fall back to.
The workaround this forces
My proxy serves its enrolment page at /api/__enrol. Not because that is a sensible place for a
page, but because the /api/ denylist is the only crack through the precache. To be reachable from
an installed PWA, an operator has to put a non-API HTML page inside the namespace the API owns, and
order it ahead of the rule that would otherwise answer /api/* with a 401.
That works, and it is squatting. If Collie ever routed /api/__* itself, or tightened the denylist,
it would break silently. A supported escape hatch would remove the guesswork: a documented path that
the service worker always passes to the network, so a fronting proxy has somewhere legitimate to put
a sign-in page.
The other half, and why it makes this worse
Until the operator can reach that page, they also cannot tell they need to. Every loader error
except AbortError lands in the same catch and returns the last-good data flagged stale, which the
connection banner renders as "reconnecting". A 401 from the proxy therefore looks exactly like a
dead radio. It is self-sustaining too: the escalation latch clears only via markLive(), so under a
persistent 401 it never clears.
That half is not proxy-specific. The bridge answers 403 from checkAccess and guard for
host not allowed, cross-origin rejected and device not authorised, so a misconfigured
COLLIE_PUBLIC_HOSTS or a device that is not on the allowlist presents the same way.
I have opened #30 for that half separately, since it seemed clear cut: classify 401 and 403 in the
loaders and let the banner say it is not a connection problem. ApiError already carries the status,
it was simply never read.
What I am asking for
Only the routing half, because it is your call. An extra denylist entry, a dedicated path the
service worker always passes through, or something else entirely. Changing navigation routing in an
installed PWA is easy to get wrong in a way that locks people out, so I would rather build whichever
shape you prefer than guess at it. Happy to do that once you say which.
I run Variant C behind a proxy that authenticates devices before anything reaches the bridge. Two
things combine so that a lost session leaves the installed PWA with nothing usable on screen and no
way back in. I hit this in my own deployment and had to build a workaround for it, which is really
the point of this report: the workaround is ugly in a way that suggests the shape upstream is
missing.
The service worker answers every navigation from the precache
Every navigation except one under
/api/is served from the precached app shell without touchingthe network. Confirmed in the built bundle, not just the source:
web/dist/sw.json 0.14.2 containsdenylist:[/^\/api\//].So a sign-in or enrolment page served by the proxy at
/is invisible to an installed PWA. It loadsthe cached Collie UI instead, and there is no address bar to fall back to.
The workaround this forces
My proxy serves its enrolment page at
/api/__enrol. Not because that is a sensible place for apage, but because the
/api/denylist is the only crack through the precache. To be reachable froman installed PWA, an operator has to put a non-API HTML page inside the namespace the API owns, and
order it ahead of the rule that would otherwise answer
/api/*with a 401.That works, and it is squatting. If Collie ever routed
/api/__*itself, or tightened the denylist,it would break silently. A supported escape hatch would remove the guesswork: a documented path that
the service worker always passes to the network, so a fronting proxy has somewhere legitimate to put
a sign-in page.
The other half, and why it makes this worse
Until the operator can reach that page, they also cannot tell they need to. Every loader error
except
AbortErrorlands in the same catch and returns the last-good data flagged stale, which theconnection banner renders as "reconnecting". A 401 from the proxy therefore looks exactly like a
dead radio. It is self-sustaining too: the escalation latch clears only via
markLive(), so under apersistent 401 it never clears.
That half is not proxy-specific. The bridge answers 403 from
checkAccessandguardforhost not allowed,cross-origin rejectedanddevice not authorised, so a misconfiguredCOLLIE_PUBLIC_HOSTSor a device that is not on the allowlist presents the same way.I have opened #30 for that half separately, since it seemed clear cut: classify 401 and 403 in the
loaders and let the banner say it is not a connection problem.
ApiErroralready carries the status,it was simply never read.
What I am asking for
Only the routing half, because it is your call. An extra
denylistentry, a dedicated path theservice worker always passes through, or something else entirely. Changing navigation routing in an
installed PWA is easy to get wrong in a way that locks people out, so I would rather build whichever
shape you prefer than guess at it. Happy to do that once you say which.