Frontmatter
| id | 8695 |
| title | Service Worker: Ensure Unmanaged Apps Handle Recovery Messages |
| state | Closed |
| labels | bugaicore |
| assignees | tobiu |
| createdAt | Jan 16, 2026, 5:52 AM |
| updatedAt | Jan 16, 2026, 6:04 AM |
| githubUrl | https://github.com/neomjs/neo/issues/8695 |
| author | tobiu |
| commentsCount | 2 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Jan 16, 2026, 5:58 AM |
Service Worker: Ensure Unmanaged Apps Handle Recovery Messages

tobiu
Jan 16, 2026, 5:57 AM
Input from Gemini 3 Pro:
✦I have centralized the Service Worker communication logic to ensure all apps, managed or unmanaged, can handle recovery messages.
Changes Implemented:
Refactored
src/worker/Manager.mjs(Core Worker Manager):
- Added a
checkServiceWorkerVersion()method that proactively queries the controlling Service Worker for its version and compares it withNeo.config.version. If a mismatch is found, it triggers a forced reload.- Updated the
construct()method to:
- Bind
navigator.serviceWorker.onmessagetoWorkerManager.onWorkerMessage. This ensures the Main Thread listens for Service Worker messages (likereloadWindow) globally, regardless of whether the app explicitly uses the Service Worker addon.- Call
checkServiceWorkerVersion()immediately if a controller is present.Cleaned up
src/main/addon/ServiceWorker.mjs:
- Removed the redundant
onmessagebinding and version check logic, as these are now handled by the coreWorkerManager.- This addon now focuses solely on registering the Service Worker for apps that opt-in.
Outcome: Any Neo.mjs app running in a browser context controlled by a Neo Service Worker (even "unmanaged" apps like SharedCovid accessed after Portal) will now:
- Proactively detect version mismatches on boot.
- Correctly receive and execute
reloadWindowcommands sent by the Service Worker (e.g., in response to 404s). This closes the "Unmanaged App" vulnerability where the SW would send recovery commands into the void.
tobiu assigned to @tobiu on Jan 16, 2026, 5:57 AM
tobiu closed this issue on Jan 16, 2026, 5:58 AM

tobiu
Jan 16, 2026, 6:04 AM
Input from Gemini 3 Pro:
✦ Final refinement and safety guards implemented.
Additional Improvements:
Infinite Loop Protection:
- Added a throttle mechanism to
checkServiceWorkerVersionusingsessionStorage.- If a reload was triggered by a version mismatch less than 5 seconds ago, the check aborts. This prevents infinite reload loops in case of persistent deployment errors (e.g., if the "new" version also believes it is mismatched or if the server is returning 404s for the new version's assets).
Architectural Documentation:
- Added a CRITICAL comment block in
src/worker/Manager.mjsexplaining the "Unmanaged App" scenario.- This documents why we bind
navigator.serviceWorker.onmessageeven whenuseServiceWorkeris false: to ensure that if a user navigates from a SW-controlled app (Portal) to a non-SW app (SharedCovid), the non-SW app can still receive and execute thereloadWindowrecovery command sent by the persisting Service Worker. This prevents future regressions where developers might try to "optimize" this binding away.The solution is now robust against race conditions, unmanaged app transitions, and persistent failure loops.
To ensure that applications which do not explicitly use the Service Worker addon (
useServiceWorker: false) can still recover from "Zombie" states (404s) when running under the control of a Service Worker installed by another app (e.g., Portal):The Problem: If App A (Portal) installs a Service Worker, and then the user navigates to App B (SharedCovid, no SW config), the SW still controls App B. If App B encounters a version mismatch (404 on assets), the SW sends a
reloadWindowcommand. However, because App B does not loadmain.addon.ServiceWorker, it has no listener onnavigator.serviceWorker.onmessage. The "Reload" command is ignored, and App B crashes with a blank page.The Solution: Move the
navigator.serviceWorker.onmessagebinding from the optionalServiceWorkeraddon to the coreWorkerManager. This ensures that every Neo.mjs application listens for Service Worker messages (likereloadWindow) if a SW is present, regardless of its own configuration.Changes:
src/worker/Manager.mjs: Bindnavigator.serviceWorker.onmessageinconstructif the API exists.src/main/addon/ServiceWorker.mjs: Remove the redundant binding.