DEV Community

Ekong Ikpe
Ekong Ikpe

Posted on

PWAs Cache Resources. Spirit Installs an Application.

PWA reimagined

Before we go into this post I earlier planned for a different topic however in the course of my vibe experience something else came up.

There is a question nobody asks when they set up a service worker:

Who owns the application lifecycle?

With a conventional PWA, the browser primarily orchestrates installation, caching, and the service worker lifecycle. The application participates in those decisions, but the browser ultimately controls when service workers are activated, how storage is managed, and what "installed" means on a given platform.

Spirit gives a different answer. The application owns installation, storage, and updates. The browser simply provides the runtime.

That is not a small distinction. It starts with one reframe:

spirit-sw.js is firmware, not middleware.

Every conventional PWA uses the service worker as a proxy — it intercepts requests and decides whether to serve from cache or go to the network. Spirit uses the service worker differently. It never serves your application files. It never touches Cache Storage. It serves a single hardcoded bootstrap string that knows how to resurrect the application from IndexedDB. The service worker is a bootloader. It boots the same way every time.

Everything else follows from that.


What Spirit is

Spirit is an IDB-native installation and boot system. Four moving parts:

spirit-grave.js — the storage layer. Pure IndexedDB, no DOM, no network. Buries a file as a Blob, exhumes it later. Nothing else.

spirit-reg.js — the installer. Runs once, on the first real online visit. Reads spirit-manifest.json, fetches every listed file, buries each one in IndexedDB under gnoke:spirit/files.

spirit-sw.js — the bootloader. Intercepts every navigation request and responds immediately with a hardcoded HTML string baked into the worker itself. That string contains an inline IDB reader — no separate fetch. It reconstructs the application from the grave: CSS injected as <style>, images converted to blob URLs, JS executed as classic scripts in manifest order. Zero network requests on boot.

spirit-revive.js — the updater. Exposes Spirit.reviveFromNetwork(). Call it from a button inside the running application. It re-fetches the manifest, re-buries changed files, and reloads only if something actually changed. Nothing runs in the background automatically.


The lifecycle comparison

With a conventional PWA:

Browser requests URL
→ SW intercepts, checks cache
→ serves cached or fetches fresh
→ background SW update check runs independently
→ new SW waits for all tabs to close (or skipWaiting forces it)
→ user may receive updated code mid-session without requesting it
Enter fullscreen mode Exit fullscreen mode

With Spirit:

Browser requests URL
→ SW responds immediately with hardcoded bootstrap
→ bootstrap opens IndexedDB, reconstructs app from buried files
→ app runs — no network involved
→ update only happens when Spirit.reviveFromNetwork() is called
→ app controls when to prompt, when to reload
Enter fullscreen mode Exit fullscreen mode

The update is part of the application's lifecycle, not the browser's.


What this makes possible

Given a valid installation, startup is deterministic. The same boot sequence runs every time — online or offline, first launch or hundredth. There is no cache miss, no conditional fetch, no race between a new service worker and open tabs.

Spirit is designed so updates replace the installed application as a single managed operation, avoiding the mixed-version states that can occur when independently cached assets drift. One call, one operation, one reload decision.

The bootloader is stable. spirit-sw.js is the only file that triggers the browser's own SW update cycle — and it is designed to change rarely, like actual firmware. The application underneath it can update as often as it needs to without touching the bootloader.


Where this matters and where it does not

Spirit is not a general improvement over Cache Storage. For a news site, a marketing page, a content application — the browser's built-in model is exactly right. HTTP caching, ETags, Cache Storage, and the SW update cycle were designed for that world and they work well in it.

Spirit makes sense for a different category: software that happens to be delivered via the web.

  • Offline-first tools where network access is intermittent or absent
  • Browser OS environments where the app manages its own disk
  • Industrial or embedded browser runtimes where update timing must be controlled
  • Editors and development tools that need to own their own boot sequence

Spirit was built for GnokeStation — a browser-native OS where IndexedDB is the disk, a SharedWorker is the kernel, and tabs are processes. In that context, the conventional PWA model has a fundamental mismatch. Spirit fits the architecture instead of fighting it.


The honest trade-offs

What you give up:

  • Browser DevTools understand Cache Storage natively. An IDB-backed virtual filesystem is more opaque to debug.
  • HTTP-level caching is bypassed entirely. Spirit's current change detection compares blob sizes — a content hash would be more robust and is worth adding.
  • reviveFromNetwork() buries files sequentially. A tab closed mid-update leaves a partially updated grave. A staged swap — write new files first, then commit — would make updates truly atomic.
  • The first visit still requires a real online session. Unavoidable.
  • navigator.storage.persist() lowers eviction risk but does not eliminate it.

What you gain:

  • The web delivers the application once. After that it runs from local storage entirely.
  • No dependency on Cache Storage eviction policies.
  • The application decides when it updates and what the user sees when it does.
  • The bootloader is stable across application versions.

The deeper idea

Spirit doesn't try to replace the PWA model. It explores a different one: treating the browser as a runtime capable of hosting installed software, rather than a document viewer that happens to cache files.

The web is used once to install the application. From then on, the browser hosts a locally managed runtime whose lifecycle is controlled by the application — not by HTTP caching semantics, not by the SW activation queue, not by storage eviction policy.

Whether that trade-off is right depends entirely on the kind of application you are building.

Spirit is built for the second kind.

And for the records my semantics for file names are my way of expressing myself, what matters to me is that the aim is achieved not the name of the file 🤓


Spirit is part of GnokeStation v2 — a browser-native OS built on SharedWorker as kernel, IndexedDB as disk, and tabs as processes. Built by edmundsparrow.

Top comments (0)