23-Jun-2026 — Web Push Notifications and VAPID Keys
A companion to NGX Ramblers Architecture Overview. It explains the Web Push (VAPID) keys listed in the System Configuration section: what they are and why NGX stores them.
What they are for
Web Push (VAPID) keys are the credential that lets NGX send a browser notification to a committee member when a new message arrives in a shared inbox.
Web Push, in plain terms
Web Push is the browser standard behind notifications that appear even when the site is not open in a tab. NGX registers a small background script (a service worker) in each committee member's browser. When a new inbound message is stored, the server sends a push, and that script shows the notification and updates the unread badge.
The catch: a browser will not accept a push from just anyone. Each push travels through the browser maker's push service — Google's for Chrome, Mozilla's for Firefox, Apple's for Safari — and that service needs to know the push genuinely came from this application and not from a spammer.
Where VAPID comes in
VAPID (Voluntary Application Server Identification) is how the application proves who it is. It is a pair of keys:
- a public key, handed to the browser when a member subscribes to notifications
- a private key, kept on the server and used to sign every push it sends
The push service checks the signature against the public key the browser registered, and only then delivers the message. A subject (a mailto: contact address) is included so the push service has someone to contact if there is a problem.
How NGX uses them
- The keys are generated once and reused. They are stored in the central config (the
inboxPushsetting:vapidPublicKey,vapidPrivateKey,vapidSubject), alongside the other service credentials. - When a committee member turns on notifications, their browser subscribes using the public key, and NGX stores that subscription (the
inboxPushSubscriptionscollection). - When an inbound message is assigned to a role, NGX signs a push with the private key and sends it to each subscribed member of that role. The same signal drives the live unread badge in the admin UI.
The flow at a glance
1. Subscribe (once, when a member turns on notifications):
flowchart LR
APP1@{ icon: "ngx:ramblers", label: "NGX Ramblers", pos: "b", h: 48 }
MEMBER@{ icon: "ngx:user", label: "Member's browser", pos: "b", h: 48 }
STORE@{ icon: "ngx:mongodb", label: "inboxPushSubscriptions", pos: "b", h: 48 }
APP1 -->|"public VAPID key"| MEMBER
MEMBER -->|"subscription: endpoint + keys"| STORE
2. Send (each time a message arrives):
flowchart LR
MSG["New inbound message"]
APP2@{ icon: "ngx:ramblers", label: "NGX Ramblers", pos: "b", h: 48 }
PUSHSVC["Browser push service<br/>Google · Mozilla · Apple"]
SW["Service worker<br/>in member's browser"]
BADGE["Notification + unread badge"]
MSG --> APP2
APP2 -->|"push signed with private key"| PUSHSVC
PUSHSVC -->|"verify against public key, deliver"| SW
SW --> BADGE
The inbound flow that triggers these notifications is described in the Email Architecture section of the architecture overview.