Self-hosted web push worker,
works on iOS.
A push endpoint you own, in one Cloudflare Worker.
- Notifications you have to act on should not be buried in a Telegram channel, a Discord server, or email.
- The other way onto a lock screen is shipping a native app whose only job is that one line.
A Worker and a KV namespace. Nothing to host, renew, or restart at 3am.
Your keys stay in your Worker, and the payload is encrypted to the device. Nobody's dashboard sees your alerts.
iOS has rendered these natively since 18.4. No App Store, no $99 a year for four words on a lock screen.
Send a push notification in seconds
Anything that can make an HTTP call can ring your phone.
Install
Standalone is its own Worker at its own address, serving the enrolment page it ships with. Mounted is three lines inside a Worker you already run. You do not have to decide first: setup asks whether you want the front end it ships with, and that answer decides most of it. Find your line anyway:
I don't run a Cloudflare Worker yet.
Standalone →I run one, and notification taps should land inside my own site.
Mounted →I run one, but push can live on its own address, away from my site.
Standalone →Standalone
One command writes the Worker, generates every key, and installs them. Storage provisions itself. Pass the answers as flags instead and it asks nothing.
Open the page in Safari, Add to Home Screen, open it from the icon. Apple's rule, once per device. The invite code, if you asked for one, is typed here.
The curl above, from anything that can reach the internet. The response counts the devices it reached.
The first question is the personal one. With the code
on, only devices you invite can enrol; with it off, anyone who reaches the
page can, and will get everything you send. Both answers are one word in
src/worker.ts afterwards, and no device re-enrols.
Mounted into a Worker you already have
handle returns null outside its prefix, so your own
routing is untouched.
The binding name is fixed; the namespace provisions on your next deploy.
Return the bundled page from a route of yours, or build your own
against /push/subscribe. Devices enrol on your hostname,
which is where a tap should land.
Prerequisites
Kukuroo supports a short list, on purpose:
- Cloudflare Workers. The only place it runs. No Docker image, no binary, no other host.
- Safari. Declarative Web Push has shipped nowhere else yet. Other browsers are turned away at the enrolment page rather than left half working.
- iOS 18.4 or later, with the page on your Home Screen and opened from the icon. A Safari tab will not do, and that is Apple's rule rather than ours. macOS Safari 18.5 receives too, if you can think of a reason to want it.
- About 50 devices per send if you are on a free-plan Worker. That is the subrequest ceiling, and about 47 more phones than one person owns.