Skip to content

Offline Push protocol

Offline Push нужен для доставки входящих звонков и сообщений, когда клиент не держит активный CommandLoop. Регистрация выполняется обычной JSON-командой поверх авторизованного CommandLoop, поэтому сервер всегда привязывает подписку к текущему client_id.

Команда:

{
  "push_subscription_update": {
    "provider": "webpush",
    "endpoint": "https://fcm.googleapis.com/fcm/send/...",
    "previous_endpoint": "",
    "p256dh": "<base64url>",
    "auth": "<base64url>",
    "permission": "granted",
    "supported": true,
    "platform": "web",
    "user_agent": "Mozilla/5.0 ...",
    "expiration_time": 0,
    "subscription": {
      "endpoint": "https://...",
      "keys": {
        "p256dh": "<base64url>",
        "auth": "<base64url>"
      }
    }
  }
}

Providers

Provider Назначение Поля
webpush Browser Push API: Chrome/Edge/Firefox/Safari Web Apps. endpoint, p256dh, auth, expiration_time, subscription.
fcm Native Android/KMP через Firebase Cloud Messaging. token.

Если provider не передан, сервер считает подписку webpush для обратной совместимости.

Browser Web Push

Web-клиент получает публичный VAPID ключ из:

GET /api/v1.0/server_info

Поля ответа:

Поле Назначение
push_enabled Включен ли Push на сервере.
push_vapid_public_key Публичный VAPID ключ в base64url.

Клиент вызывает PushManager.subscribe({ userVisibleOnly: true, applicationServerKey }), затем отправляет push_subscription_update.

Минимальный payload:

{
  "push_subscription_update": {
    "provider": "webpush",
    "endpoint": "https://updates.push.services.mozilla.com/wpush/v2/...",
    "p256dh": "<base64url>",
    "auth": "<base64url>",
    "permission": "granted",
    "supported": true,
    "platform": "web",
    "user_agent": "Mozilla/5.0 ..."
  }
}

При смене browser subscription клиент должен передать старый endpoint в previous_endpoint. Сервер удалит старую запись и сохранит новую.

Удаление подписки:

{
  "push_subscription_update": {
    "provider": "webpush",
    "endpoint": "",
    "previous_endpoint": "https://old-push-endpoint/..."
  }
}

Серверная отправка для webpush использует стандартный encrypted Web Push:

Заголовок Значение
Authorization VAPID JWT: vapid t=..., k=....
Content-Encoding aes128gcm.
TTL Значение из [Push] TTL.

Native FCM / KMP

Нативный клиент регистрирует Firebase token той же командой:

{
  "push_subscription_update": {
    "provider": "fcm",
    "token": "<firebase_registration_token>",
    "permission": "granted",
    "supported": true,
    "platform": "android"
  }
}

Рекомендации для platform:

Платформа Значение
Android KMP android
iOS KMP через Firebase ios
Desktop KMP desktop

Сервер хранит native token в push_subscriptions.token, а технический ключ записи дублирует в endpoint, чтобы сохранить один уникальный ключ (client_id, endpoint) для всех providers.

Удаление native token:

{
  "push_subscription_update": {
    "provider": "fcm",
    "token": "",
    "previous_endpoint": "<old_firebase_registration_token>"
  }
}

Текущий WebPush sender выбирает только provider=webpush. Отправка в FCM должна выполняться отдельным sender'ом через Firebase HTTP v1 API с service account, project_id и OAuth2 access token.

DB model

Таблица push_subscriptions:

Колонка Назначение
client_id Пользователь VideoGrace.
endpoint Browser endpoint или native token как storage key.
provider webpush, fcm, будущие providers.
token Native push token, например FCM registration token.
p256dh, auth Browser Web Push encryption keys.
permission granted, denied, default, unsupported.
platform Платформа клиента.
user_agent Browser user agent или диагностическая строка native-клиента.
expiration_time Browser subscription expiration time, если есть.
subscription_json Исходный JSON подписки для диагностики.
updated_at Время последней синхронизации.

Delivery events

Сервер пытается отправлять offline push, когда:

  • приходит входящий call invitation, а целевой клиент не держит активную command-сессию;
  • приходит direct message для offline получателя;
  • приходит conference message для offline участников конференции, кроме отправителя.

Push не заменяет CommandLoop и не является источником состояния. После открытия приложения клиент должен обычным способом подключиться, синхронизировать сообщения и состояние звонков.

Security

  • Клиент не должен получать приватный VAPID ключ.
  • Native FCM service account не должен попадать в клиент или в CommandLoop.
  • Push payload должен быть коротким и не должен содержать секреты. Для звонков и сообщений достаточно title/body/type/url.
  • Сервер принимает push_subscription_update только от авторизованной command-сессии и привязывает запись к текущему client_id, а не к полю из payload.