Browser notifications travel through a push service that the browser picks:
Chrome uses Google's, Firefox uses Mozilla's, Safari uses Apple's. The ungoogled-chromium
engine TronBrowser runs on has none, so on a stock build every site's
pushManager.subscribe() fails with
Registration failed - push service error.
TronBrowser brings its own. The built-in extension supplies pushManager to
every page and registers with https://tronbrowser.dev/api/1/push by default.
Change it under Settings → Push notifications: another compatible
service, or off.
Your site subscribes the usual way and gets an endpoint on our service. Your server sends
exactly what it sends to FCM or Mozilla: an aes128gcm body (RFC 8291) with a
VAPID header (RFC 8292), via web-push, @profullstack/notifications
or anything else that speaks the standard. We answer 201, and 404 /
410 when a subscription is gone, so your cleanup keeps working.
One difference: the notification is shown by TronBrowser from the payload's
title, body, icon and url (also
notification.* and click_action). Your service worker's
push handler does not run, so a silent push that only syncs data shows as a
plain notification.
| Request | Who | What |
|---|---|---|
GET / | settings page | {"service":"tronbrowser-push","version":1}, which is how the browser checks a custom URL |
POST /{token} | site servers | RFC 8030 push. TTL required, body ≤ 4096 bytes, Topic replaces a pending message |
POST /subscriptions | browser | {origin, applicationServerKey} → {token, endpoint} |
DELETE /subscriptions/{token} | browser | unsubscribe; later pushes get 410 |
GET /messages, POST /ack | browser | pending messages, then {ids} to acknowledge |
GET /connect | browser | WebSocket. Send {"type":"hello","secret"}; receive {"type":"messages"}; reply {"type":"ack","ids"} |
Browser calls carry Authorization: Bearer <device secret>.
The source is in services/api/src/push.