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.