A `webpush` alert channel, and the PWA it needs to arrive. The payload is encrypted to the subscription (RFC 8291) and the request signed with this installation's own keypair (RFC 8292), both over `http-ece` — `pywebpush` does the same in one call but brings `requests` and `aiohttp` with it, two HTTP stacks beside httpx on a machine that may be a Raspberry Pi. The manifest and the worker are hand-written rather than `vite-plugin-pwa`: there is nothing worth precaching when the page carrying the credential is `no-store`, so the worker handles `push` and `notificationclick` and nothing else. `registration.scope` is the app's root in both places it runs, which is why the payload carries no URL. A run finishing in error is the first event worth waking someone for; `ok` and `cancelled` describe to nothing, so a nightly batch that works stays quiet. The events were already on the bus — only the filter changed. `WEBPUSH_FILE` is a derived path, so the keypair lands on the data volume with the alerts beside it. Off it, a rebuild would silently stop every phone being notified: the key they subscribed against would be gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EbeFPm6WNC3YD9vrqqT3a
50 lines
1.3 KiB
Nginx Configuration File
50 lines
1.3 KiB
Nginx Configuration File
server {
|
|
listen 80;
|
|
|
|
# nginx's mime.types predates the manifest, so without this it goes out as
|
|
# application/octet-stream.
|
|
types {
|
|
application/manifest+json webmanifest;
|
|
}
|
|
include /etc/nginx/mime.types;
|
|
|
|
# The bundle is ~770 kB of JavaScript and ~85 kB of CSS, and nginx's base
|
|
# image ships gzip commented out — so every first load shipped all of it
|
|
# uncompressed. Roughly a third of the bytes with this on.
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_min_length 1024;
|
|
gzip_proxied any;
|
|
gzip_types
|
|
application/javascript
|
|
application/json
|
|
application/manifest+json
|
|
application/wasm
|
|
image/svg+xml
|
|
text/css
|
|
text/plain;
|
|
|
|
location / {
|
|
root /usr/share/nginx/html;
|
|
index index.html index.htm;
|
|
try_files $uri /index.html =404;
|
|
}
|
|
|
|
# Vite puts a content hash in every asset's name, so one can never change
|
|
# under its URL. Without this each of the ~50 of them cost a 304 round trip
|
|
# on every reload.
|
|
location /assets/ {
|
|
root /usr/share/nginx/html;
|
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
}
|
|
|
|
# The one file that must not be cached: it is what names the current
|
|
# assets, so a stale copy pins the browser to the previous build.
|
|
location = /index.html {
|
|
root /usr/share/nginx/html;
|
|
add_header Cache-Control "no-cache";
|
|
}
|
|
|
|
include /etc/nginx/extra-conf.d/*.conf;
|
|
}
|