Skip to content

Nihil: сетевой протокол (compositor-agnostic)

Nihil: сетевой протокол (compositor-agnostic)

Section titled “Nihil: сетевой протокол (compositor-agnostic)”

Спека протокола «присутствие + общие окна». Фиксируется до кода (этап N0). Nihil, Minecraft-мод и любой другой клиент общаются через этот протокол как равноправные пиры.

Статус: черновик v0 (транспорт и схема решены по исследованию, wire-формат — в работе).

Один QUIC-коннект (quinn, Rust) между пирами:

  • Stream 0 (reliable, ordered) — handshake: Hello { protocol_versions: [u16], capabilities: {...} } в обе стороны. Используется min(common_version) и пересечение capabilities (как WebRTC SDP offer/answer).
  • Дополнительные streams (reliable) — join/leave, чат, описания surface, права.
  • Datagrams (unreliable) — presence: позы на 20–30 Гц. Каждая датаграмма несёт sequence number; получатель выкидывает устаревшие (snapshot interpolation).

QUIC выбран за: TLS 1.3 из коробки, один канал для reliable+unreliable, connection migration, quinn зрелый (350M загрузок, активен). Антипаттерны: TCP/WebSocket для поз (head-of-line blocking), свой UDP-reliability (переизобретение QUIC).

Схема сообщений (решено)

Section titled “Схема сообщений (решено)”

FlatBuffers. Причины: append-only эволюция полей (добавил поле — старые клиенты не ломаются), union как первоклассный реестр типов сообщений, zero-copy чтение (датаграмму 30 Гц можно читать прямо из буфера), отсутствующие поля = дефолты и не занимают байты.

Правила эволюции (обязательные, проверяются в CI):

  1. Поля только добавляются (append, монотонно растущие ID).
  2. required поля ЗАПРЕЩЕНЫ (compat-breaking by design).
  3. Удаление — только через deprecated и мажорный бамп протокола.
  4. Читатель игнорирует неизвестные поля; писатель может добавлять.

Каждое сообщение на проводе:

message { min_version: u16; type: union_tag; payload: table }

union_tag — реестр типов. Диапазон 0x8000+ зарезервирован под экспериментальные/вендорные типы (как X11 opcodes / HTTP private-use). Старый клиент, встретив неизвестный тег, молча дропает сообщение.

  1. Presence (datagrams, 20–30 Гц): {player_id, pos: vec3, yaw, pitch, avatar_name}. Крошечный трафик.
  2. Surface (reliable stream): описание общего окна {surface_id, размер, владелец, права} + поза в общей комнате.
  3. Media (отдельно): пиксели окна. Приоритет — sync-state: {url, timestamp, playing, rate} (~100 байт на действие) вместо стрима видео; хост может отойти, видео продолжится у каждого локально. Пиксельный стрим (для настоящих локальных окон) — str0m (Sans-IO WebRTC, встраивается в event loop композитора) + GStreamer/VAAPI кодирование (H.264 сначала, AV1 где есть железо). MoQ (Media over QUIC) — следить, не готово.

Права и безопасность (жёстко)

Section titled “Права и безопасность (жёстко)”
  • По умолчанию ничего не расшарено.
  • Расшаривание окна — явное, по окну.
  • Гость видит только расшаренное.
  • Гранулярные права на окно: view (только смотреть) → seek (можно перематывать / базовые клики) → control (полный ввод: мышь+клавиатура идут в приложение хоста).
  • Обратный канал ввода (гость → хост) — отдельный reliable stream; применяется только если у окна выставлен seek/control.
  • У гостей нет доступа к хост-системе вообще, только к потоку и (по флагу) к вводу в расшаренное окно.

Первый юзкейс (мотивация)

Section titled “Первый юзкейс (мотивация)”

Постримить браузер или терминал друзьям прямо в Minecraft: на сервере поднят мод, все заходят в особую область, окно хоста висит как живой экран в мире. Гости не только смотрят, но и могут перематывать видео. Отсюда требования: медиа-поток окна + обратный ввод с гранулярными правами (view/seek/control).

Server-authoritative relay (один пир хостит комнату и ретранслирует) с клиентским предсказанием/интерполяцией. P2P-mesh — отложено (O(n²) трафик, нет авторитета). Релей-сервер — тот же протокол, без рендера.

Открыто (нужно дочернуть в N0)

Section titled “Открыто (нужно дочернуть в N0)”
  • Конкретный набор сообщений v1 (presence, join/leave, surface descriptor, sync-state).
  • Аутентификация/идентичность пиров (публичные ключи? комнаты по ссылке?).
  • Разрешение конфликтов позы surface (кто владелец двинул — тот и прав?).
  • Точный формат ключа окна для восстановления (связь с сохранением раскладки, SPEC раздел 2).