{"id":"6a39c32f86dfe812bb057a0b","title":"Web Push Vapid","path":"how-to/technical-articles/2026-02-11-ngx-ramblers-architecture/web-push-vapid","contentMarkdown":"# 23-Jun-2026 — Web Push Notifications and VAPID Keys\n\n_____\n\nA companion to [NGX Ramblers Architecture Overview](https://ngx-ramblers.org.uk/how-to/technical-articles/2026-02-11-ngx-ramblers-architecture). It explains the **Web Push (VAPID) keys** listed in the System Configuration section: what they are and why NGX stores them.\n\n## What they are for\n\nWeb 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.\n\n## Web Push, in plain terms\n\nWeb 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.\n\nThe 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.\n\n## Where VAPID comes in\n\nVAPID (Voluntary Application Server Identification) is how the application proves who it is. It is a pair of keys:\n\n- a **public key**, handed to the browser when a member subscribes to notifications\n- a **private key**, kept on the server and used to sign every push it sends\n\nThe 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.\n\n## How NGX uses them\n\n- The keys are generated once and reused. They are stored in the central config (the `inboxPush` setting: `vapidPublicKey`, `vapidPrivateKey`, `vapidSubject`), alongside the other service credentials.\n- When a committee member turns on notifications, their browser subscribes using the public key, and NGX stores that subscription (the `inboxPushSubscriptions` collection).\n- 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.\n\n## The flow at a glance\n\n**1. Subscribe** (once, when a member turns on notifications):\n\n```mermaid\nflowchart LR\n    APP1@{ icon: \"ngx:ramblers\", label: \"NGX Ramblers\", pos: \"b\", h: 48 }\n    MEMBER@{ icon: \"ngx:user\", label: \"Member's browser\", pos: \"b\", h: 48 }\n    STORE@{ icon: \"ngx:mongodb\", label: \"inboxPushSubscriptions\", pos: \"b\", h: 48 }\n    APP1 -->|\"public VAPID key\"| MEMBER\n    MEMBER -->|\"subscription: endpoint + keys\"| STORE\n```\n\n**2. Send** (each time a message arrives):\n\n```mermaid\nflowchart LR\n    MSG[\"New inbound message\"]\n    APP2@{ icon: \"ngx:ramblers\", label: \"NGX Ramblers\", pos: \"b\", h: 48 }\n    PUSHSVC[\"Browser push service<br/>Google · Mozilla · Apple\"]\n    SW[\"Service worker<br/>in member's browser\"]\n    BADGE[\"Notification + unread badge\"]\n    MSG --> APP2\n    APP2 -->|\"push signed with private key\"| PUSHSVC\n    PUSHSVC -->|\"verify against public key, deliver\"| SW\n    SW --> BADGE\n```\n\nThe inbound flow that triggers these notifications is described in the Email Architecture section of the [architecture overview](https://ngx-ramblers.org.uk/how-to/technical-articles/2026-02-11-ngx-ramblers-architecture).\n","contentHtml":"<h1>23-Jun-2026 — Web Push Notifications and VAPID Keys</h1>\n<hr>\n<p>A companion to <a href=\"https://ngx-ramblers.org.uk/how-to/technical-articles/2026-02-11-ngx-ramblers-architecture\">NGX Ramblers Architecture Overview</a>. It explains the <strong>Web Push (VAPID) keys</strong> listed in the System Configuration section: what they are and why NGX stores them.</p>\n<h2>What they are for</h2>\n<p>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.</p>\n<h2>Web Push, in plain terms</h2>\n<p>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&#39;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.</p>\n<p>The catch: a browser will not accept a push from just anyone. Each push travels through the browser maker&#39;s push service — Google&#39;s for Chrome, Mozilla&#39;s for Firefox, Apple&#39;s for Safari — and that service needs to know the push genuinely came from this application and not from a spammer.</p>\n<h2>Where VAPID comes in</h2>\n<p>VAPID (Voluntary Application Server Identification) is how the application proves who it is. It is a pair of keys:</p>\n<ul>\n<li>a <strong>public key</strong>, handed to the browser when a member subscribes to notifications</li>\n<li>a <strong>private key</strong>, kept on the server and used to sign every push it sends</li>\n</ul>\n<p>The push service checks the signature against the public key the browser registered, and only then delivers the message. A <strong>subject</strong> (a <code>mailto:</code> contact address) is included so the push service has someone to contact if there is a problem.</p>\n<h2>How NGX uses them</h2>\n<ul>\n<li>The keys are generated once and reused. They are stored in the central config (the <code>inboxPush</code> setting: <code>vapidPublicKey</code>, <code>vapidPrivateKey</code>, <code>vapidSubject</code>), alongside the other service credentials.</li>\n<li>When a committee member turns on notifications, their browser subscribes using the public key, and NGX stores that subscription (the <code>inboxPushSubscriptions</code> collection).</li>\n<li>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.</li>\n</ul>\n<h2>The flow at a glance</h2>\n<p><strong>1. Subscribe</strong> (once, when a member turns on notifications):</p>\n<pre><code class=\"language-mermaid\">flowchart LR\n    APP1@{ icon: &quot;ngx:ramblers&quot;, label: &quot;NGX Ramblers&quot;, pos: &quot;b&quot;, h: 48 }\n    MEMBER@{ icon: &quot;ngx:user&quot;, label: &quot;Member&#39;s browser&quot;, pos: &quot;b&quot;, h: 48 }\n    STORE@{ icon: &quot;ngx:mongodb&quot;, label: &quot;inboxPushSubscriptions&quot;, pos: &quot;b&quot;, h: 48 }\n    APP1 --&gt;|&quot;public VAPID key&quot;| MEMBER\n    MEMBER --&gt;|&quot;subscription: endpoint + keys&quot;| STORE\n</code></pre>\n<p><strong>2. Send</strong> (each time a message arrives):</p>\n<pre><code class=\"language-mermaid\">flowchart LR\n    MSG[&quot;New inbound message&quot;]\n    APP2@{ icon: &quot;ngx:ramblers&quot;, label: &quot;NGX Ramblers&quot;, pos: &quot;b&quot;, h: 48 }\n    PUSHSVC[&quot;Browser push service&lt;br/&gt;Google · Mozilla · Apple&quot;]\n    SW[&quot;Service worker&lt;br/&gt;in member&#39;s browser&quot;]\n    BADGE[&quot;Notification + unread badge&quot;]\n    MSG --&gt; APP2\n    APP2 --&gt;|&quot;push signed with private key&quot;| PUSHSVC\n    PUSHSVC --&gt;|&quot;verify against public key, deliver&quot;| SW\n    SW --&gt; BADGE\n</code></pre>\n<p>The inbound flow that triggers these notifications is described in the Email Architecture section of the <a href=\"https://ngx-ramblers.org.uk/how-to/technical-articles/2026-02-11-ngx-ramblers-architecture\">architecture overview</a>.</p>\n"}