bitwarden/server
Push & SignalR
Active contributors: platform team.
Purpose
Bitwarden clients are kept in sync with the server via push notifications. The server notifies a user's connected devices when their vault changes (cipher created/updated/deleted, folder change, organization update, login request). The push pipeline is multi-provider:
- SignalR — the Notifications host fans out push messages to currently-connected clients (web vault, browser extension, desktop, CLI).
- Azure Notification Hub — the API talks directly to ANH for mobile (iOS APNs, Android FCM/HMS) push.
- Relay mode — self-hosted instances forward push to Bitwarden's cloud Notification Hub via the relay so customer mobile apps still receive native notifications.
Directory layout
src/Core/Platform/Push/
├── IPushNotificationService.cs # Domain-facing interface
├── PushType.cs # Every push event the server can emit
├── NotificationsApiPushNotificationService.cs # Calls the Notifications host (SignalR)
├── NotificationHubPushNotificationService.cs # Calls Azure Notification Hub (mobile)
├── RelayPushNotificationService.cs # Self-host: forwards to upstream cloud relay
├── MultiServicePushNotificationService.cs # Composite that calls multiple providers
├── PushTokenService.cs # Manages installation + device push tokens
└── ...
src/Core/Platform/PushRegistration/
├── IPushRegistrationService.cs # Register/unregister device tokens
├── NotificationHubPushRegistrationService.cs
└── ...
src/Notifications/
├── NotificationsHub.cs / AnonymousNotificationsHub.cs / HubHelpers.cs
└── (see apps/notifications.md)Key abstractions
| Type | Path | Description |
|---|---|---|
IPushNotificationService |
src/Core/Platform/Push/IPushNotificationService.cs |
Emit-side interface: PushSyncCipherUpdate, PushSyncFolderCreate, PushAuthRequestResponse, ... |
PushType |
src/Core/Platform/Push/PushType.cs |
The enum of events; intentionally outside CODEOWNERS so any team can extend it. |
IPushRegistrationService |
src/Core/Platform/PushRegistration/IPushRegistrationService.cs |
Register / unregister a device's push tokens (APNs / FCM tags). |
MultiServicePushNotificationService |
src/Core/Platform/Push/MultiServicePushNotificationService.cs |
Composite that fans a single emit out to SignalR + ANH + Relay as needed. |
RelayPushNotificationService |
src/Core/Platform/Push/RelayPushNotificationService.cs |
Self-host bridge: HTTPs POST to Bitwarden's cloud relay, signed with the installation key. |
NotificationsHub |
src/Notifications/NotificationsHub.cs |
The SignalR endpoint clients connect to. |
HubHelpers |
src/Notifications/HubHelpers.cs |
13KB router that maps PushType → SignalR group sends. |
How it works
graph LR
Api["src/Core/<Domain>/...\n(IPushNotificationService.PushSyncCipherUpdate)"] --> Multi["MultiServicePushNotificationService"]
Multi --> Sig["NotificationsApiPushNotificationService"]
Multi --> ANH["NotificationHubPushNotificationService"]
Multi --> Relay["RelayPushNotificationService\n(self-host only)"]
Sig --> NoHost["Notifications host\n(SignalR /hub)"]
ANH --> Cloud["Azure Notification Hub"]
Relay --> Upstream["Bitwarden cloud relay"]
NoHost --> WebClient["Web vault / Browser ext\n/ Desktop / CLI"]
Cloud --> Mobile["iOS / Android apps"]
Upstream --> CloudWhen the API updates a cipher, it calls IPushNotificationService.PushSyncCipherUpdate(cipher, collectionIds). The MultiServicePushNotificationService decides which downstream services to call based on globalSettings.Notifications and globalSettings.PushHub:
- If a Notifications URL is configured → POST to it; the Notifications host enqueues into
HubHelperswhich callsClients.Group(userId).SendAsync(...). - If
globalSettings.PushHub.HubNameis configured → call ANH with tags so iOS/Android receive a native push. - If
globalSettings.Installation.Idindicates self-host → call the relay.
Push registration
Mobile devices register their APNs/FCM token via Api.DevicesController (src/Api/Controllers/DevicesController.cs). The IPushRegistrationService writes the registration to ANH (for cloud) or the local registration table.
When a user signs out or removes a device, the same registration is deleted to avoid stale push targets.
Why three providers?
- SignalR is great for currently-open clients but doesn't reach mobile devices that are backgrounded.
- ANH handles native push but requires Apple / Google credentials that customers can't reasonably manage themselves — hence the relay.
- Relay mode lets self-hosted customers pay nothing extra for mobile push while keeping all their data on-prem.
Entry points for modification
- New push event → add value to
PushType, extendIPushNotificationServiceand every implementation, route inHubHelpers. - New push provider → implement
IPushNotificationService, register throughMultiServicePushNotificationService. - Change registration tags → edit
NotificationHubPushRegistrationService.GetTagsForRegistration.
For the public connection contract, see apps/notifications.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.