Open Think Deploy →

Two channels, one skill (`notify-user`). Email path uses the same `send_email` binding as the email plugin; Web Push path is a from-scratch implementation in `src/webpush/` covering ECDSA P-256 VAPID JWT signing, ECDH-ES + HKDF + AES-128-GCM payload encryption, and 410/404-driven auto-pruning of dead subscriptions. Browsers fetch the VAPID public key unauthenticated from `/webpush/public-key`; subscriptions land in D1 `push_subscriptions` after Access-gated POST to `/webpush/subscribe`. Every notification is audit-logged in D1 `notifications`.

Install

1 · Config

Append to ENABLED_PLUGINS in wrangler.toml:

notifier

2 · Secrets

Run each from your Worker project:

  • wrangler secret put VAPID_PUBLIC_KEY optional — Web Push public key (uncompressed P-256, base64url). Generate with `npm run vapid:generate`.
  • wrangler secret put VAPID_PRIVATE_KEY optional — Web Push private scalar (32 bytes, base64url). Keep as a Worker secret.
  • wrangler secret put VAPID_SUBJECT optional — VAPID `sub` claim — `mailto:you@example.com` or an https URL.
Copy-paste .dev.vars template
VAPID_PUBLIC_KEY=# optional — Web Push public key (uncompressed P-256, base64url). Generate with `npm run vapid:generate`.
VAPID_PRIVATE_KEY=# optional — Web Push private scalar (32 bytes, base64url). Keep as a Worker secret.
VAPID_SUBJECT=# optional — VAPID `sub` claim — `mailto:you@example.com` or an https URL.

3 · Steps

  1. Decide your channels: email-only is fine without VAPID. For Web Push you need all three VAPID secrets.
  2. npm run vapid:generate — copy the two values, then `wrangler secret put VAPID_PUBLIC_KEY` and VAPID_PRIVATE_KEY
  3. wrangler secret put VAPID_SUBJECT ← e.g. mailto:you@example.com
  4. Browser side: GET /webpush/public-key, then PushManager.subscribe(...), then POST /webpush/subscribe
  5. Test: curl -X POST https://<worker>/webpush/test -H 'cf-access-jwt-assertion: <token>'

Tags

pushvapidweb-pushemailrfc-8291rfc-8292