From 9e7d0836a3e3d952a72b957d1893341aa5431821 Mon Sep 17 00:00:00 2001 From: zarazaex69 Date: Tue, 19 May 2026 22:46:39 +0300 Subject: [PATCH] docs: translate docs to Russian and remove project-map --- docs/about.md | 1105 +++++++----------------------------- docs/client.example.yaml | 34 +- docs/configuration.md | 178 +++--- docs/failover.example.yaml | 9 +- docs/fast.md | 96 ++-- docs/manual.md | 9 +- docs/project-map.md | 422 -------------- docs/server.example.yaml | 51 +- docs/settings.md | 12 +- docs/uri.md | 7 +- 10 files changed, 401 insertions(+), 1522 deletions(-) delete mode 100644 docs/project-map.md diff --git a/docs/about.md b/docs/about.md index ff980bf..828f5ab 100644 --- a/docs/about.md +++ b/docs/about.md @@ -1,967 +1,268 @@ -# olcRTC - полная документация +# olcRTC - общее описание -> **olcRTC** (OpenLibreCommunity RTC) - инструмент обхода интернет-блокировок через паразитирование на легальных WebRTC-сервисах видеозвонков, уже находящихся в российских белых списках. -> -> Проект: [github.com/openlibrecommunity/olcrtc](https://github.com/openlibrecommunity/olcrtc) -> Лицензия: WTFPL -> Статус: **Beta** +`olcRTC` (OpenLibreCommunity RTC) - зашифрованный TCP-over-WebRTC туннель. Он маскирует трафик под обычное участие в WebRTC/SFU-сервисе: Jitsi Meet, Yandex Telemost или WB Stream. ---- +Проект: [github.com/openlibrecommunity/olcrtc](https://github.com/openlibrecommunity/olcrtc) +Лицензия: WTFPL +Статус: **Beta** -## Содержание +## Зачем это нужно -1. [Почему olcRTC существует](#1-почему-olcrtc-существует) -2. [Идея и история создания](#2-идея-и-история-создания) -3. [Как это работает](#3-как-это-работает) -4. [Архитектура](#4-архитектура) -5. [Структура репозитория](#5-структура-репозитория) -6. [Carriers - провайдеры](#6-carriers--провайдеры) -7. [Transports - транспорты](#7-transports--транспорты) -8. [Шифрование](#8-шифрование) -9. [Мультиплексирование](#9-мультиплексирование) -10. [SOCKS5 прокси](#10-socks5-прокси) -11. [Mobile / Android](#11-mobile--android) -12. [Python PoC скрипты](#12-python-poc-скрипты) -13. [Сборка и деплой](#13-сборка-и-деплой) -14. [YAML конфигурация](#14-yaml-конфигурация) -15. [URI-формат и подписки](#15-uri-формат-и-подписки) -16. [Матрица совместимости](#16-матрица-совместимости) -17. [CI/CD](#17-cicd) -18. [Что планируется сделать - Issues](#18-что-планируется-сделать--issues) -19. [Контрибуторы](#19-контрибуторы) -20. [Частые ошибки](#20-частые-ошибки) +В сценариях, где прямой доступ к произвольному VPS нестабилен или заблокирован, полезно переносить трафик через сервисы, которые уже доступны у пользователя. Для внешнего наблюдателя соединение выглядит как обычный WebRTC-звонок с выбранным сервисом, а полезная нагрузка внутри дополнительно шифруется общим ключом `crypto.key`. ---- +Базовая схема: -## 1. Почему olcRTC существует - -В России работают ТСПУ (технические средства противодействия угрозам). В мобильных сетях провайдеры перешли в режим **белых списков**: ТСПУ дропает все пакеты, кроме явно разрешённых IP-адресов и SNI. - -Фильтрация двухуровневая: -- **L3** - по IP-адресу назначения. Не разрешён → пакет физически не уходит дальше второго хопа. -- **L7** - по SNI в TLS ClientHello. Есть в чёрном списке → RST. - -Классические обходы через VPS ломаются когда VPS не попадает в белый список. Yandex Cloud, VK Cloud, Timeweb в списке - но провайдеры активно банят инстансы используемые как прокси. - -**Решение olcRTC**: не пытаться попасть в белый список - использовать сервисы, которые там уже есть навсегда. Телемост и WB Stream - сервисы видеозвонков крупных российских компаний. Пока они живы, olcRTC работает. Чтобы их заблокировать - нужно заблокировать сам сервис. - -Трафик идёт через WebRTC SFU этих сервисов: - -``` -Клиент (cnc) → SFU Яндекса/WB → Сервер (srv, ваш VPS) +```text +приложение + -> SOCKS5 127.0.0.1:8808 + -> olcrtc cnc + -> WebRTC/SFU сервис + -> olcrtc srv + -> интернет ``` -Для ТСПУ это выглядит как обычный видеозвонок. +## Как это работает ---- +Клиентский режим `cnc` поднимает локальный SOCKS5. Браузер, `curl`, sing-box или другое приложение подключается к нему как к обычному proxy. -## 2. Идея и история создания +Серверный режим `srv` подключается к той же комнате/сессии, принимает зашифрованный smux stream и от своего имени открывает TCP-соединения к целевым адресам. -### Хронология +Внутри туннеля: -**2025-04-03** - первый коммит `init repo`. Идея. - -**2025-04-06** - `remove text`. Единственная правка за целый год. - - -**2026-04-04** - `Initialize project with base configuration and assets`. Реальный перезапуск с нуля. - -**2026-04-05** - За один день появляются Python PoC: -- `telemost_poc_datachannel.py` - первое рабочее соединение через Telemost DataChannel -- `vcsend.py` - передача данных QR-кодами через видеопоток -- `flood.py` - стресс-тест соединений -- `limits.py` - обнаружен лимит Telemost DataChannel: 8KB на сообщение, всё что выше молча дропается -- `info.py` - исследование API Telemost - -**2026-04-06** - QR-код двусторонняя передача (`invicible`), первые замеры: **44 Mbps** через DataChannel. - -**2026-04-07** - первый Go бинарник: WebRTC туннель с ChaCha20-Poly1305 шифрованием, SOCKS5 прокси, деплой через Podman. Провайдер: только Telemost. - -**2026-04-08..09** - активная Go разработка: клиент-серверная архитектура, кастомный мультиплексор с sequence numbering, имена участников из файла, graceful shutdown, DNS поддержка, Android мост. - -**2026-04-10..11** - простой UI, Docker образ сервера. - -**2026-04-12..14** - большой рефакторинг: golangci-lint, Windows скрипты от `DeNcHiK3713`. - -**2026-04-19..20** - архитектурный рефакторинг: выделение слоёв `carrier` / `transport` / `link`, WB Stream провайдер через LiveKit SDK, видеоканальный PoC на Python. - -**2026-04-21..22** - `videochannel` транспорт (данные кодируются в QR-коды внутри VP8 видеопотока через ffmpeg), `vp8channel` транспорт (данные в VP8 payload), NVENC поддержка. - -**2026-04-25..30** - tile кодек для videochannel с Reed-Solomon коррекцией ошибок, `vp8channel` поверх KCP для надёжной доставки, замена самописного мультиплексора на smux. - -**2026-05-01..06** - `seichannel` (данные в H264 SEI NAL-юнитах), E2E тесты на реальных провайдерах, URI-формат и формат подписок, SOCKS5 аутентификация. - -**2026-05-07..10** - финальная полировка: исправлен throughput bug в vp8channel (ограничение было в 32 раза ниже реального), документация, SEI конфигурация, SOCKS5 аутентификация (username/password). - -**2026-05-11..14** - большой архитектурный рефакторинг `refactor/universal-carrier`: -- Разделение `internal/provider/` на `internal/engine/` (wire-level SFU протоколы) + `internal/auth/` (HTTP/API авторизация) -- Два основных engine: `livekit` (WB Stream), `goolom` (Telemost) -- Auth-провайдеры: `wbstream`, `telemost`, `jitsi` -- Замена `-carrier` на `-auth`/`-engine`/`-url`/`-token` -- Публичный Go API `pkg/olcrtc` (net.Conn через Session.Dial) для встраивания в sing-box и другие -- `cmd/olcrtc-cgo` — C-shared библиотека с Ping API -- YAML конфигурация вместо CLI-флагов (`internal/config/`) -- Протокол handshake (`internal/handshake/`) с CLIENT_HELLO/SERVER_WELCOME -- Session callbacks: OnSessionOpen, OnSessionClose, OnTraffic -- Перевод документации на русский - -### Статья на Хабре - -Проект описан в двух статьях на Хабре: -- *«Это - всё что вам надо знать о белых списках»* - технический анализ как работает фильтрация, 63k IP в белом списке из 46 млн российских, методы обхода -- *«BAREBONE2022: чтобы заблокировать этот протокол придётся запретить MAX и Yandex»* - описание идеи olcRTC, первые замеры скорости - ---- - -## 3. Как это работает - -``` -Браузер/приложение - │ (обычные TCP соединения) - ▼ - SOCKS5 :8808 ← cnc (клиент), работает на вашей машине - │ - │ ChaCha20-Poly1305 - │ smux поверх muxconn - │ - ▼ - Transport (datachannel / vp8channel / seichannel / videochannel) - │ - ▼ - Carrier (wbstream / telemost / jitsi) - │ WebRTC DataChannel или VideoTrack - ▼ - SFU Яндекса / WB / Jitsi ← сервер в белом списке у всех провайдеров - │ - ▼ - Transport (datachannel / vp8channel / seichannel / videochannel) - │ - ▼ - srv (сервер), работает на вашем VPS - │ (обычный TCP/DNS) - ▼ - Интернет +```text +SOCKS CONNECT + -> smux stream + -> XChaCha20-Poly1305 + -> transport + -> engine + -> WebRTC/SFU ``` -Клиент (`cnc`) поднимает локальный SOCKS5. Любой браузер или приложение подключается к нему как к обычному прокси. Трафик мультиплексируется через smux, шифруется ChaCha20-Poly1305 и передаётся через выбранный транспорт поверх WebRTC SFU. +## Режимы -Сервер (`srv`) стоит на вашем VPS. Он подключается к той же комнате видеозвонка, получает зашифрованный поток и от своего имени делает TCP соединения к нужным адресам в интернете. +| Режим | Назначение | +|---|---| +| `srv` | серверная сторона, принимает tunnel streams и делает TCP dial к целям | +| `cnc` | клиентская сторона, слушает локальный SOCKS5 | +| `gen` | создаёт Room ID для провайдеров, которые умеют создавать комнаты | -ТСПУ видит трафик к IP выбранного сервиса с корректным TLS и SNI - ничем не отличается от обычного видеозвонка. +CLI принимает один YAML-файл: ---- - -## 4. Архитектура - -Проект разбит на чёткие слои. Каждый слой можно заменить независимо. - -``` -cmd/olcrtc/ CLI entrypoint, загрузка YAML конфига -cmd/olcrtc-cgo/ C-shared библиотека (Ping API для десктопных клиентов) - │ -pkg/olcrtc/ Публичный Go API (net.Conn через Session.Dial) - │ -internal/config/ Загрузка и маппинг YAML конфига -internal/app/session/ конфигурация, валидация, роутинг в server/client - │ │ -internal/server/ internal/client/ бизнес-логика: SOCKS5, smux - │ -internal/handshake/ Протокол handshake (CLIENT_HELLO / SERVER_WELCOME) -internal/muxconn/ io.ReadWriteCloser поверх link.Link + AEAD - │ -internal/link/direct/ pass-through, пробрасывает в transport - │ -internal/transport/ интерфейс Transport + реестр - ├── datachannel/ WebRTC DataChannel как byte stream - ├── vp8channel/ VP8 видео + KCP поверх него - ├── seichannel/ H264 SEI NAL-юниты - └── videochannel/ QR-коды / тайлы в VP8 видеофрейме через ffmpeg - │ -internal/carrier/ интерфейс Carrier + реестр - ├── builtin/ регистрация engine+auth → carrier - └── bytestream.go ByteStream, VideoTrack capability - │ -internal/engine/ Wire-level SFU протоколы (URL+Token → WebRTC) - ├── livekit/ LiveKit (WB Stream) - ├── goolom/ Goolom (Yandex Telemost) - └── jitsi/ Jitsi Meet - │ -internal/auth/ HTTP/API авторизация → Credentials для engine - ├── wbstream/ WB Stream API (guest register, join, token) - ├── telemost/ Yandex Telemost (connection-info) - └── jitsi/ Jitsi room URL parsing - │ -internal/crypto/ ChaCha20-Poly1305 AEAD -internal/names/ генератор имён участников -internal/protect/ Android VPN protect() интеграция -internal/logger/ структурированное логирование -internal/link/ интерфейс Link + реестр -internal/e2e/ E2E тесты на реальных провайдерах +```bash +olcrtc server.yaml +olcrtc client.yaml ``` ---- +## Auth Providers -## 5. Структура репозитория +`auth.provider` выбирает сервис и способ получения credentials. -### Корень +| Provider | Engine | Комментарий | +|---|---|---| +| `jitsi` | `jitsi` | URL комнаты Jitsi, без отдельной регистрации | +| `telemost` | `goolom` | credentials через Yandex Telemost API | +| `wbstream` | `livekit` | guest flow WB Stream, умеет создавать комнаты для `gen` | +| `none` | задаётся в `engine.name` | прямой engine-режим с `engine.url` и `engine.token` | -| Файл/папка | Что это | -|---|---| -| `readme.md` | Краткое описание, команды сборки, ссылки | -| `SECURITY.md` | Политика безопасности | -| `magefile.go` | Система сборки на Mage (аналог Makefile для Go). Таргеты: `build`, `buildCLI`, `cross`, `mobile`, `docker`, `podman`, `lint`, `test`, `e2e`, `deps`, `clean` | -| `Dockerfile` | Многоэтапный образ: Alpine build → Alpine runtime с непривилегированным пользователем `olcrtc` | -| `docker-compose.server.yml` | Compose для серверного режима | -| `.gitmodules` | Субмодуль `internal/transport/videochannel/gr` - кастомные кодеки QR и tile | -| `.golangci.yml` | Конфиг линтера golangci-lint v2 | -| `.github/workflows/ci.yml` | CI: тесты, покрытие, E2E, lint, сборка CLI для всех платформ, сборка Android AAR | +Термин `carrier` ещё встречается во внутреннем API и логах как историческое имя для выбранного auth/provider пути. В YAML актуальное поле - `auth.provider`. -### `cmd/olcrtc/` +## Engines -| Файл | Что делает | -|---|---| -| `main.go` | Точка входа. Загружает YAML конфиг (`olcrtc config.yaml`), настраивает логирование, подавляет шум LiveKit/pion в не-debug режиме, запускает `session.Run` или `session.Gen`. Graceful shutdown по SIGTERM/SIGINT с 5-секундным таймаутом | -| `main_test.go` | Юнит-тесты CLI: валидация конфига, режимы, edge cases | +`engine` - низкоуровневый протокол конкретного SFU/signaling: -### `cmd/olcrtc-cgo/` +| Engine | Пакет | Возможности | +|---|---|---| +| `livekit` | `internal/engine/livekit` | data packets и video tracks через LiveKit SDK | +| `goolom` | `internal/engine/goolom` | Telemost/Goolom signaling, publisher/subscriber PeerConnection | +| `jitsi` | `internal/engine/jitsi` | Jitsi MUC/Jingle/colibri-ws, datachannel-путь и best-effort video | -| Файл | Что делает | -|---|---| -| `main.go` | C-shared библиотека. Экспортирует функцию `Ping()` для десктопных клиентов: запускает короткоживущий olcRTC клиент, ждёт SOCKS listener, делает HTTP ping через него и возвращает latency в миллисекундах. Используется для проверки связности из нативных приложений | +`internal/engine/builtin` связывает `auth.provider` с нужным engine. Отдельного пакета `internal/carrier` в текущем проекте нет. -### `internal/app/session/` +## Transports -| Файл | Что делает | -|---|---| -| `session.go` | Главная точка конфигурации. `RegisterDefaults()` регистрирует все carriers, links, transports. `Validate()` проверяет все настройки. `Run()` роутит в `server.Run` или `client.Run`. `Gen()` генерирует Room ID для auth-провайдеров с `RoomCreator` и ретраями | -| `session_test.go` | Тесты валидации конфига | +`net.transport` определяет, как tunnel bytes помещаются в WebRTC primitive. -### `internal/config/` +| Transport | Как передаёт данные | Основной сценарий | +|---|---|---| +| `datachannel` | нативный byte/data path engine | самый простой и быстрый путь, стабильно с Jitsi | +| `vp8channel` | KCP поверх VP8-like video frames | основной video-path для WB Stream и Telemost | +| `seichannel` | payload в H264 SEI NAL units, ACK/retry | fallback для WB Stream | +| `videochannel` | QR/tile кадры через ffmpeg, ACK/retry | экспериментальный визуальный транспорт | -| Файл | Что делает | -|---|---| -| `config.go` | Загрузка и парсинг YAML конфига. `Load(path)` читает файл, `Apply(dst, f)` маппит YAML поля в `session.Config`. Все структуры YAML: `File`, `Auth`, `Room`, `Crypto`, `Net`, `SOCKS`, `Engine`, `Video`, `VP8`, `SEI`, `Gen` | -| `config_test.go` | Тесты загрузки и маппинга | +Рекомендуемый старт: `jitsi + datachannel`. Альтернатива: `wbstream + vp8channel`. -### `internal/handshake/` +## Шифрование и handshake -| Файл | Что делает | -|---|---| -| `handshake.go` | Протокол handshake на контрольном smux-стриме. Wire format: 4-byte big-endian length + JSON. Клиент шлёт `CLIENT_HELLO` (device ID, claims), сервер отвечает `SERVER_WELCOME` (session ID) или `REJECT`. После handshake контрольный стрим остаётся открытым для keepalive | -| `handshake_test.go` | Тесты | +`internal/crypto` использует XChaCha20-Poly1305. Общий ключ задаётся как 64 hex-символа: -### `internal/server/` - -| Файл | Что делает | -|---|---| -| `server.go` | Серверная сторона туннеля. Подключается к комнате как второй участник звонка. Создаёт `muxconn` → `smux.Session`. Первый smux-стрим — контрольный (handshake CLIENT_HELLO / SERVER_WELCOME). Для каждого последующего стрима читает JSON `ConnectRequest` от клиента, устанавливает TCP соединение и гоняет байты. Поддерживает хуки: `OnSessionOpen`, `OnSessionClose`, `OnTraffic`. Умеет переподключаться при разрыве | -| `server_test.go` | Тесты серверной логики | - -### `internal/client/` - -| Файл | Что делает | -|---|---| -| `client.go` | Клиентская сторона. Поднимает SOCKS5-сервер. Для каждого входящего подключения: SOCKS5 handshake (поддержка RFC 1929 username/password auth), создаёт smux-стрим, шлёт JSON `ConnectRequest` с адресом, гоняет байты. Первый smux-стрим — контрольный (handshake). Переподключается при разрыве WebRTC сессии с retry loop | -| `client_test.go` | Тесты клиентской логики | - -### `internal/muxconn/` - -| Файл | Что делает | -|---|---| -| `conn.go` | Адаптер `link.Link` → `io.ReadWriteCloser`. Каждый `Write` шифрует блок ChaCha20-Poly1305 и отдаёт в link как одно сообщение. Входящие сообщения дешифруются и буферизуются; `Read` дренирует буфер в произвольных кусках (smux не знает о границах сообщений). Синхронизация через `sync.Cond` | -| `conn_test.go` | Тесты | - -### `internal/link/` - -| Файл | Что делает | -|---|---| -| `link.go` | Интерфейс `Link` (`Send`, `SetOnData`, `Connect`, `Close` и т.д.) + реестр | -| `link_test.go` | Тесты реестра | -| `direct/direct.go` | Единственная реализация Link. Pass-through: создаёт Transport и форвардит вызовы. Называется "direct" потому что нет промежуточного relay - данные идут прямо в transport | -| `direct/direct_test.go` | Тесты | - -### `internal/transport/` - -| Файл | Что делает | -|---|---| -| `transport.go` | Интерфейс `Transport` + реестр. `Features` описывает: надёжность, упорядоченность, message-oriented или stream, макс. размер payload | -| `transport_test.go` | Тесты реестра | -| `datachannel/transport.go` | Самый простой транспорт. Открывает ByteStream у carrier (DataChannel), просто форвардит байты. Лимит payload: 12KB | -| `vp8channel/transport.go` | Данные кодируются в VP8 видеофреймы. Поверх carrier строится KCP (надёжный UDP-подобный протокол) для реорганизации и ретрансмиссии. Данные батчатся по N фреймов за тик. Keepalive через keyframe | -| `vp8channel/kcp.go` | KCP сессия: conv ID = `0xC0FFEE01`, MTU 1400, окно 4096 сегментов. Length-prefix framing поверх KCP stream mode (workaround бага kcp-go с фрагментацией) | -| `vp8channel/kcpconn.go` | `io.ReadWriteCloser` адаптер для KCP | -| `seichannel/transport.go` | Данные передаются в SEI NAL-юнитах внутри H264 видеопотока. Собственный бинарный протокол с magic `OVC1`, версией, типами фреймов Data/Ack, CRC32, sequence numbers. ACK timeout, фрагментация, ретрансмиссия | -| `seichannel/h264.go` | Сборка H264 Access Unit с SEI payload. UUID для SEI: `5dc03ba8-450f-4b55-9a77-1f916c5b0739`. Статичные SPS/PPS/IDR как базовые заголовки | -| `videochannel/transport.go` | Данные визуально кодируются в кадры (QR-коды или тайлы), кадры транслируются через VP8 видеопоток. ffmpeg запускается как subprocess для кодирования/декодирования. ACK-based flow control с sequence numbers | -| `videochannel/visual.go` | Рендеринг кадров: QR-коды через `gr/qr`, тайлы через `gr/tile` с Reed-Solomon. Декодирование входящих кадров | -| `videochannel/ffmpeg.go` | ffmpeg encoder/decoder как subprocess с pipe. Поддержка VP8, H264. Hardware acceleration через NVENC. Таймаут на получение фрейма | -| `videochannel/frame.go` | Протокол фреймов videochannel | - -### `internal/carrier/` - -| Файл | Что делает | -|---|---| -| `carrier.go` | Интерфейс `Session` + реестр. `Capabilities` описывает что умеет carrier: ByteStream и/или VideoTrack | -| `bytestream.go` | `ByteStream` и `VideoTrack` интерфейсы | -| `carrier_test.go` | Тесты | -| `builtin/register.go` | Регистрирует telemost, wbstream, jitsi, none в реестре carrier через `registerEngineAuth` (связывает auth provider с engine) и `registerDirect` (прямое подключение без auth) | -| `builtin/engine_adapter.go` | Адаптер `engine.Session` → `carrier.Session`. Связывает auth provider (Issue → Credentials) с engine (Connect с URL+Token). Поддерживает Refresh callback для engines, требующих свежие credentials при реконнекте (Goolom) | - -### `internal/engine/` - -| Файл | Что делает | -|---|---| -| `engine.go` | Интерфейс `Session` (Connect, Send, Close, WatchConnection, CanSend и т.д.) + `Factory` + реестр. `Config` содержит URL, Token, Extra, OnData, DNSServer, Refresh callback. `Capabilities`: ByteStream, VideoTrack | -| `livekit/engine.go` | LiveKit engine — используется WB Stream. Подключается через LiveKit SDK, публикует/подписывается на DataChannel и VideoTrack | -| `goolom/engine.go` | Goolom engine — проприетарный протокол Яндекса (Telemost). WebSocket signaling, dual pub/sub PeerConnections, DataChannel, telemetry. Использует `Refresh` callback для получения свежих credentials при реконнекте | -| `jitsi/engine.go` | Jitsi engine — MUC/Jingle/colibri-ws, byte stream через bridge channel и best-effort VideoTrack | - -### `internal/auth/` - -| Файл | Что делает | -|---|---| -| `auth.go` | Интерфейс `Provider` (Engine, DefaultServiceURL, Issue) + `RoomCreator` + реестр. `Credentials`: URL, Token, Extra | -| `wbstream/provider.go` | WB Stream auth: guest register → join room → token exchange. Реализует `RoomCreator`. `Engine()` → `"livekit"`, `DefaultServiceURL()` → `"https://stream.wb.ru"` | -| `telemost/provider.go` | Yandex Telemost auth: HTTP connection-info → engine credentials. `Engine()` → `"goolom"`, `DefaultServiceURL()` → `"https://telemost.yandex.ru"` | -| `jitsi/provider.go` | Jitsi auth: разбирает URL комнаты и передаёт параметры engine. `Engine()` → `"jitsi"` | - -### `internal/crypto/` - -| Файл | Что делает | -|---|---| -| `chacha.go` | ChaCha20-Poly1305 AEAD. 32-байтовый ключ. Каждый Encrypt генерирует случайный nonce и prepend его к ciphertext. Decrypt проверяет AEAD тег | -| `chacha_test.go` | Тесты | - -### `internal/names/` - -| Файл | Что делает | -|---|---| -| `names.go` | Генератор случайных имён участников для WebRTC комнаты. Имена загружаются из `data/names` и `data/surnames` (встроены через `//go:embed`). Можно переопределить внешними файлами. `Generate()` возвращает "Имя Фамилия" с крипто-рандомом | -| `names_test.go` | Тесты | - -### `internal/protect/` - -| Файл | Что делает | -|---|---| -| `protect.go` | Android VPN protect() интеграция. `Protector func(fd int) bool` - если установлен, вызывается перед каждым connect чтобы сокет не роутился через VPN (нужно для корректной работы в связке с VPN-приложением на Android) | -| `protect_test.go` | Тесты | - -### `internal/logger/` - -| Файл | Что делает | -|---|---| -| `logger.go` | Структурированный логгер с уровнями Info/Warn/Error/Debug/Verbose. В не-debug режиме подавляет шум pion/LiveKit | -| `logger_test.go` | Тесты | - -### `internal/e2e/` - -| Файл | Что делает | -|---|---| -| `tunnel_test.go` | E2E тесты на реальных провайдерах. Матрица всех carrier × transport комбинаций. Запускается с флагом `-olcrtc.real-e2e`. В CI запускается на каждый push | - -### `pkg/olcrtc/` - -| Файл | Что делает | -|---|---| -| `olcrtc.go` | Публичный Go API для встраивания olcrtc как библиотеки. `New(ctx, Config)` создаёт `Session`. Два режима: direct engine (URL+Token) или built-in auth (Auth+RoomID). `Session.Connect()`, `Send()`, `Close()`, `WatchConnection()`, `SetEndedCallback()` | -| `conn.go` | `Session.Dial(ctx)` → `net.Conn`. Реализует `net.Conn` через `io.Pipe`: `Read` из pipe (заполняется OnData), `Write` через engine.Send. Для интеграции с sing-box и другими io.ReadWriter потребителями | -| `olcrtc_test.go` | Тесты публичного API | -| `tunnel/` | Подпакет для высокоуровневого туннелирования | - -### `mobile/` - -| Файл | Что делает | -|---|---| -| `mobile.go` | gomobile-совместимый API для Android/iOS. Синглтон: `Start()`, `Stop()`, `IsRunning()`. `SocketProtector` интерфейс для Android VPN bypass. `LogWriter` интерфейс для получения логов в Kotlin/Java. По умолчанию использует `vp8channel` транспорт | -| `mobile_test.go` | Тесты mobile API | - -### `code/` - Python PoC скрипты - -| Файл | Что делает | -|---|---| -| `telemost_poc_datachannel.py` | Базовый PoC: два гостя в одной Telemost комнате, обмен данными через DataChannel | -| `telemost_poc_videochannel.py` | Передача данных QR-кодами в видеопотоке Telemost | -| `telemost_info.py` | Сбор полной информации о Telemost конференции: участники, кодеки, ICE серверы, SDP | -| `wbstream_poc_datachannel.py` | PoC DataChannel через WB Stream | -| `wbstream_poc_videochannel.py` | PoC видеоканала через WB Stream | -| `wbstream_info.py` | Информация о WB Stream комнате | -| `secretny_ddoos.py` | Утилита для стресс-тестирования (flood) | -| `init.sh` | Скрипт инициализации окружения | -| `requirements.txt` | Python зависимости: aiortc, opencv, pyzbar и др. | - -### `script/` - -| Файл | Что делает | -|---|---| -| `srv.sh` | Интерактивный скрипт запуска сервера через Podman. Задаёт вопросы про carrier/transport/room/key, генерирует YAML конфиг, собирает образ, запускает контейнер. Флаги: `--branch=` (сменить ветку), `--no-cache` (очистить Go-кеш перед сборкой) | -| `cnc.sh` | Интерактивный скрипт запуска клиента через Podman | -| `script/docker/olcrtc-entrypoint.sh` | Docker entrypoint: читает env переменные, генерирует YAML конфиг, запускает `olcrtc` | -| `docker/olcrtc-healthcheck.sh` | Docker healthcheck: проверяет что процесс запущен | - -### `data/` - -| Файл | Что делает | -|---|---| -| `names` | Список русских имён для генератора имён участников | -| `surnames` | Список русских фамилий | - -### `docs/` - -| Файл | Что делает | -|---|---| -| `about.md` | Этот документ — полная документация проекта | -| `fast.md` | Быстрый старт через скрипты (Podman) | -| `manual.md` | Мануальная сборка: Go, mage, кросс-компиляция, все шаги | -| `settings.md` | Матрица совместимости carrier×transport, описание всех YAML полей, готовые примеры конфигов | -| `configuration.md` | Краткая справка по YAML схеме | -| `uri.md` | URI формат для клиентских приложений: `olcrtc://?@#$` | -| `sub.md` | Формат подписок: список серверов в одном файле с метаданными | -| `server.example.yaml` | Полный пример серверного YAML конфига | -| `client.example.yaml` | Полный пример клиентского YAML конфига | - ---- - -## 6. Carriers - провайдеры - -Carrier - это WebRTC сервис видеозвонков, через который идёт туннель. - -### Yandex Telemost (`telemost`) - -- Сервис видеозвонков от Яндекса: `telemost.yandex.ru` -- **Удалил DataChannel** - его больше нет в Telemost -- VideoTrack: только vp8channel стабильно работает, videochannel — best effort, seichannel не поддерживается -- Требует создания комнаты вручную через сайт (нет автогенерации) -- Двухуровневый keepalive: WebSocket ping + app-level ping - -### WB Stream (`wbstream`) - -- Сервис трансляций от Wildberries: `stream.wb.ru` -- **Рекомендуется** - самый стабильный -- Минимальная прослойка, почти прямой relay -- Работает с vp8channel, seichannel, videochannel -- DataChannel **не работает** в обычном guest flow: WB Stream выдаёт токены с `canPublishData=false`, DC не маршрутизирует данные (expected fail в E2E тестах) -- Room ID можно создать вручную через stream.wb.ru или через `mode: gen` -- Инициализация звонка автоматически - ---- - -## 7. Transports - транспорты - -Transport определяет как именно данные упаковываются в WebRTC поток. - -### datachannel - -Самый простой и быстрый. Данные идут напрямую через WebRTC DataChannel (SCTP over DTLS). - -- Лимит payload: 12KB на сообщение (ограничение SFU) -- Надёжный, упорядоченный (SCTP гарантирует) -- Работает с Jitsi и direct engine-сценариями -- Telemost удалил DataChannel -- WB Stream DataChannel **не работает** в обычном guest flow — токены выдаются с `canPublishData=false` - -### vp8channel - -Данные упаковываются в VP8 видеофреймы. Поверх этого строится KCP - надёжный протокол с повторной передачей, работающий поверх ненадёжного канала. - -- Работает с telemost и wbstream (pass в E2E тестах) -- Большой пинг из-за батчинга фреймов -- KCP параметры: MTU 1400, окно 4096, conv ID `0xC0FFEE01` -- Рекомендуется: `vp8.fps: 60`, `vp8.batch_size: 64` - -### seichannel - -Данные передаются в SEI (Supplemental Enhancement Information) NAL-юнитах H264 видеопотока. SEI - стандартный механизм для метаданных в H264. - -- Собственный бинарный протокол: magic `OVC1` (0x4f564331), версия, тип Data/Ack, CRC32, sequence numbers -- UUID для SEI payload: `5dc03ba8-450f-4b55-9a77-1f916c5b0739` -- ACK timeout (по умолчанию 3с), фрагментация, ретрансмиссия до 4 попыток -- Работает только с wbstream (pass в E2E тестах) -- Telemost не поддерживает (fail) -- Рекомендуется: `sei.fps: 60`, `sei.batch_size: 64`, `sei.fragment_size: 900`, `sei.ack_timeout_ms: 2000` - -### videochannel - -Данные визуально кодируются в видеофреймы через ffmpeg. Два визуальных кодека: - -**qrcode** - данные кодируются в QR-код, QR рендерится в VP8 кадр. На приёмнике VP8 декодируется и QR сканируется. Использует библиотеку `gr/qr` (субмодуль). Настройки: разрешение, ECC уровень (`low`/`medium`/`high`/`highest`), размер фрагмента. - -**tile** - тайловый кодек, только 1080x1080. Пиксели кодируют биты напрямую. Reed-Solomon коррекция ошибок. Параметры: размер тайла в пикселях (1..270), процент избыточности (0..200). Быстрее QR но нестабильнее. - -Общее: ffmpeg как subprocess, поддержка NVENC, VP8 видеопоток. Самый медленный транспорт. Работает стабильно с wbstream, best effort с telemost. - ---- - -## 8. Шифрование - -Весь туннельный трафик шифруется **ChaCha20-Poly1305** (XChaCha20-Poly1305 через `golang.org/x/crypto`). - -- Ключ: 32 байта, передаётся как hex строка (64 символа) -- Генерация: `openssl rand -hex 32` -- Каждое сообщение: случайный nonce (24 байта) prepend к ciphertext + AEAD тег -- Ключ должен совпадать на сервере и клиенте -- Шифрование происходит в `muxconn` - до передачи в transport/carrier - -WebRTC сам по себе шифрует трафик через DTLS-SRTP, но olcRTC добавляет поверх свой слой - провайдер видит только зашифрованный blob. - ---- - -## 9. Мультиплексирование - -Через один WebRTC DataChannel / VideoTrack одновременно могут идти сотни TCP соединений браузера. - -Реализация через **smux** (`github.com/xtaci/smux`) - библиотека мультиплексирования потоков, аналог HTTP/2 multiplexing. - -До мая 2026 был самописный мультиплексор с sequence numbering и ручным out-of-order handling. Заменён на smux поверх KCP для vp8channel, и smux напрямую для datachannel. - -`muxconn.Conn` адаптирует `link.Link` (message-oriented) в `io.ReadWriteCloser` (stream-oriented) который нужен smux. Каждый `Write` = одно зашифрованное сообщение в link. - ---- - -## 10. SOCKS5 прокси - -Клиент (`cnc`) поднимает локальный SOCKS5-сервер. - -**Поддерживается:** -- SOCKS5 (RFC 1928) с командой CONNECT -- Аутентификация username/password (RFC 1929) через `socks.user`/`socks.pass` в YAML конфиге -- SOCKS5h (hostname resolution на стороне сервера) - DNS запросы идут через туннель -- Без аутентификации (по умолчанию) - -**Адрес по умолчанию:** `127.0.0.1:8808` - -**Использование:** -```sh -curl --socks5-hostname 127.0.0.1:8808 https://icanhazip.com -export all_proxy=socks5h://127.0.0.1:8808 -export all_proxy=socks5h://user:pass@127.0.0.1:8808 # с авторизацией -``` - -**Сервер** (`srv`) может сам ходить через SOCKS5 прокси для исходящего трафика (`socks.proxy_addr`, `socks.proxy_port` в YAML конфиге). - ---- - -## 11. Mobile / Android - -`mobile/mobile.go` - gomobile-совместимый API. - -Собирается в `olcrtc.aar` через `mage mobile` (`gomobile bind`). - -Community Android клиент: [alananisimov/olcbox](https://github.com/alananisimov/olcbox) - -**API:** -- `Start(carrier, roomID, keyHex string)` - запустить туннель -- `Stop()` - остановить -- `IsRunning() bool` -- `SetProtector(p SocketProtector)` - Android VPN bypass (VpnService.protect) -- `SetLogWriter(w LogWriter)` - получать логи в Kotlin/Java - -По умолчанию использует `vp8channel` транспорт (наиболее совместимый). - -`protect.go` - механизм Android VPN protect: перед каждым `connect()` вызывается Kotlin-коллбэк который вызывает `VpnService.protect(fd)`. Без этого трафик olcRTC может рекурсивно идти через тот же VPN. - ---- - -## 12. Python PoC скрипты - -Исторический слой - с этого всё начиналось. Используются для исследования API провайдеров и проверки гипотез. - -**Telemost:** -- `telemost_poc_datachannel.py` - первый рабочий туннель, обнаружен лимит 8KB DataChannel (молча дропает больше) -- `telemost_poc_videochannel.py` - QR в видео, `vcsend.py` - передача файлов -- `telemost_info.py` - полный дамп SDP, ICE серверов, участников - -**WB Stream:** -- `wbstream_poc_datachannel.py` - DataChannel -- `wbstream_poc_videochannel.py` - видеоканал -- `wbstream_info.py` - информация - -Для запуска: `pip install -r code/requirements.txt` - ---- - -## 13. Сборка и деплой - -### Зависимости - -- Go 1.25+ (go.mod: `go 1.25.0`) -- Mage (`go install github.com/magefile/mage@latest`) -- ffmpeg (для videochannel транспорта) -- git с `--recurse-submodules` (субмодуль `gr` для videochannel кодеков) -- gomobile (для Android сборки) - -### Mage таргеты - -```sh -mage build # текущая платформа -mage buildCLI # только CLI бинарник -mage cross # все платформы: linux/amd64, linux/arm64, windows/amd64, - # darwin/amd64, darwin/arm64, freebsd/amd64, freebsd/arm64, - # openbsd/amd64, openbsd/arm64 -mage mobile # Android AAR через gomobile -mage podman # Docker образ через podman -mage docker # Docker образ через docker -mage lint # golangci-lint v2 -mage test # go test -race ./... -mage e2e # E2E тесты (нужны реальные провайдеры) -mage deps # go mod tidy + download -mage clean # удалить build/ -``` - -### Быстрый старт через скрипты (Podman) - -```sh -git clone https://github.com/openlibrecommunity/olcrtc --recurse-submodules -cd olcrtc - -# на сервере (VPS): -./script/srv.sh - -# на клиенте: -./script/cnc.sh -``` - -### Мануальный запуск - -```sh -# генерация ключа +```bash openssl rand -hex 32 +``` -# создать конфиг (пример: wbstream + vp8channel) -cat > server.yaml < SERVER_WELCOME +CONTROL_PING <-> CONTROL_PONG +``` + +Если control pong не приходит несколько раз подряд, runtime пересобирает smux-сессию или отдаёт управление failover supervisor. + +## YAML + +Минимальный сервер: + +```yaml mode: srv -link: direct auth: - provider: wbstream + provider: jitsi room: - id: "ROOM_ID_HERE" + id: "https://meet.small-dm.ru/myroom" crypto: - key: "REPLACE_WITH_64_HEX" + key: "REPLACE_ME_WITH_64_HEX_CHARS" net: - transport: vp8channel + transport: datachannel dns: "1.1.1.1:53" data: data -EOF +``` -# запустить сервер -./olcrtc server.yaml +Минимальный клиент: -# конфиг клиента -cat > client.yaml <", + DNSServer: "1.1.1.1:53", +}) +err := srv.Run(ctx) +``` + +В этом API поле `Carrier` сохранено ради совместимости с существующими интеграциями; по смыслу это имя `auth.provider`. + +## Mobile / Android + +`mobile/mobile.go` предоставляет gomobile API: + +- `SetProtector` для Android VPN `protect(fd)`; +- `SetTransport`, `SetDNS`, `SetVP8Options`, `SetLivenessOptions`; +- `Start`, `StartWithTransport`, `Stop`; +- `Check`/ping helpers для проверки доступности. + +По умолчанию mobile-клиент использует `vp8channel`; `datachannel` тоже поддерживается. + +## Тесты + +```bash +go test -count=1 ./... +mage test +mage e2e +``` + +Real-provider E2E включаются через переменные: + +```bash +E2E_CARRIERS=wbstream E2E_TRANSPORTS=vp8channel mage e2e +``` + +## Частые проблемы + +| Симптом | Что проверить | |---|---| -| `socks.host` | Адрес SOCKS5 (по умолчанию `127.0.0.1`) | -| `socks.port` | Порт SOCKS5 (по умолчанию `8808`) | -| `socks.user` | Логин (опционально) | -| `socks.pass` | Пароль (опционально) | - -### Только сервер (`mode: srv`) - -| YAML путь | Описание | -|---|---| -| `socks.proxy_addr` | Адрес SOCKS5 прокси для исходящего трафика | -| `socks.proxy_port` | Порт этого прокси | - -### Генерация (`mode: gen`) - -| YAML путь | Описание | -|---|---| -| `gen.amount` | Количество комнат для генерации | - -### Транспорты - -Настройки транспортов задаются в секциях `vp8`, `sei`, `video` YAML конфига. Подробнее в [configuration.md](configuration.md). - ---- - -## 15. URI-формат и подписки - -### URI формат - -Соглашение для клиентских приложений. Сам `olcrtc` не парсит - используется в сторонних клиентах. - -``` -olcrtc://?@#$ -``` - -Где `` - опциональный блок `` с параметрами транспорта. - -**Примеры:** -``` -olcrtc://wbstream?vp8channel@room-01#d823fa...$RU -olcrtc://wbstream?datachannel@room-01#d823fa...$RU / DC does not work in guest flow -olcrtc://wbstream?seichannel@room-01#d823fa...$RU -``` - -### Формат подписки (sub.md) - -Текстовый файл со списком серверов. Хостится на сервере как plain text. - -```text -#name: Zarazaex Free RU -#update: 1778011200 -#refresh: 10m -#icon: 🇷🇺 - -olcrtc://wbstream?vp8channel@room-01#key$RU / free -##name: RU-1 -##ip: 1.2.3.4 -##comment: basic free node -``` - -Клиентские приложения читают этот файл и предлагают список серверов пользователю (аналог подписок в v2ray/sing-box). - ---- - -## 16. Матрица совместимости - -| Transport | telemost | wbstream | jitsi | -|---|:---:|:---:|:---:| -| datachannel | `-` | `-` | `+` | -| vp8channel | `+` | `+` | `~` | -| seichannel | `-` | `+` | `~` | -| videochannel | `~` | `+` | `~` | - -- `+` работает (pass в E2E тестах) -- `-` не работает / не поддерживается (fail в E2E тестах) -- `~` best effort (может работать, но нестабильно) - -**Telemost:** только vp8channel стабильно проходит. DataChannel удалён из Telemost. seichannel не поддерживается. videochannel — best effort. - -**WBStream:** все транспорты кроме datachannel работают. DataChannel помечен как expected fail — в обычном guest flow WB Stream выдаёт токены с `canPublishData=false`, и DC не маршрутизирует данные. Для DC нужны модераторские/permission права. - -**Рекомендуется:** `wbstream + vp8channel` — работает стабильно, не требует специальных прав. - -**Скорость по убыванию:** `datachannel` > `vp8channel` > `seichannel` > `videochannel` - - - -**Рекордный замер:** на связке `wbstream + datachannel` (test by `x2827262628281872727`) зафиксированы пинг **7 мс** и скорость **792.62 Mbps на вход / 749.69 Mbps на выход** — максимум, измеренный через olcRTC. Этот режим больше не работает в обычном guest flow (WB Stream выдаёт токены без `canPublishData`). - -speedtest - ---- - -## 17. CI/CD - -`.github/workflows/ci.yml` - GitHub Actions, запускается на каждый push и PR в master. - -| Job | Что делает | -|---|---| -| `test` | `go test -count=1 ./...` | -| `coverage` | `go test --cover ./...` | -| `real-e2e` | E2E матрица всех carrier×transport на реальных провайдерах (25 мин таймаут) | -| `lint` | golangci-lint v2 | -| `build-cli` | `mage cross` - кросс-компиляция для 9 платформ, артефакты в Actions | -| `build-android` | `mage mobile` - Android AAR, артефакт в Actions | - -Go версия в CI: 1.25.x - ---- - -## 18. Что планируется сделать - Issues - -### Открытые - -**Issue #22 - реализовать поддержку stream.wb.ru** `enhancement` - -WB Stream - текущий приоритет. Основа уже реализована, остаётся: -- [ ] Симуляция XHR телеметрии (маскировка под легитимный клиент) -- [ ] Симуляция задержек и обрезание до размера реальных сообщений -- [ ] Система завершения звонка -- [ ] Авто перезапуск звонка если идёт слишком долго -- [ ] Юзать TLS стек Chrome как naiveproxy - -**Issue #2 - реализовать поддержку telemost.yandex.ru** `enhancement` - -- [ ] Симуляция XHR телеметрии -- [ ] Симуляция задержек -- [ ] Инициализация звонка изнутри автоматически -- [ ] Система завершения звонка -- [ ] Авто перезапуск звонка -- [ ] TLS стек Chrome - -### Закрытые (уже сделано) - -| Issue | Что было | -|---|---| -| #44 | Very high ping - исправлен throughput bug vp8channel | -| #40 | Подключение нескольких устройств | -| #39 | Oracle VPS поддержка | -| #38 | Стандартный URI формат - реализован | -| #37 | Jitsi Meet - не планируется | -| #33 | iOS клиент - в планах | -| #27 | Инструкция - написана | -| #26 | SIP003 transport - не планируется | -| #25 | TLS/DTLS фингерпринтинг | -| #9 | Нормальный мультиплексор - реализован (smux) | -| #3 | macOS/Linux/Android/Windows поддержка - реализована | - ---- - -## 19. Контрибуторы - -| Контрибутор | Коммиты | Вклад | -|---|---|---| -| **zarazaex69** (zarazaex@tuta.io) | 417 | Автор проекта. Вся архитектура, все транспорты, carriers, crypto, mobile API, CI, документация | -| **zowue** (heminpo49@gmail.com) | 24 | Соавтор. Упомянут в оригинальной статье на Хабре | -| **TheDevisi** (devisinov@gmail.com) | 20 | UI, SOCKS5 улучшения, Windows поддержка, фиксы | -| **Qtozdec** | 10 | Фиксы, URI добавление | -| **Alexander Anisimov** / alananisimov | 6 | Android клиент [olcbox](https://github.com/alananisimov/olcbox), mobile.go фиксы, mobile provider config, cmd/olcrtc-cgo (C-shared Ping API) | -| **spkprsnts** (jectokuu@gmail.com) | 2 | Кастомный путь к ffmpeg (`-ffmpeg` flag), снижение задержки VP8 кодирования | -| **win64exe** (doost-55@yandex.ru) | 1 | Фикс srv.sh (--network host) | -| **s0me0ne-25** | 3 | Расширение датасета имён и фамилий | -| **Kot-nikot** | 3 | Фиксы | -| **HLNikNiky** / Sesdear | 2 | URI добавление, фиксы | -| **Denis Suchok** / DeNcHiK3713 | 1 | Windows Podman скрипты | -| **scalebb2** | 1 | - | - ---- - -## 20. Частые ошибки - -### `Connection refused` на порту SOCKS5 + `i/o timeout` при резолве - -**Симптомы:** -``` -curl: (7) Failed to connect to 127.0.0.1 port 8808 after 0 ms: Connection refused -``` - -Клиент сообщает `[+] Client started successfully!`, но SOCKS5 порт не слушает. - -В логах контейнера: -``` -client: failed to connect link: transport connect: stream connect: connect: -get room token: register guest: do request: Post "https://stream.wb.ru/...": -dial tcp: lookup stream.wb.ru: i/o timeout -``` - -**Причина:** клиент не смог зарезолвить `stream.wb.ru` через указанный DNS сервер. Соединение не установилось, SOCKS5 не поднялся. - -**Решение:** указать другой DNS сервер в скрипте. Вместо дефолтного `1.1.1.1` попробовать `8.8.8.8` или `77.88.8.8`: - -```sh -# при запуске cnc.sh - в поле DNS ввести: -8.8.8.8:53 -# или -77.88.8.8:53 -``` - -При ручном запуске — указать другой DNS в YAML конфиге: -```yaml -net: - dns: "8.8.8.8:53" -``` - -После смены DNS в логах должна появиться строка: -``` -SOCKS5 server listening on 0.0.0.0:8808 -``` - -### `dial tcp4 : i/o timeout` на сервере (VPS блокирует исходящий трафик) - -**Симптомы:** - -В логах сервера появляются строки вида: -``` -sid=59 dial 157.240.205.60:443 failed (10.000774052s): dial failed: dial tcp4 157.240.205.60:443: i/o timeout -sid=69 dial 194.221.250.50:443 failed (10.002092858s): dial failed: dial tcp4 194.221.250.50:443: i/o timeout -sid=81 dial 149.154.167.41:5222 failed (10.000219783s): dial failed: dial tcp4 149.154.167.41:5222: i/o timeout -``` - -Таймаут всегда ровно 10 секунд (это дефолтный `Timeout: 10 * time.Second` в `server.go`). Затронутые сайты открываются нормально с локального браузера через прокси, но сервер до них не добирается. - -**Причина:** хостинг-провайдер или фаервол VPS блокирует исходящие соединения к определённым IP-адресам или портам. Типичные жертвы: - -- `157.240.x.x` - Facebook/Meta (порты 80, 443) -- `194.221.x.x`, `149.154.x.x`, `91.108.x.x`, `91.105.x.x` - Telegram (порты 80, 443, 5222) - -Российские VPS-провайдеры блокируют исходящий трафик к этим сайтам на уровне фаервола хостинга - независимо от настроек iptables на самой машине. - -**Диагностика:** выполнить прямо на сервере: -```sh -curl -v --connect-timeout 5 https://157.240.205.60 -curl -v --connect-timeout 5 https://149.154.167.41 -``` -Если таймаут - проблема на уровне хостинга. - -**Решение:** - -1. Сменить хостинг-провайдера или локацию на того, кто не блокирует исходящий трафик. -2. Использовать на сервере исходящий SOCKS5 прокси через YAML конфиг: -```yaml -socks: - proxy_addr: "1.2.3.4" - proxy_port: 1080 -``` - -Это ошибка не на стороне olcRTC - он корректно логирует ошибки и продолжает работу. Соединения к незаблокированным адресам проходят без проблем. Проблема на стороне хостинга или фаервола. - ---- - -## Контакты - -- Telegram канал: [@openlibrecommunity](https://t.me/openlibrecommunity) - бесплатный прокси в закрепе, обновления, анонсы -- Telegram автора: [@zarazaexe](https://t.me/zarazaexe) -- Email: [zarazaex@tuta.io](mailto:zarazaex@tuta.io) -- GitHub: [openlibrecommunity](https://github.com/openlibrecommunity) -- Android клиент: [alananisimov/olcbox](https://github.com/alananisimov/olcbox) -- Белые списки (еженедельное обновление): [openlibrecommunity/twl](https://github.com/openlibrecommunity/twl) +| `key required` или `invalid key` | на обеих сторонах одинаковый 64-символьный hex key | +| SOCKS5 не слушает | `mode: cnc`, `socks.host`, `socks.port`, логи клиента | +| Jitsi не соединяется без второго участника | сервер и клиент должны быть в одной комнате | +| WB Stream + datachannel не работает | в guest flow нет `canPublishData`; используй `vp8channel`, `seichannel` или `videochannel` | +| `seichannel ack timeout` | провайдер режет/не маршрутизирует video path; смени transport/provider | +| `ffmpeg` not found | установи ffmpeg или задай `ffmpeg: /path/to/ffmpeg` | + +## Ссылки + +- [Быстрый старт](fast.md) +- [Ручная сборка](manual.md) +- [Настройка YAML](configuration.md) +- [Матрица совместимости](settings.md) +- [URI формат](uri.md) +- [Формат подписки](sub.md) diff --git a/docs/client.example.yaml b/docs/client.example.yaml index 81c4de1..370c277 100644 --- a/docs/client.example.yaml +++ b/docs/client.example.yaml @@ -1,24 +1,22 @@ -# olcrtc client config example -# Run with: olcrtc client.yaml +# Пример клиентского конфига olcrtc +# Запуск: olcrtc client.yaml mode: cnc -link: direct - auth: - provider: jitsi # must match the server + provider: jitsi # должен совпадать с сервером -# For jitsi: full conference URL (https://host/room or host/room). -# Must match the server. +# Для jitsi: полный URL комнаты (https://host/room или host/room). +# Должен совпадать с сервером. room: id: "https://meet.small-dm.ru/REPLACE_WITH_ROOM_NAME" crypto: - # Or use key_file: "./olcrtc.key" to keep the secret out of this file. - key: "REPLACE_ME_WITH_64_HEX_CHARS" # must match the server + # Можно использовать key_file: "./olcrtc.key", чтобы не хранить секрет прямо здесь. + key: "REPLACE_ME_WITH_64_HEX_CHARS" # должен совпадать с сервером net: - transport: datachannel # must match the server + transport: datachannel # должен совпадать с сервером dns: "8.8.8.8:53" liveness: @@ -26,24 +24,24 @@ liveness: timeout: 5s failures: 3 -# Optional planned rebuild for long-running calls. +# Необязательный плановый rebuild долгих звонков. # lifecycle: # max_session_duration: 6h -# Optional reliability shaping for encrypted wire messages. +# Необязательный лимит/pacing для зашифрованных wire-сообщений. # traffic: # max_payload_size: 4096 # min_delay: 5ms # max_delay: 30ms -# Local SOCKS5 listener exposed to applications +# Локальный SOCKS5 listener для приложений. socks: host: "127.0.0.1" port: 8808 - user: "" # optional inbound auth + user: "" # необязательная входящая auth pass: "" -# Direct engine mode — only when auth.provider is "none" +# Прямой engine-режим: используется только при auth.provider: none. engine: name: "" url: "" @@ -54,10 +52,10 @@ vp8: batch_size: 64 sei: - fps: 20 - batch_size: 1 + fps: 60 + batch_size: 64 fragment_size: 900 - ack_timeout_ms: 3000 + ack_timeout_ms: 2000 video: width: 1920 diff --git a/docs/configuration.md b/docs/configuration.md index e4fd98f..c043e4b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,63 +1,94 @@ -# Настройка +# Настройка YAML -olcrtc считывает всю свою конфигурацию среды выполнения из одного YAML-файла. -теперь флагов CLI нет. +`olcrtc` читает runtime-настройки из одного YAML-файла. CLI принимает ровно один аргумент - путь к конфигу; отдельных CLI-флагов для режима, транспорта и провайдера больше нет. ```bash olcrtc /etc/olcrtc/server.yaml +olcrtc /etc/olcrtc/client.yaml ``` -Примеры: +Готовые примеры: - [`server.example.yaml`](./server.example.yaml) - [`client.example.yaml`](./client.example.yaml) - [`failover.example.yaml`](./failover.example.yaml) -## Схема +## Схема -| YAML path | Значение | -|------------------------------------------------------------------|-----------------------------------------------------------| -| `mode` | `srv`, `cnc`, or `gen` | -| `link` | `direct` | -| `auth.provider` | `jitsi`, `telemost`, `wbstream`, `none` | -| `room.id` | conference room id | -| `crypto.key` / `crypto.key_file` | 64-char hex (32 bytes), inline or read from file | -| `net.transport` | `datachannel`, `videochannel`, `seichannel`, `vp8channel` | -| `net.dns` | resolver `host:port` | -| `socks.host` / `.port` | client-side listener | -| `socks.user` / `.pass` | optional client-side auth | -| `socks.proxy_addr` / `.proxy_port` | server-side egress proxy | -| `engine.name` / `.url` / `.token` | only when `auth.provider: none` | -| `video.*` | videochannel tuning | -| `vp8.*` | vp8channel tuning | -| `sei.fps` / `.batch_size` / `.fragment_size` / `.ack_timeout_ms` | seichannel tuning | -| `liveness.interval` | control-stream ping interval, default `10s` | -| `liveness.timeout` | pong timeout, default `5s` | -| `liveness.failures` | missed pongs before reconnect, default `3` | -| `lifecycle.max_session_duration` | planned session rebuild interval, e.g. `6h`; unset = off | -| `traffic.max_payload_size` | safe encrypted wire-message cap; `0` = transport default | -| `traffic.min_delay` / `.max_delay` | optional send pacing jitter, e.g. `5ms` / `30ms` | -| `gen.amount` | gen mode: number of rooms to create | -| `profiles[]` | ordered srv/cnc failover profiles | -| `failover.retry_delay` | delay before trying the next profile, e.g. `2s` | -| `failover.max_cycles` | stop after N full profile-list passes; `0` = forever | -| `data` | path to data directory | -| `debug` | verbose logging | -| `ffmpeg` | path to ffmpeg binary | +| YAML path | Значение | +|---|---| +| `mode` | `srv`, `cnc` или `gen` | +| `auth.provider` | `jitsi`, `telemost`, `wbstream`, `none` | +| `room.id` | ID/URL комнаты для выбранного auth-провайдера | +| `room.channel` | необязательный ID канала для peer-routing сценариев | +| `crypto.key` / `crypto.key_file` | общий ключ: 64 hex-символа, напрямую или из файла | +| `net.transport` | `datachannel`, `vp8channel`, `seichannel`, `videochannel` | +| `net.dns` | DNS resolver в формате `host:port` | +| `socks.host` / `socks.port` | локальный SOCKS5 listener в `mode: cnc` | +| `socks.user` / `socks.pass` | необязательная auth для входящих SOCKS5-подключений | +| `socks.proxy_addr` / `socks.proxy_port` | исходящий SOCKS5-прокси на серверной стороне | +| `engine.name` / `engine.url` / `engine.token` | прямой engine-режим, только при `auth.provider: none` | +| `video.*` | настройки `videochannel` | +| `vp8.*` | настройки `vp8channel` | +| `sei.*` | настройки `seichannel` | +| `liveness.interval` | интервал ping по control stream, по умолчанию `10s` | +| `liveness.timeout` | таймаут pong, по умолчанию `5s` | +| `liveness.failures` | сколько pong можно пропустить до rebuild, по умолчанию `3` | +| `lifecycle.max_session_duration` | плановый rebuild сессии, например `6h`; пусто = выключено | +| `traffic.max_payload_size` | лимит зашифрованного wire-message; `0` = лимит транспорта | +| `traffic.min_delay` / `traffic.max_delay` | необязательный pacing отправки, например `5ms` / `30ms` | +| `gen.amount` | режим `gen`: сколько комнат создать | +| `profiles[]` | список failover-профилей для `srv`/`cnc` | +| `failover.retry_delay` | пауза перед следующим профилем, например `2s` | +| `failover.max_cycles` | сколько полных проходов по профилям сделать; `0` = бесконечно | +| `data` | путь к директории с runtime-данными (`names`, `surnames`) | +| `debug` | подробное логирование | +| `ffmpeg` | путь к бинарнику ffmpeg для `videochannel` | -`mode: cnc` refuses non-loopback `socks.host` values unless both -`socks.user` and `socks.pass` are set. +`crypto.key_file` читается относительно YAML-файла. Нельзя одновременно задавать `crypto.key` и `crypto.key_file`. -`crypto.key_file` is resolved relative to the YAML file. Do not set it -together with `crypto.key`. +`mode: cnc` запрещает слушать не-loopback адрес (`0.0.0.0`, LAN IP и т.п.), если не заданы оба поля `socks.user` и `socks.pass`. + +## Обязательный минимум + +### Сервер + +```yaml +mode: srv +auth: + provider: jitsi +room: + id: "https://meet.small-dm.ru/myroom" +crypto: + key: "REPLACE_ME_WITH_64_HEX_CHARS" +net: + transport: datachannel + dns: "1.1.1.1:53" +data: data +``` + +### Клиент + +```yaml +mode: cnc +auth: + provider: jitsi +room: + id: "https://meet.small-dm.ru/myroom" +crypto: + key: "REPLACE_ME_WITH_64_HEX_CHARS" +net: + transport: datachannel + dns: "1.1.1.1:53" +socks: + host: "127.0.0.1" + port: 8808 +data: data +``` ## Liveness -After `CLIENT_HELLO` / `SERVER_WELCOME`, the first smux stream stays open as -an encrypted control stream. olcrtc now sends `CONTROL_PING` / `CONTROL_PONG` -messages over that stream to prove the real tunnel path still round-trips. -This detects states where a provider or WebRTC layer looks connected but the -encrypted smux path is no longer usable. +После `CLIENT_HELLO` / `SERVER_WELCOME` первый smux stream остаётся открытым как зашифрованный control stream. По нему `olcrtc` отправляет `CONTROL_PING` / `CONTROL_PONG`, чтобы проверять именно рабочий путь туннеля, а не только статус WebRTC-соединения. ```yaml liveness: @@ -66,34 +97,22 @@ liveness: failures: 3 ``` -When the failure threshold is reached, the current smux session is rebuilt. -In failover mode, a profile that exits after liveness-triggered reconnect -failure lets the supervisor advance to the next profile. +Когда порог пропущенных pong достигнут, текущая smux-сессия пересоздаётся. В failover-режиме профиль, который завершился после неудачного reconnect, отдаёт управление supervisor, и тот пробует следующий профиль. ## Lifecycle Rotation -`lifecycle.max_session_duration` sets a planned upper bound for one provider -call/session. When the duration expires, olcrtc cancels the active server or -client session and starts a fresh one with the same config. While this option -is enabled, clean session endings are also restarted so the peer that did not -fire the timer can follow the rebuild. This is useful for long-running -deployments where provider calls get stale, accumulate media state, or should -be periodically re-created. +`lifecycle.max_session_duration` задаёт плановый верхний предел длительности одного звонка/сессии у провайдера. Когда время истекает, активная `srv` или `cnc` сессия закрывается и запускается заново с тем же конфигом. ```yaml lifecycle: max_session_duration: 6h ``` -The field is optional and disabled when omitted. Values use Go duration syntax -such as `30m`, `2h`, or `6h`; zero and negative durations are rejected. +Поле необязательное. Формат - Go duration: `30m`, `2h`, `6h`. Ноль и отрицательные значения не принимаются. ## Traffic Shaping -`traffic` applies a shared reliability-oriented wrapper around the selected -transport. It can cap encrypted wire-message size and add small send pacing -delays without truncating data. When a payload would exceed the effective cap, -the send fails clearly instead of cutting bytes and corrupting smux. +`traffic` добавляет общий wrapper вокруг выбранного транспорта. Он может ограничить размер зашифрованного сообщения и добавить небольшую задержку перед отправкой. Данные не обрезаются: если payload не помещается в эффективный лимит, отправка завершается явной ошибкой. ```yaml traffic: @@ -102,23 +121,14 @@ traffic: max_delay: 30ms ``` -The wrapper clamps the configured payload cap to the selected transport's -advertised `MaxPayloadSize`. Client and server also reduce smux frame size to -fit the effective encrypted payload cap, accounting for crypto overhead. `0` -adds no extra cap beyond the selected transport's advertised limit. Delays use -Go duration syntax; if only `min_delay` is set, it is a fixed delay. Use the -same traffic settings on both peers. +Лимит сжимается до `MaxPayloadSize`, который заявляет выбранный транспорт. Клиент и сервер также уменьшают smux frame size с учётом crypto overhead. Значение `0` не добавляет лимит сверх лимита транспорта. Если задан только `min_delay`, задержка фиксированная. Используй одинаковые `traffic`-настройки на обеих сторонах. ## Failover Profiles -`mode: srv` and `mode: cnc` can define `profiles`. Top-level fields are used -as common defaults; each profile overrides only the fields it sets. The CLI -runs profiles in order. If a profile fails or ends while the process is still -alive, olcrtc waits `failover.retry_delay` and starts the next profile. +`mode: srv` и `mode: cnc` могут задавать `profiles`. Верхнеуровневые поля становятся общими defaults, а каждый профиль переопределяет только то, что указано внутри него. ```yaml mode: srv -link: direct crypto: key_file: ./olcrtc.key net: @@ -147,10 +157,26 @@ failover: max_cycles: 0 ``` -Both peers must use compatible profile order and room settings. This first -failover layer rebuilds the session on the next profile; active smux streams -do not migrate, but new connections can recover on the next profile. +Порядок профилей и параметры комнаты должны быть совместимы на сервере и клиенте. Активные smux streams между профилями не мигрируют; новые подключения смогут восстановиться на следующем профиле. -When `debug: true` is enabled, the CLI also emits a compact supervisor status -snapshot with the active profile, per-profile start/failure counters, and -bounded failover history size. +## mode: gen + +`gen` создаёт Room ID заранее и печатает их в stdout. Сейчас это полезно прежде всего для `wbstream`, потому что его auth-провайдер реализует создание комнат. + +```yaml +mode: gen +auth: + provider: wbstream +crypto: + key: "REPLACE_ME_WITH_64_HEX_CHARS" +net: + transport: vp8channel + dns: "1.1.1.1:53" +gen: + amount: 3 +data: data +``` + +```bash +olcrtc gen.yaml +``` diff --git a/docs/failover.example.yaml b/docs/failover.example.yaml index bf42482..223734e 100644 --- a/docs/failover.example.yaml +++ b/docs/failover.example.yaml @@ -1,8 +1,7 @@ -# olcrtc failover config example -# Use the same profile order on both peers. +# Пример failover-конфига olcrtc +# Используй одинаковый порядок профилей на обеих сторонах. mode: srv -link: direct crypto: key_file: "./olcrtc.key" @@ -15,11 +14,11 @@ liveness: timeout: 5s failures: 3 -# Optional planned rebuild for each active profile. +# Необязательный плановый rebuild для каждого активного профиля. # lifecycle: # max_session_duration: 6h -# Optional reliability shaping for encrypted wire messages. +# Необязательный лимит/pacing для зашифрованных wire-сообщений. # traffic: # max_payload_size: 4096 # min_delay: 5ms diff --git a/docs/fast.md b/docs/fast.md index a7c7216..f43fbce 100644 --- a/docs/fast.md +++ b/docs/fast.md @@ -93,11 +93,11 @@ cd olcrtc ### Auth (на каком сервисе передавать трафик) ``` -Select auth provider: +Выберите auth-провайдера: 1) jitsi 2) telemost 3) wbstream -Enter choice [1-3, default: 1]: +Введите номер [1-3, по умолчанию: 1]: ``` Выбери сервис. Полную матрицу совместимости смотри в [settings.md](settings.md). @@ -107,26 +107,26 @@ Enter choice [1-3, default: 1]: ### Transport (как именно передавать данные) ``` -Select transport: +Выберите транспорт: 1) datachannel 2) videochannel 3) seichannel 4) vp8channel -Enter choice [1-4, default: 1]: +Введите номер [1-4, по умолчанию: 1]: ``` Рекомендации: - **datachannel** - самый быстрый, минимальный пинг. Стабильно работает с `jitsi` через colibri-ws bridge channel. **WBStream DC не работает** в обычном guest flow (токены без `canPublishData`). **Telemost удалил DC**. - **vp8channel** - работает с telemost и wbstream, быстрый, но большой пинг. - **seichannel** - работает только с wbstream, медленный, но мелкий пинг. -- **videochannel** - работает с wbstream (стабильно) и telemost (best effort), самый медленный и большой пинг. +- **videochannel** - работает с wbstream стабильно, с telemost по возможности; самый медленный и с большим пингом. **Рекомендуемая комбинация: `jitsi + datachannel`** — работает стабильно, не требует регистрации, легко поднимать на своём сервере. Альтернатива: `wbstream + vp8channel`. ### Room ID ``` -Enter Room ID: +Введите Room ID: ``` Для **jitsi** — полный URL комнаты в формате `https://host/room` (например `https://meet.small-dm.ru/myroom`). Имя комнаты придумывается на лету, без регистрации. Подойдёт любой публичный или self-hosted Jitsi Meet. @@ -136,7 +136,7 @@ Enter Room ID: ### DNS ``` -DNS server [default: 8.8.8.8:53]: +DNS-сервер [по умолчанию: 8.8.8.8:53]: ``` Нажми Enter. Менять не нужно если нет причин, на всякий можно поставить 77.88.8.8 или DNS твоего провайдера. @@ -144,7 +144,7 @@ DNS server [default: 8.8.8.8:53]: ### SOCKS5 прокси для исходящего трафика ``` -Use SOCKS5 proxy for egress? (y/N): +Использовать SOCKS5-прокси для исходящего трафика? (y/N): ``` Если нет - просто Enter, если надо то введи `y`. Нужно чтобы сервер сам ходил через прокси. @@ -152,10 +152,10 @@ Use SOCKS5 proxy for egress? (y/N): ### Параметры транспорта (только для videochannel) ``` -Video codec: +Видео-кодек: 1) qrcode - 2) tile (requires 1080x1080) -Enter choice [1-2, default: 1]: + 2) tile (требует 1080x1080) +Введите номер [1-2, по умолчанию: 1]: ``` Выбери кодек: @@ -165,57 +165,57 @@ Enter choice [1-2, default: 1]: #### qrcode ``` -Video width [default: 1920]: -Video height [default: 1080]: -QR error correction (low/medium/high/highest) [default: low]: -QR fragment size bytes [default: 0 (auto)]: +Ширина видео [по умолчанию: 1920]: +Высота видео [по умолчанию: 1080]: +Коррекция ошибок QR (low/medium/high/highest) [по умолчанию: low]: +Размер QR-фрагмента в байтах [по умолчанию: 0 (авто)]: ``` -- **Video width / height** - разрешение видео. Больше = больше данных за кадр, но тяжелее поток. -- **QR error correction** - коррекция ошибок: `low` быстрее, `highest` надёжнее при плохом канале. -- **QR fragment size** - размер фрагмента в байтах. `0` = автоматически. +- **Ширина / высота видео** - разрешение видео. Больше = больше данных за кадр, но тяжелее поток. +- **Коррекция ошибок QR** - `low` быстрее, `highest` надёжнее при плохом канале. +- **Размер QR-фрагмента** - размер фрагмента в байтах. `0` = автоматически. #### tile ``` -[*] Tile codec selected - forcing 1080x1080 -Tile module size in pixels 1..270 [default: 4]: -Tile Reed-Solomon parity percent 0..200 [default: 20]: +[*] Выбран tile-кодек, принудительно выставляю 1080x1080 +Размер tile-модуля в пикселях 1..270 [по умолчанию: 4]: +Процент Reed-Solomon parity для tile 0..200 [по умолчанию: 20]: ``` -- **Tile module size** - размер одного тайла в пикселях. Меньше = больше данных за кадр. +- **Размер tile-модуля** - размер одного тайла в пикселях. Меньше = больше данных за кадр. - **Tile Reed-Solomon parity** - процент избыточности. `0` = без коррекции, `20` оптимально. #### Общие параметры (для обоих кодеков) ``` -Video FPS [default: 30]: -Video bitrate [default: 2M]: -Hardware acceleration (none/nvenc) [default: none]: +FPS видео [по умолчанию: 30]: +Битрейт видео [по умолчанию: 2M]: +Аппаратное ускорение (none/nvenc) [по умолчанию: none]: ``` -- **Video FPS** - кадров в секунду. Больше FPS = выше пропускная способность, больше нагрузка на CPU. -- **Video bitrate** - битрейт ffmpeg. Примеры: `2M`, `5M`, `500K`. -- **Hardware acceleration** - `none` если нет GPU, `nvenc` для NVIDIA GPU. +- **FPS видео** - кадров в секунду. Больше FPS = выше пропускная способность, больше нагрузка на CPU. +- **Битрейт видео** - битрейт ffmpeg. Примеры: `2M`, `5M`, `500K`. +- **Аппаратное ускорение** - `none` если нет GPU, `nvenc` для NVIDIA GPU. --- ### Параметры транспорта (только для vp8channel) ``` -VP8 FPS [default: 60]: 60 -VP8 batch size (frames per tick) [default: 64]: 64 +VP8 FPS [по умолчанию: 60]: +VP8 batch size (кадров за тик) [по умолчанию: 64]: ``` -Введи `60` и `64` - это оптимальные значения. +Нажми Enter, если устраивают значения по умолчанию `60` и `64`. ### Параметры транспорта (только для seichannel) ``` -SEI FPS [default: 20]: 60 -SEI batch size (frames per tick) [default: 1]: 64 -SEI fragment size in bytes [default: 900]: 900 -SEI ACK timeout in milliseconds [default: 3000]: 2000 +SEI FPS [по умолчанию: 60]: +SEI batch size (кадров за тик) [по умолчанию: 64]: +Размер SEI-фрагмента в байтах [по умолчанию: 900]: +SEI ACK timeout в миллисекундах [по умолчанию: 2000]: ``` Нажми Enter для всех - значения по умолчанию оптимальны. @@ -227,16 +227,16 @@ SEI ACK timeout in milliseconds [default: 3000]: 2000 После запуска скрипт выведет: ``` -[+] Server started successfully! +[+] Сервер успешно запущен! -Container name: olcrtc-server -Auth: wbstream -Transport: datachannel -Room ID: abc123xyz -Encryption key: d823fa01cb3e0609b67322f7cf984c4ee2e4ce2e294936fc24ef38c9e59f4799 +Имя контейнера: olcrtc-server +Auth: wbstream +Transport: datachannel +Room ID: abc123xyz +Ключ шифрования: d823fa01cb3e0609b67322f7cf984c4ee2e294936fc24ef38c9e59f4799 ``` -**Сохрани Room ID и Encryption key** - они нужны для клиента. +**Сохрани Room ID и ключ шифрования** - они нужны для клиента. --- @@ -255,7 +255,7 @@ cd olcrtc Когда спросит ключ: ``` -Enter Encryption Key (hex): Encryption key +Введите ключ шифрования (hex): ``` Вставь ключ с сервера. @@ -263,8 +263,8 @@ Enter Encryption Key (hex): Encryption key ### SOCKS5 адрес и порт ``` -SOCKS5 ip [default: 127.0.0.1]: -SOCKS5 port [default: 8808]: +SOCKS5 IP [по умолчанию: 127.0.0.1]: +SOCKS5 порт [по умолчанию: 8808]: ``` Нажми Enter оба раза. Прокси поднимется на `127.0.0.1:8808`. @@ -272,7 +272,7 @@ SOCKS5 port [default: 8808]: ### SOCKS5 аутентификация (необязательно) ``` -SOCKS5 username (leave empty to disable auth): +SOCKS5 логин (оставь пустым, чтобы отключить auth): ``` Если нужна защита логином и паролем - введи логин, затем пароль. Если нет - просто Enter, аутентификация будет отключена. @@ -280,9 +280,9 @@ SOCKS5 username (leave empty to disable auth): ### Результат ``` -[+] Client started successfully! +[+] Клиент успешно запущен! -Container name: olcrtc-client +Имя контейнера: olcrtc-client SOCKS5 proxy: 127.0.0.1:8808 ``` diff --git a/docs/manual.md b/docs/manual.md index 46f13c3..027edf2 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -143,7 +143,7 @@ openssl rand -hex 32 ## Шаг 7: Запустить сервер -На серверной машине (VPS и т.д.). Подбери нужную комбинацию carrier + transport из матрицы в [settings.md](settings.md). +На серверной машине (VPS и т.д.). Подбери нужную комбинацию auth provider + transport из матрицы в [settings.md](settings.md). ### jitsi + datachannel (рекомендуется) @@ -154,7 +154,6 @@ openssl rand -hex 32 ```yaml # server.yaml mode: srv -link: direct auth: provider: jitsi room: @@ -186,7 +185,6 @@ data: data ```yaml # server.yaml mode: srv -link: direct auth: provider: wbstream room: @@ -223,14 +221,13 @@ Room ID нужно передать клиенту. ## Шаг 8: Запустить клиент -На своей машине. Auth provider, transport, room ID и key должны совпадать с сервером. +На своей машине. `auth.provider`, `net.transport`, `room.id` и `crypto.key` должны совпадать с сервером. ### jitsi + datachannel (рекомендуется) ```yaml # client.yaml mode: cnc -link: direct auth: provider: jitsi room: @@ -257,7 +254,6 @@ data: data ```yaml # client.yaml mode: cnc -link: direct auth: provider: wbstream room: @@ -288,7 +284,6 @@ SOCKS5 server listening on 127.0.0.1:8808 ```yaml # client.yaml mode: cnc -link: direct auth: provider: wbstream room: diff --git a/docs/project-map.md b/docs/project-map.md deleted file mode 100644 index a85d1f4..0000000 --- a/docs/project-map.md +++ /dev/null @@ -1,422 +0,0 @@ -# olcRTC Project Map - -This is a developer map for finding the useful parts of the project quickly. -It focuses on code ownership, runtime flow, extension points, and areas that -are worth deeper work. - -## One-Sentence Model - -olcRTC is an encrypted TCP-over-WebRTC tunnel: the client exposes a local -SOCKS5 listener, the server dials requested TCP targets, and both sides carry -the smux byte stream through a selected WebRTC carrier and transport. - -## Runtime Stack - -```text -YAML config - -> cmd/olcrtc - -> internal/config - -> internal/app/session - -> internal/server or internal/client - -> internal/link/direct - -> internal/transport/{datachannel,vp8channel,seichannel,videochannel} - -> internal/carrier/builtin - -> internal/auth/ + internal/engine/ - -> external service SFU / signaling -``` - -Tunnel data path: - -```text -local app - -> client SOCKS5 - -> smux stream - -> muxconn AEAD encrypt - -> link.Send - -> transport encoding - -> carrier/engine - -> SFU/service - -> peer engine/carrier - -> transport decoding - -> muxconn AEAD decrypt - -> smux stream - -> server TCP dial - -> target host -``` - -## Entrypoints - -| Path | Purpose | -|---|---| -| `cmd/olcrtc/main.go` | Main CLI. Accepts one YAML file, applies auth and transport defaults, starts `srv`, `cnc`, or `gen`. | -| `cmd/olcrtc-cgo/main.go` | Small c-shared entrypoint for desktop/native consumers. | -| `pkg/olcrtc` | Embeddable lower-level API that returns a `net.Conn`-like handle over an engine data path. | -| `pkg/olcrtc/tunnel` | Embeddable server-side tunnel API with auth and traffic hooks. | -| `mobile/mobile.go` | gomobile API for Android clients, including VPN socket protection. | -| `script/srv.sh`, `script/cnc.sh` | Interactive shell launchers that generate YAML and run/build the app. | -| `Dockerfile`, `script/docker/*` | Container build and server entrypoint/healthcheck. | - -## Config And Session Layer - -`internal/config` owns YAML parsing and file-backed secret loading. - -Important fields: - -| YAML | Runtime field | Notes | -|---|---|---| -| `mode` | `session.Config.Mode` | `srv`, `cnc`, or `gen`. | -| `auth.provider` | `Auth` | `jitsi`, `telemost`, `wbstream`, or `none`. | -| `room.id` | `RoomID` | Carrier-specific room reference. | -| `crypto.key` / `crypto.key_file` | `KeyHex` | Shared 32-byte key encoded as 64 hex chars. | -| `net.transport` | `Transport` | `datachannel`, `vp8channel`, `seichannel`, or `videochannel`. | -| `net.dns` | `DNSServer` | Resolver used by server-side target dials and provider HTTP where wired. | -| `socks.*` | SOCKS fields | Client listener and optional server egress proxy. | -| `engine.*` | direct engine fields | Used only with `auth.provider: none`. | -| `liveness.*` | control liveness | Ping/pong interval, timeout, and missed-pong threshold. | -| `lifecycle.*` | session lifecycle | Planned call/session rotation. | -| `traffic.*` | send shaping | Encrypted wire-message size cap and optional pacing jitter. | - -`internal/app/session` is the main router: - -1. Registers built-ins via `RegisterDefaults`. -2. Applies auth defaults: auth provider decides engine and default service URL. -3. Applies transport defaults: documented defaults for `vp8`, `sei`, and `video`. -4. Validates mode, auth, link, transport, room, key, DNS, transport options, and SOCKS listener safety. -5. Runs `server.Run`, `client.Run`, or `Gen`. - -## Server Side - -`internal/server` accepts encrypted smux sessions from the peer and proxies -each smux stream to a TCP target. - -Core pieces: - -| Symbol | Role | -|---|---| -| `server.Run` | Creates cipher, link, smux server, and serve loop. | -| `bringUpLink` | Builds `link.Link`, wires reconnect callbacks, connects carrier. | -| `installSession` / `reinstallSession` | Creates or replaces `muxconn + smux.Session`. | -| `acceptHandshake` | First smux stream; runs `handshake.Server`. | -| `handleStream` | Reads connect JSON and dispatches a tunnel stream. | -| `dispatch` | Dials target, sends ready byte, copies both directions. | -| `AuthHook` | Embedders can authorize clients after `CLIENT_HELLO`. | -| `OnSessionOpen`, `OnSessionClose`, `OnTraffic` | Observability hooks. | - -Server risk areas: - -- Target dialing is powerful by design. Any real product wrapper should add - an `AuthHook` and probably destination policy. -- `defaultAuthHook` admits everyone who knows the room and key. -- Reconnect rebuilds smux sessions; active streams are sacrificed. - -## Client Side - -`internal/client` exposes a local SOCKS5 listener and opens one smux stream -per SOCKS CONNECT request. - -Core pieces: - -| Symbol | Role | -|---|---| -| `RunWithReady` | Starts link, opens smux client, listens on local SOCKS. | -| `openControlStream` | First smux stream; runs `handshake.Client`. | -| `handleSocks5` | SOCKS method negotiation and CONNECT parsing. | -| `sendConnectRequest` | Sends server-side target JSON and waits for ready byte. | -| `handleReconnect` | Rebuilds smux and control stream after carrier reconnect. | -| `resolveDeviceID` | Optional persistent client identity for hooks. | - -Client risk areas: - -- A non-loopback SOCKS listener must require `socks.user` and `socks.pass`. -- SOCKS credentials are simple static credentials, not a full account system. -- Existing streams do not survive reconnect; new SOCKS connections can recover. - -## Wire Protocol Above WebRTC - -`internal/muxconn` adapts `link.Link` to `io.ReadWriteCloser`. - -- Every smux write is encrypted with `internal/crypto`. -- Every inbound link message is decrypted and appended to an internal byte buffer. -- Bad AEAD frames are dropped. -- `CanSend` provides backpressure before encrypting and sending. - -`internal/crypto` uses XChaCha20-Poly1305 with a random nonce prepended to -each ciphertext. - -`internal/handshake` runs on the first smux stream: - -```text -CLIENT_HELLO { version, device_id, claims } -SERVER_WELCOME { version, session_id } -or -SERVER_REJECT { version, reason } -``` - -The handshake has a 64 KiB frame cap and a default 15 second timeout. - -After handshake, `internal/control` keeps that same encrypted smux stream open -and exchanges length-prefixed JSON control messages: - -```text -CONTROL_PING { version, seq, sent_unix_nano } -CONTROL_PONG { version, seq, sent_unix_nano } -``` - -Defaults are `liveness.interval: 10s`, `liveness.timeout: 5s`, and -`liveness.failures: 3`. Missed pongs mark the smux session unhealthy and -trigger a session rebuild/reconnect path. - -Client and server runtimes also maintain a `control.Status` snapshot with -session ID, last pong time, RTT, missed pongs, reconnect count, and unhealthy -event count. Embedders can consume it through the client/server health -callbacks. - -## Registries And Plugin Shape - -The universal-carrier refactor centers on small registries: - -| Registry | Package | Registers | -|---|---|---| -| Auth providers | `internal/auth` | Service-specific credential and room creation flows. | -| Engines | `internal/engine` | Wire-level SFU protocol implementations. | -| Carriers | `internal/carrier` | Auth + engine adapters exposed as byte/video capability providers. | -| Transports | `internal/transport` | Byte transport strategy over carrier primitives. | -| Links | `internal/link` | Higher-level link abstraction; currently only `direct`. | - -`internal/carrier/builtin` connects the auth and engine worlds: - -```text -carrier "wbstream" -> auth/wbstream -> engine/livekit -carrier "telemost"-> auth/telemost -> engine/goolom -carrier "jitsi" -> auth/jitsi -> engine/jitsi -carrier "none" -> direct user-supplied engine/url/token -``` - -## Auth Providers - -| Provider | Engine | Room generation | Notes | -|---|---|---:|---| -| `jitsi` | `jitsi` | No | Parses host/room from a public or self-hosted Jitsi URL. No HTTP auth. | -| `telemost` | `goolom` | No | Calls Telemost room-info flow and returns Goolom credentials. | -| `wbstream` | `livekit` | Yes | Registers guest, optionally creates room, joins room, fetches LiveKit token. | -| `none` | chosen by config | No | Direct engine mode for downstream tools or self-hosted SFUs. | - -## Engines - -Engines expose the low-level service/SFU protocol. - -| Engine | Package | Byte stream | Video track | Main job | -|---|---|---:|---:|---| -| `livekit` | `internal/engine/livekit` | Yes | Yes | LiveKit SDK room, data packets, local/remote tracks, reconnect with credential refresh. | -| `goolom` | `internal/engine/goolom` | Yes | Yes | Yandex Telemost/Goolom signaling, split publisher/subscriber peer connections, telemetry/keepalive. | -| `jitsi` | `internal/engine/jitsi` | Yes | Best effort | Jitsi MUC/Jingle/colibri-ws plus optional video track negotiation. | - -Engine work is where most provider breakage and reconnect complexity lives. - -## Transports - -Transports decide how raw tunnel bytes are carried once the carrier provides -either a byte stream or a video track. - -| Transport | Primitive | Reliability model | Best fit | Notes | -|---|---|---|---|---| -| `datachannel` | Carrier byte stream | Native reliable ordered messages | Jitsi and direct engines | Simple pass-through with 12 KiB message cap. | -| `vp8channel` | VP8 video track | KCP over VP8-looking frames | WB Stream and Telemost-style video paths | Highest-performance video-path transport. Uses epochs and binding tokens to survive restarts/loopback. | -| `seichannel` | H264 SEI video track | Custom fragments + ACK/retry | WB Stream fallback | Carries data in SEI NAL units with fragmentation, CRC, ACK. | -| `videochannel` | Visual frames via ffmpeg | QR/tile frames + ACK/retry | Experimental/inspection-friendly path | Encodes visual payload frames, requires ffmpeg, supports QR and tile codecs. | - -Transport work is where throughput, loss recovery, and adaptive tuning should -happen. - -## Public/Embedding Surfaces - -| Package | User | -|---|---| -| `pkg/olcrtc` | Go programs that want a `net.Conn` over a selected auth/engine. | -| `pkg/olcrtc/tunnel` | Go programs that want to embed the server-side tunnel with auth/traffic hooks. | -| `mobile` | Android app bindings. Wraps client mode, VPN socket protection, logging, simple health checks. | -| `cmd/olcrtc-cgo` | Native desktop/client integrations using c-shared Go export. | - -These surfaces are important if the CLI becomes only one frontend among many. - -## Tests - -The project has broad unit coverage: - -- Config/session validation and defaults. -- Auth provider HTTP flows with test servers. -- Engine helper logic and reconnect paths. -- SOCKS parsing, smux handshake, server dispatch. -- Crypto, muxconn, names, protect, logging. -- Transport frame codecs, ACK paths, KCP loopback, ffmpeg helpers. -- Memory-backed E2E tunnel tests and optional real-provider E2E matrix. - -Useful commands: - -```sh -go test -count=1 ./... -go test -race -count=1 ./cmd/olcrtc ./internal/app/session ./internal/config ./internal/engine/livekit -go test -race -count=1 -v ./internal/e2e -E2E_CARRIERS=wbstream E2E_TRANSPORTS=vp8channel mage e2e -go build -trimpath -o build/olcrtc ./cmd/olcrtc -``` - -## High-Value Coding Areas - -### 1. Supervisor And Multi-Profile Failover - -The first supervisor layer exists in `internal/supervisor`: the CLI can run a -prioritized list of carrier/transport profiles and move to the next profile -when the active one fails or ends. - -```yaml -mode: srv -link: direct -crypto: - key_file: ./olcrtc.key -net: - dns: "1.1.1.1:53" -profiles: - - name: wb-vp8 - auth: - provider: wbstream - room: - id: WB_ROOM_ID - net: - transport: vp8channel - - name: jitsi-dc - auth: - provider: jitsi - room: - id: https://meet.example.org/olcrtc-room - net: - transport: datachannel -failover: - retry_delay: 2s - max_cycles: 0 -``` - -Implemented: - -- Config schema for `profiles[]`. -- Ordered supervisor loop. -- `failover.retry_delay`. -- `failover.max_cycles`. -- Profile start/end logs. -- Planned session rotation with `lifecycle.max_session_duration`. -- Shared supervisor status snapshots with bounded failover history. -- Shared traffic wrapper with payload cap, pacing jitter, and smux frame sizing. - -Still valuable: - -- Health scoring per profile. -- Control-stream coordination before switching. -- Stream draining and migration instead of dropping active smux streams. -- User-facing status endpoint/export for the active profile and failover history. - -Likely files: - -- `internal/config/config.go` -- `internal/app/session/session.go` -- `internal/supervisor` -- `internal/server` -- `internal/client` -- `docs/configuration.md` -- `internal/e2e/tunnel_test.go` - -### 2. Transport Telemetry And Adaptive Tuning - -Add metrics from transport to link/session: - -- Send queue depth. -- ACK latency. -- Retries. -- Reconnect count. -- Dropped/decrypt-failed frames. -- KCP RTT/loss where available. - -Then make `vp8.batch_size`, `sei.fragment_size`, ACK timeout, and pacing -adaptive instead of static YAML knobs. - -### 3. Control Stream Protocol - -The first smux stream now carries control ping/pong after handshake. It is -still the natural place for: - -- Server policy updates. -- Graceful reconnect notifications. -- Drain/start markers for failover. -- More per-session stats. - -Likely files: - -- `internal/control` -- `internal/server` -- `internal/client` - -### 4. Destination Policy And Real Auth - -The tunnel can dial arbitrary server-side TCP targets. A production wrapper -should use `AuthHook` and enforce: - -- Allowed destination CIDRs/domains/ports. -- Per-device or per-plan policy. -- Session expiration. -- Traffic accounting limits. -- Sanitized rejection reasons. - -This mostly belongs in `pkg/olcrtc/tunnel` and `internal/server`. - -### 5. Provider Hardening - -Provider APIs can drift. Worth adding: - -- Central protected HTTP/WebSocket client creation with TLS 1.2+, - environment proxy support, HTTP/2 for HTTP, and bounded timeouts. -- Better typed errors from auth providers. -- Provider health probes. -- Fixture-based contract tests for API response changes. -- Per-provider rate/backoff policy. -- Safer secret/log redaction. - -Likely files: - -- `internal/auth/*` -- `internal/engine/*` -- `internal/carrier/builtin` - -### 6. Codebase Hygiene - -Some public-facing text and comments are not suitable for a serious external -project. Cleaning that up would improve maintainability and downstream trust. -The most obvious targets are top-level docs and a large hostile block comment -in `internal/transport/vp8channel/transport.go`. - -## Where To Look First - -| Goal | Start here | -|---|---| -| Change YAML schema | `internal/config/config.go`, `cmd/olcrtc/main.go`, docs examples. | -| Change validation/defaults | `internal/app/session/session.go`. | -| Add a new auth provider | `internal/auth`, then register in `internal/carrier/builtin/register.go`. | -| Add a new SFU protocol | `internal/engine`, then connect through auth/carrier. | -| Add a new byte transport | `internal/transport`, then register in `session.RegisterDefaults`. | -| Add link behavior above transports | `internal/link`; currently only `direct`. | -| Improve SOCKS behavior | `internal/client`. | -| Improve server target dialing or policy | `internal/server`, `pkg/olcrtc/tunnel`. | -| Improve reconnect | Engines first, then `internal/client` and `internal/server` smux rebuild behavior. | -| Improve Android app integration | `mobile`, `internal/protect`, `client.RunWithReady`. | - -## Mental Model For Big Changes - -Prefer to keep the layer boundaries: - -- Auth creates credentials; it should not know transport details. -- Engine speaks service/SFU protocol; it should not know SOCKS or smux. -- Carrier adapts auth+engine into byte/video capabilities. -- Transport turns byte/video capabilities into reliable-ish tunnel bytes. -- Link is policy above transport. -- Client/server own SOCKS, smux, handshake, target dialing, and session hooks. - -If a change crosses more than two layers, it probably deserves a new -orchestrator package instead of pushing more state into an engine or transport. diff --git a/docs/server.example.yaml b/docs/server.example.yaml index b57698d..716f406 100644 --- a/docs/server.example.yaml +++ b/docs/server.example.yaml @@ -1,22 +1,19 @@ -# olcrtc server config example -# Run with: olcrtc server.yaml +# Пример серверного конфига olcrtc +# Запуск: olcrtc server.yaml mode: srv -# Connection topology -link: direct # p2p link type - auth: provider: jitsi # jitsi | telemost | wbstream | none -# For jitsi: full conference URL (https://host/room or host/room). -# For telemost / wbstream: room ID returned by the service. +# Для jitsi: полный URL комнаты (https://host/room или host/room). +# Для telemost / wbstream: Room ID, который вернул сервис. room: id: "https://meet.small-dm.ru/REPLACE_WITH_ROOM_NAME" crypto: - # 32-byte hex (64 chars). Generate with: openssl rand -hex 32 - # Or use key_file: "./olcrtc.key" to keep the secret out of this file. + # 32 байта в hex (64 символа). Сгенерировать: openssl rand -hex 32 + # Можно использовать key_file: "./olcrtc.key", чтобы не хранить секрет прямо здесь. key: "REPLACE_ME_WITH_64_HEX_CHARS" net: @@ -28,52 +25,52 @@ liveness: timeout: 5s failures: 3 -# Optional planned rebuild for long-running calls. +# Необязательный плановый rebuild долгих звонков. # lifecycle: # max_session_duration: 6h -# Optional reliability shaping for encrypted wire messages. +# Необязательный лимит/pacing для зашифрованных wire-сообщений. # traffic: # max_payload_size: 4096 # min_delay: 5ms # max_delay: 30ms -# Outbound SOCKS5 proxy for server-side egress (optional) +# Исходящий SOCKS5-прокси на серверной стороне (необязательно). socks: - proxy_addr: "" # e.g. "127.0.0.1" - proxy_port: 0 # e.g. 1080 + proxy_addr: "" # например "127.0.0.1" + proxy_port: 0 # например 1080 -# Direct engine mode — only used when auth.provider is "none" +# Прямой engine-режим: используется только при auth.provider: none. engine: name: "" # livekit | goolom | jitsi url: "" token: "" -# vp8channel tuning (only when net.transport == vp8channel) +# Настройки vp8channel (только когда net.transport == vp8channel). vp8: fps: 60 batch_size: 64 -# seichannel tuning (only when net.transport == seichannel) +# Настройки seichannel (только когда net.transport == seichannel). sei: - fps: 20 - batch_size: 1 + fps: 60 + batch_size: 64 fragment_size: 900 - ack_timeout_ms: 3000 + ack_timeout_ms: 2000 -# videochannel tuning (only when net.transport == videochannel) +# Настройки videochannel (только когда net.transport == videochannel). video: width: 1920 height: 1080 fps: 30 bitrate: "2M" hw: none # none | nvenc - codec: qrcode # qrcode | tile (tile requires 1080x1080) - qr_size: 0 # 0 = auto + codec: qrcode # qrcode | tile (для tile нужно 1080x1080) + qr_size: 0 # 0 = авто qr_recovery: low # low (7%) | medium (15%) | high (25%) | highest (30%) - tile_module: 4 # 1..270, only for codec: tile - tile_rs: 20 # 0..200, only for codec: tile + tile_module: 4 # 1..270, только для codec: tile + tile_rs: 20 # 0..200, только для codec: tile -data: data # data directory (names files etc.) +data: data # директория с runtime-данными (names, surnames) debug: false -ffmpeg: ffmpeg # path to ffmpeg binary (only used by videochannel) +ffmpeg: ffmpeg # путь к ffmpeg, нужен только videochannel diff --git a/docs/settings.md b/docs/settings.md index c855750..cfd4fac 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -43,11 +43,10 @@ | YAML поле | Что вводить | |-----------|-------------| | `mode` | `srv` на сервере, `cnc` на клиенте, `gen` для генерации Room ID | -| `auth.provider` | `telemost`, `wbstream` или `jitsi` | +| `auth.provider` | `telemost`, `wbstream`, `jitsi` или `none` | | `net.transport` | `datachannel`, `vp8channel`, `seichannel` или `videochannel` | | `room.id` | Room ID | | `crypto.key` или `crypto.key_file` | Ключ шифрования hex 64 символа. Генерация: `openssl rand -hex 32` | -| `link` | Всегда `direct` | | `data` | Всегда `data` | | `net.dns` | DNS-сервер, например `1.1.1.1:53` | @@ -216,7 +215,6 @@ WB Stream DataChannel **не работает** в обычном guest flow — # server.yaml mode: srv -link: direct auth: provider: wbstream room: @@ -232,7 +230,6 @@ data: data ```yaml # client.yaml mode: cnc -link: direct auth: provider: wbstream room: @@ -253,7 +250,6 @@ data: data ```yaml # client.yaml с логином и паролем на прокси mode: cnc -link: direct auth: provider: wbstream room: @@ -285,7 +281,6 @@ export all_proxy=socks5h://myuser:mypass@127.0.0.1:8808 ```yaml # server.yaml mode: srv -link: direct auth: provider: telemost room: @@ -304,7 +299,6 @@ data: data ```yaml # client.yaml mode: cnc -link: direct auth: provider: telemost room: @@ -330,7 +324,6 @@ data: data ```yaml # server.yaml mode: srv -link: direct auth: provider: telemost room: @@ -351,7 +344,6 @@ data: data ```yaml # client.yaml mode: cnc -link: direct auth: provider: telemost room: @@ -377,7 +369,6 @@ data: data ```yaml # server.yaml mode: srv -link: direct auth: provider: telemost room: @@ -400,7 +391,6 @@ data: data ```yaml # client.yaml mode: cnc -link: direct auth: provider: telemost room: diff --git a/docs/uri.md b/docs/uri.md index 1fdb50e..ebc8ae2 100644 --- a/docs/uri.md +++ b/docs/uri.md @@ -92,7 +92,7 @@ Payload не используется. | `` | `crypto.key` | | `` | В `olcrtc` не передаётся. Это только клиентский комментарий | -`link: direct` и `data: data` в этом формате не кодируются, потому что для текущих сценариев они фиксированные. +`data: data` в этом формате не кодируется, потому что это локальная runtime-настройка конкретного запуска. --- @@ -125,7 +125,6 @@ Payload не нужен - datachannel параметров не имеет. Дл ```yaml mode: cnc -link: direct auth: provider: wbstream room: @@ -147,7 +146,6 @@ olcrtc://wbstream?vp8channel@room-01#d823fa01cb3e0609b6 ```yaml mode: cnc -link: direct auth: provider: wbstream room: @@ -172,7 +170,6 @@ olcrtc://wbstream?seichannel@room-01#d823f ```yaml mode: cnc -link: direct auth: provider: wbstream room: @@ -199,7 +196,6 @@ olcrtc://telemost?videochannel