Push Notifications

Overview

Cloudillo implements Web Push notifications using the VAPID (Voluntary Application Server Identification) protocol. Push notifications are sent when users receive actions while offline or not connected via WebSocket.

Web Push Standards

The implementation follows these RFCs:

RFC Title Purpose
RFC 8292 VAPID for Web Push Server identification
RFC 8188 Encrypted Content-Encoding for HTTP Payload encryption
RFC 8291 Message Encryption for Web Push End-to-end encryption

VAPID Keys

Each tenant has a P-256 VAPID key pair for authenticating with push services. The private key is stored in the database; the public key is shared with clients. Keys are generated automatically the first time the public key is requested. The server signs a VAPID JWT (ES256, 12-hour expiry, subject mailto:admin@<id_tag>) per push request.

Subscription Flow

sequenceDiagram
    participant C as Client
    participant SW as Service Worker
    participant S as Cloudillo Server
    participant PS as Push Service

    C->>S: GET /api/auth/vapid
    S-->>C: {vapidPublicKey: "BM5..."}

    C->>SW: pushManager.subscribe({userVisibleOnly: true, applicationServerKey})
    SW->>PS: Subscribe request
    PS-->>SW: PushSubscription
    SW-->>C: PushSubscription

    C->>S: POST /api/notifications/subscription {subscription}
    S-->>C: {id: 12345}

Push Delivery

When an action is received for an offline user:

sequenceDiagram
    participant A as Action Sender
    participant S as Cloudillo Server
    participant PS as Push Service
    participant B as User's Browser

    A->>S: POST /api/inbox {action}
    S->>S: Process action
    S->>S: Check if recipient is online

    alt User is online (WebSocket connected)
        S->>B: WebSocket message
    else User is offline
        S->>S: Load push subscriptions
        S->>S: Encrypt payload with user's public key
        S->>PS: POST {encrypted payload}
        PS->>B: Push notification
        B->>B: Display notification
    end

Notification Types

notify.push is the master switch. Per-event toggles let users choose which notifications they receive:

Setting Default Event
notify.push.message true Direct messages (MSG)
notify.push.connection true Connection requests (CONN)
notify.push.file_share true File shares (FSHR)
notify.push.comment true Comments on your posts (CMNT)
notify.push.mention true Mentions in posts
notify.push.follow false New followers (FLLW)
notify.push.reaction false Reactions to your posts (REACT)
notify.push.post false Posts from people you follow (POST)

An equivalent notify.email.* set controls email notifications (master switch notify.email is off by default). Settings are tenant-scoped and checked before sending.

Encryption

Push payloads are encrypted with the browser subscription’s P-256 public key (p256dh) and auth secret using the ece (Encrypted Content-Encoding, aes128gcm) scheme. The push service relays the ciphertext but cannot read it. The encrypted body already embeds the salt and server public key in its header, and is POSTed with Content-Encoding: aes128gcm and TTL: 86400.

Payload Structure

The encrypted payload is the NotificationPayload serialized as JSON:

{
  "title": "Alice",
  "body": "New message from Alice",
  "path": "/messages/alice@example.com"
}
Field Description
title Notification title
body Notification body text
path URL path to open when clicked (optional)
image Image URL (optional)
tag Tag for grouping notifications (optional)

Subscription Management

Users can have multiple push subscriptions (one per device/browser). Each subscription has a unique ID, and all active subscriptions receive notifications.

Push subscriptions can expire when the browser reports expiration, the push service responds with HTTP 404/410, or the user manually unsubscribes. Invalid subscriptions are removed automatically.

Error Handling

The push result is classified by the push service’s HTTP status:

Push Service Response Result Action
2xx Success Counted as delivered
404, 410 Gone Subscription gone Delete subscription
Other 4xx (incl. 429) Permanent error Log, don’t retry
5xx / network error Temporary error Log, can retry

See Also