---
name: rastrillo-pwa
description: Add installation, a public offline fallback and service-worker update handling to a Rastrillo web app, with optional aviso Web Push composition.
---

# Rastrillo PWA

Module and source: `amadan.net/rastrillo/pwa`. Read this skill from the
version the app imports. This kit adds installability and an offline
fallback, not offline data or writes. For the latter, read
[docs/offline.md](docs/offline.md) before choosing an app data model.

Use `examples/basic/main.go` as the complete wiring reference. It is a
separate Go module, so read it from the repository, not a module-cache zip.

- Create `pwa.Manifest` with stable `ID`, `Name`, `StartURL`, `Scope` and
  icons. Paths are root-relative, scope ends in `/`, and start URL stays
  within scope. Call `.Handler()` at boot and check the error. Supply actual
  192x192 and 512x512 PNGs, plus a 180px Apple touch icon. Branding is app-owned.
- Mount the manifest at `/manifest.webmanifest`. Mount `pwa.Assets()` using
  `http.StripPrefix("/pwa", ...)` at `/pwa/`. Assets have JavaScript MIME
  types and `no-cache`; add manifest, theme-colour and Apple icon head tags.
- Serve app-owned `/sw.js` at its intended scope with JavaScript MIME and
  `Cache-Control: no-cache`. It imports `/pwa/worker.js` and calls
  `RastrilloPWA.install()` once. For `/app/` scope, use `/app/sw.js` and
  matching manifest scope/start paths; asset helpers may live elsewhere.
  Serve the manifest and worker assets without authentication redirects.
- Import `register` from `/pwa/client.mjs`. It returns a registration or
  `null` when unsupported. It reports waiting updates through `onUpdate`;
  it never reloads or asks for permission. Without `clients.claim`, the
  first page becomes controlled on its next navigation. Wait for
  `navigator.serviceWorker.ready` before passing the registration to push.

Update activation is app policy. Prefer asking people to save and close all
tabs. `activateUpdate(registration)` explicitly activates a waiting worker
and affects every tab in its scope. Call only after resolving unsaved work
across them. The app owns any `controllerchange` reload.

The worker intercepts only in-scope GET navigations. Network failures get a
503 public offline page; 401/403/500 responses remain unchanged. API calls
and writes stay on the network. No Cache Storage or IndexedDB writes.
`install({offlineHTML})` accepts a trusted, public, self-contained document
for custom copy/translations. Its CSP permits inline style, no scripts or
external assets. Never interpolate a signed-in person's data into it.

For push, load the pinned `amadan.net/rastrillo/aviso` skill and compose its
worker handlers into the same `/sw.js`. Keep one registration per scope;
do not register a second push worker over it. Aviso owns subscriptions and
transport. The app owns notification policy, payloads and encryption.
Push permission must be requested from a user gesture. On iOS, guide the
person to install and sign in inside the Home Screen copy first.

Validate with `make ci` and the app's own gate. The module gate includes its
nested example and Chromium/WebKit worker tests; physical-device install
and push checks are separate. There is no native adapter in this module.
