rastrillo / aviso Public
Making a rastrillo app installable
Aviso does not own any of this, on purpose: a manifest is the app's identity, and rastrillo's scaffold is not changed by the addon. Android and desktop browsers deliver push to an ordinary website. iOS and iPadOS (from 16.4) deliver it only to an app added to the Home Screen, and only from that installed copy — so an app that wants push on iPhones wants this recipe too. It is four things.
1. A manifest
Serve manifest.webmanifest (content type application/manifest+json) with, at least:
{
"id": "/",
"name": "Birthday Alarm",
"short_name": "Birthdays",
"start_url": "/",
"scope": "/",
"display": "standalone",
"theme_color": "#5b6cff",
"background_color": "#f6f7fb",
"icons": [
{ "src": "/static/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/static/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}id is what keeps an installed app the same app across a renamed start_url; set it once and never change it. A twenty-line handler that writes this from the app's name is enough — Eleven's serveManifest is the model — and a static file is enough too.
2. Three tags in the layout's head
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#5b6cff">
<link rel="apple-touch-icon" href="/static/icon-180.png">Safari reads the Apple touch icon, not the manifest's icons, for the Home Screen tile; 180 px is the size it wants.
3. The worker at its scope
Serve sw.js at the path whose scope it should control — /sw.js for the whole app — with Cache-Control: no-cache, so a new worker is noticed on the next load rather than after a cache expiry nobody chose. Load the helper from inside it:
importScripts("/static/aviso/aviso-sw.js");The rest of the worker is in SKILL.md.
4. Coaching, keyed on capabilities()
capabilities() from push.mjs reports standalone: whether this page is running as an installed app. On iOS and iPadOS, when it is not, show the person how to add the app to their Home Screen (Share → Add to Home Screen) and to sign in inside the installed copy before pressing the enable button — Safari's cookies do not travel into the installed app. Do not gate Android or desktop on installation; they do not need it. Show the enable button only when capabilities().push is true, and only once navigator.serviceWorker.ready has resolved.
That is the whole recipe. None of it is Web Push; all of it is what Web Push needs on an iPhone, and a good idea everywhere else.