Nihil: сетевой протокол (compositor-agnostic)
Nihil: сетевой протокол (compositor-agnostic)
Section titled “Nihil: сетевой протокол (compositor-agnostic)”Спека протокола «присутствие + общие окна». Фиксируется до кода (этап N0). Nihil, Minecraft-мод и любой другой клиент общаются через этот протокол как равноправные пиры.
Статус: черновик v0 (транспорт и схема решены по исследованию, wire-формат — в работе).
Транспорт (решено)
Section titled “Транспорт (решено)”Один 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):
- Поля только добавляются (append, монотонно растущие ID).
requiredполя ЗАПРЕЩЕНЫ (compat-breaking by design).- Удаление — только через
deprecatedи мажорный бамп протокола. - Читатель игнорирует неизвестные поля; писатель может добавлять.
Конверт
Section titled “Конверт”Каждое сообщение на проводе:
message { min_version: u16; type: union_tag; payload: table }union_tag — реестр типов. Диапазон 0x8000+ зарезервирован под экспериментальные/вендорные типы (как X11 opcodes / HTTP private-use). Старый клиент, встретив неизвестный тег, молча дропает сообщение.
Слои протокола
Section titled “Слои протокола”- Presence (datagrams, 20–30 Гц):
{player_id, pos: vec3, yaw, pitch, avatar_name}. Крошечный трафик. - Surface (reliable stream): описание общего окна
{surface_id, размер, владелец, права}+ поза в общей комнате. - 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).
Топология
Section titled “Топология”Server-authoritative relay (один пир хостит комнату и ретранслирует) с клиентским предсказанием/интерполяцией. P2P-mesh — отложено (O(n²) трафик, нет авторитета). Релей-сервер — тот же протокол, без рендера.
Открыто (нужно дочернуть в N0)
Section titled “Открыто (нужно дочернуть в N0)”- Конкретный набор сообщений v1 (presence, join/leave, surface descriptor, sync-state).
- Аутентификация/идентичность пиров (публичные ключи? комнаты по ссылке?).
- Разрешение конфликтов позы surface (кто владелец двинул — тот и прав?).
- Точный формат ключа окна для восстановления (связь с сохранением раскладки, SPEC раздел 2).