From 4e78b16188a5cde0ef32f79692e0f80fefd9375b Mon Sep 17 00:00:00 2001 From: root Date: Sat, 6 Jun 2026 00:32:46 +1000 Subject: [PATCH] fix(sw): ship self-unregistering tombstone to kill stale Void 1 service worker --- CHANGELOG.md | 3 +++ package.json | 2 +- public/sw.js | 30 ++++++++++++++++++++++++++++++ server.js | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 public/sw.js diff --git a/CHANGELOG.md b/CHANGELOG.md index ea2be60..6d39f74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to Void 2.0 are documented here. Format: [Keep a Changelog](https://keepachangelog.com). +## 2.0.0-alpha.22 — Kill the stale Void 1 service worker +- **Self-unregistering `/sw.js` tombstone** (`public/sw.js`): Void 1 registered a caching service worker at this origin; it persisted across the cutover and served stale assets to returning visitors (immune to hard reload, since an active SW sits in front of the browser cache). Void 2 ships no SW, so the old one was never replaced. This tombstone is picked up by the browser's SW update check, then clears all caches, unregisters itself, and reloads open tabs — self-healing every device that ever ran Void 1. Root cause of "the Wiki still shows projects/tasks and isn't sectioned" despite the docs-mode + ordering code being correctly deployed. + ## 2.0.0-alpha.21 — Docs-kind spaces + long-page embedding - **`spaces.kind` (`'project'` | `'docs'`)** (`migration 021`): `'docs'` spaces render as a pure documentation repository — `public/views/space.js` shows only the sectioned page tree (no Projects/Tasks/"+ New"), and the sidebar expands a docs space to its top-level pages (`#/page/:id`) instead of projects. The **Wiki** is seeded to `'docs'`. Project spaces unchanged. - **Chunk + mean-pool embeddings** (`lib/ai/ollama.js` `chunkText`/`embedTextPooled`, used by the embed worker): long pages are split into ≤1500-char chunks, each embedded, then element-wise mean-pooled into one vector — replacing the old `slice(0,6000)` truncation that made dense/long docs fail with Ollama "input length exceeds context length". Single-chunk docs are unchanged. diff --git a/package.json b/package.json index 7e482a5..8b3a3d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "void-server", - "version": "2.0.0-alpha.21", + "version": "2.0.0-alpha.22", "type": "module", "private": true, "scripts": { diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..14ab4dc --- /dev/null +++ b/public/sw.js @@ -0,0 +1,30 @@ +// Self-unregistering "tombstone" service worker. +// +// Void 1 ran at this same origin (void.hynesy.com) and registered a caching +// service worker at /sw.js. Void 2 ships NO service worker, so that old worker +// would otherwise persist forever in returning visitors' browsers, intercepting +// fetches and serving stale assets that even a hard reload cannot bypass. +// +// Serving this self-destructing worker at the same /sw.js URL means the browser's +// periodic SW update check (which bypasses the HTTP cache and is not intercepted +// by the old worker) replaces the old worker with this one. On activation it +// wipes every cache, unregisters itself, and reloads open tabs — after which the +// page loads fresh from the network with no controlling service worker. +// +// Keep serving this indefinitely so any device that ever ran Void 1 self-heals. + +self.addEventListener('install', () => self.skipWaiting()); + +self.addEventListener('activate', (event) => { + event.waitUntil((async () => { + try { + const keys = await caches.keys(); + await Promise.all(keys.map((k) => caches.delete(k))); + } catch (_) { /* best effort */ } + try { await self.registration.unregister(); } catch (_) { /* best effort */ } + const clients = await self.clients.matchAll({ type: 'window' }); + for (const client of clients) { + try { client.navigate(client.url); } catch (_) { /* best effort */ } + } + })()); +}); diff --git a/server.js b/server.js index 6dda8d6..f540352 100644 --- a/server.js +++ b/server.js @@ -13,7 +13,7 @@ import { mcpAuth } from './lib/api/middleware/mcp_auth.js'; import { handleMcp } from './lib/mcp/http.js'; import httpProxy from 'http-proxy'; -const VERSION = '2.0.0-alpha.21'; +const VERSION = '2.0.0-alpha.22'; // Proxy /terminal (+ its WebSocket) to ttyd on CT 300, so the embedded terminal // works whether the Void is reached via Traefik (void2-app.hynesy.com) OR the