Skip to content

feat: Web Push — VAPID keys, subscription store, send loop, and browser package (#162) - #163

Merged
swimmesberger merged 1 commit into
mainfrom
claude/elarion-issue-162-bbb2bc
Sep 25, 2026
Merged

swimmesberger merged 1 commit into
mainfrom
claude/elarion-issue-162-bbb2bc

Conversation

@swimmesberger

Copy link
Copy Markdown
Owner

Summary

Closes #162. Adds Web Push (RFC 8030 / 8291 / 8292) so apps can notify users whose app is closed — the half that client events (ADR-0043) cannot cover. Recipients, triggers, and text stay in the app; keys, store, delivery with cleanup, and the browser/service-worker plumbing move into the framework (ADR-0076).

  • Elarion.WebPush — IWebPushSender (SendToUsersAsync / SendToCurrentUserAsync, WebPushMessage with title/body/url/tag/urgency/TTL). aes128gcm encryption and ES256 VAPID signing on the .NET crypto primitives (AOT-compatible, no third-party push SDK), bounded-concurrency fan-out, deletion of 404/410 and malformed subscriptions, transient failures kept. VAPID keys: configuration → store → generate (race-safe across nodes). WebPushSubscriptionService binds subscriptions to ICurrentUser (upsert by endpoint, reassigning a device that re-subscribes under another account). In-memory stores by default.
  • Elarion.WebPush.EntityFrameworkCore — elarion_push_subscriptions / elarion_vapid_keys via [GenerateElarionWebPush] (new ELWP001) or modelBuilder.UseElarionWebPush(); PostgreSQL ON CONFLICT upsert / first-insert-wins.
  • Elarion.WebPush.AspNetCore — MapElarionWebPush(): GET public-key, POST subscribe (subscription.toJSON()), POST unsubscribe; AOT-safe RequestDelegate registrations.
  • @swimmesberger/elarion-webpush (npm) — pushAvailability() (available / install-first for iOS Safari outside the Home Screen / unsupported), gesture-safe enablePush, subscribe / unsubscribe / isSubscribed / refreshOnStart over a pluggable WebPushServerApi (fetchWebPushApi() or an adapter over the generated JSON-RPC client), and a /sw module for push, notificationclick, pushsubscriptionchange.
  • Docs: new capabilities/web-push page, ADR-0076, package/attribute/diagnostic references, CHANGELOG, README, elarion skill.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Refactor / internal

Checklist

  • dotnet build Elarion.slnx -c Release passes (warnings are errors).
  • dotnet test passes; new/changed behavior is covered by tests. (2139/2139, PostgreSQL integration tests ran via Testcontainers.)
  • Source-generation changes include generator tests under tests/Elarion.Tests/Generators.
  • New analyzer diagnostics are recorded in AnalyzerReleases.Unshipped.md.
  • Documentation under docs/ is updated for any user-visible change. (website build + check:links clean.)
  • CHANGELOG.md is updated under Unreleased.
  • The change stays domain-neutral and respects the package architecture boundaries.

Notes for reviewers

  • Endpoint allow-list is fail-closed. The server POSTs to a browser-supplied URL, so by default only https endpoints on FCM / Mozilla / Apple / WNS (and subdomains) are accepted and redirects are not followed. Widen via AllowedEndpointHosts or AllowAnyEndpointHost.
  • Crypto verification: encryption is pinned byte-for-byte to the RFC 8291 Appendix A vector; VAPID JWTs are verified for signature, claims, and raw r‖s format. Not tested against a live push service.
  • Windows CNG reports an off-curve P-256 point as PlatformNotSupportedException (OpenSSL: CryptographicException); P256.TryImportPublicKey handles both.
  • WebPushMessage uses nominal required/init properties (repo DTO style) instead of the positional record sketched in the issue. Tag doubles as the RFC 8030 Topic (hashed when > 32 URL-safe chars).
  • No framework-shipped [Handler]s: apps either use MapElarionWebPush() or write three thin handlers delegating to WebPushSubscriptionService (documented).
  • Workflows: src/elarion-webpush added to the CI and publish npm matrices; per repo rule all touched action pins were bumped to latest (checkout v7.0.1, setup-dotnet v6.0.0, setup-node v7.0.0 in publish.yml).
  • Before first publish: configure an npm trusted publisher for @swimmesberger/elarion-webpush on npmjs.com; until then that matrix leg fails (others unaffected, fail-fast: false).
  • Follow-up (not in scope): a "notify this user" entry point choosing client events vs. Web Push by live subscriber — noted in ADR-0076.

…er package (#162)

Adds Elarion.WebPush (IWebPushSender, WebPushSubscriptionService, VAPID key
provider, store seams with in-memory defaults; RFC 8291/8292 on the .NET
crypto primitives), Elarion.WebPush.EntityFrameworkCore ([GenerateElarionWebPush],
ELWP001), Elarion.WebPush.AspNetCore (MapElarionWebPush), and the
@swimmesberger/elarion-webpush npm package (availability detection incl. iOS
Home Screen, gesture-safe enablePush, subscribe/refresh helpers, service-worker
module). Endpoints are restricted to known push services by default (SSRF guard).
Documented in ADR-0076 and the Web Push capability page.
@swimmesberger
swimmesberger merged commit 55f49ff into main Sep 25, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No Web Push support: VAPID keys, subscription store and send loop are rebuilt in every app

1 participant