Skip to content

Транспорты и каналы

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-запросами.

Поток загрузки:

  1. Клиент открывает upload session через POST /api/storage/uploads.
  2. Клиент отправляет файл чанками через PUT /api/storage/uploads/<upload_id>.
  3. Клиент завершает загрузку через POST /api/storage/uploads/<upload_id>/complete.
  4. Клиент вставляет ссылку /api/storage/blobs/<blob_id> в markdown сообщения.
  5. 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.