Транспорты и каналы
VideoGrace сознательно разделяет разные классы трафика. Это упрощает отладку, уменьшает влияние больших blob-передач на media и позволяет держать разные политики reconnect.
flowchart LR
Client[Client]
subgraph Channels[Transport channels]
C[CommandLoop<br/>JSON commands]
H[HTTPS storage<br/>chunked uploads]
B[BlobChannel<br/>legacy WSM blobs]
R[WebRTC<br/>ICE/DTLS/SRTP]
M[WSMedia<br/>binary RTP/RTCP fallback]
U[UDP media<br/>native/legacy]
end
Server[Server]
Client <-->|auth, commands, state| C
Client <-->|files, images, recordings| H
Client <-->|legacy blob frames| B
Client <-->|browser media primary| R
Client <-->|browser/native fallback mux| M
Client <-->|native/legacy media| U
C --> Server
H --> Server
B --> Server
M --> Server
U --> Server
Основные каналы
| Канал | Формат | Назначение | Lifecycle |
|---|---|---|---|
CommandLoop |
JSON | Control plane: auth, contacts, conferences, devices, calls, messages. | Один основной канал клиента. |
CAN |
WSS JSON | Service control bus для recorder/tolmach/consolidator/RTC node. | Долгоживущая исходящая service-сессия к /can. |
| HTTPS storage | HTTP REST + chunked upload | Вложения чатов, изображения, аватары, будущие записи. | Независимые HTTP-запросы с ACL на чтение. |
BlobChannel |
WSM binary blob frames | Legacy blob frames и совместимость старого протокола. | Не использовать для новых файловых функций. |
| WebRTC | ICE/DTLS/SRTP + SDP/ICE signaling через CommandLoop |
Основной media path для browser-клиентов. | Один RTCPeerConnection на publish/subscribe endpoint. |
WSMedia |
WSM binary media frames | RTP/RTCP media fallback. | Initial/forced fallback, когда WebRTC/UDP недоступен при первичном подключении. |
| UDP | RTP/RTCP | Native/legacy media path. | Несколько sockets допустимы, зависит от native transport. |
HTTP storage attachments
Новые вложения чатов идут через HTTPS storage API, а не через CommandLoop, BlobChannel или media path. Это сохраняет hot path простым: управление остается в JSON-командах, media остается RTP/RTCP, а крупные файлы идут отдельными HTTP-запросами.
Поток загрузки:
- Клиент открывает upload session через
POST /api/storage/uploads. - Клиент отправляет файл чанками через
PUT /api/storage/uploads/<upload_id>. - Клиент завершает загрузку через
POST /api/storage/uploads/<upload_id>/complete. - Клиент вставляет ссылку
/api/storage/blobs/<blob_id>в markdown сообщения. Processorпри доставке сообщения привязывает blob к ACL scope:direct:<min_id>:<max_id>илиconference:<tag>.
Чтение идет через GET /api/storage/blobs/<blob_id>. Endpoint поддерживает Range, отдает исходное имя файла через Content-Disposition и проверяет доступ по bearer token или cookie t. Токен доступа не должен добавляться в query string: web-клиент для приватных вложений делает fetch с Authorization: Bearer ... и передает UI локальный blob: URL.
Основные состояния blob:
| Scope/state | Назначение |
|---|---|
upload |
Объект загружен, но еще не привязан к сообщению. |
direct:<a>:<b> |
Вложение личного диалога двух пользователей. |
conference:<tag> |
Вложение чата конференции. |
visibility != 0 |
Публичное чтение, например для будущих публичных каналов. |
state = 0 |
Незавершенная upload session. |
state = 1 |
Готовый объект. |
Схема storage DB создается только в Processor::EnsureStorageDatabase(). BlobManager не должен создавать таблицы storage и остается обработчиком legacy WSM blob-команд.
Storage GC запускается при старте сервера и удаляет незавершенные upload sessions и готовые, но не привязанные к сообщению объекты старше настроенных TTL. Будущие backend-реализации S3/CAN должны сохранять те же семантики ACL, Range и GC.
UI-контракт для web-клиента:
- markdown содержит чистую ссылку
/api/storage/blobs/<blob_id>без токена; - изображения, видео и аудио приватных вложений сначала загружаются авторизованным
fetch; - для рендера используется локальный
blob:URL, который не пригоден вне текущей вкладки; - изображения должны открываться в fullscreen preview с режимами
ОригиналиВписать; - для обычного скачивания клиент также использует bearer-запрос и сохраняет исходное имя файла.
Browser media rule
В браузере WebRTC — основной media path. Клиент сначала запрашивает список RTC routes через webrtc_routes_request, выбирает rtc_node_id, создает endpoint и отправляет webrtc_offer через CommandLoop. Если WebRTC не подключился при первичном создании endpoint'а, режим auto может перевести конкретный endpoint на WSMedia. Если WebRTC уже был поднят и затем media застыла после stale/reconnect, клиент делает WebRTC restart/backoff и не деградирует в WSM.
flowchart TB
Browser[Browser web-client]
Control[CommandLoop<br/>WebRTC signaling]
PC[RTCPeerConnection<br/>ICE/DTLS/SRTP]
RTC[RTC node / WebRTC Gateway]
Mux[MediaMuxSocket<br/>WSMedia fallback]
Mic[Local mic SSRC]
Cam[Local cam SSRC]
Screen[Local screen SSRC]
RA[Remote audio SSRCs]
RV[Remote video SSRCs]
Browser --> Control
Control --> RTC
Mic --> PC
Cam --> PC
Screen --> PC
PC --> RTC
RTC --> RA
RTC --> RV
Browser -.fallback.-> Mux
Для WSMedia fallback сохраняется старый mux-инвариант: одна WSS media session на access token/conference, много ssrc, не одна сессия на каждое устройство.
Native media rule
Для новых native/KMP клиентов целевой media contract — WebRTC как основной путь, потому что он закрывает NAT traversal, SRTP и мобильную совместимость. Legacy native UDP остается совместимым path для старых клиентов и сервисов. WSS нужен как fallback для сетей, где UDP/WebRTC недоступен или нестабилен. Fallback должен использовать тот же mux-инвариант: одна WSS media session, много ssrc.
CAN rule
CAN отделен от пользовательского CommandLoop. Сервисные worker'ы подключаются к wss://<server>/can, авторизуются service token'ом и получают задания от сервера. Media и файлы через CAN не передаются: recorder и другие worker'ы используют CAN только для управления, а записи/артефакты загружают через HTTPS storage API.