54 KiB
olcRTC - полная документация
olcRTC (OpenLibreCommunity RTC) - инструмент обхода интернет-блокировок через паразитирование на легальных WebRTC-сервисах видеозвонков, уже находящихся в российских белых списках.
Проект: github.com/openlibrecommunity/olcrtc Лицензия: WTFPL Статус: Beta
Содержание
- Почему olcRTC существует
- Идея и история создания
- Как это работает
- Архитектура
- Структура репозитория
- Carriers - провайдеры
- Transports - транспорты
- Шифрование
- Мультиплексирование
- SOCKS5 прокси
- Mobile / Android
- Python PoC скрипты
- Сборка и деплой
- YAML конфигурация
- URI-формат и подписки
- Матрица совместимости
- CI/CD
- Что планируется сделать - Issues
- Контрибуторы
- Частые ошибки
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)
Для ТСПУ это выглядит как обычный видеозвонок.
2. Идея и история создания
Хронология
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 DataChannelvcsend.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)
▼
Интернет
Клиент (cnc) поднимает локальный SOCKS5. Любой браузер или приложение подключается к нему как к обычному прокси. Трафик мультиплексируется через smux, шифруется ChaCha20-Poly1305 и передаётся через выбранный транспорт поверх WebRTC SFU.
Сервер (srv) стоит на вашем VPS. Он подключается к той же комнате видеозвонка, получает зашифрованный поток и от своего имени делает TCP соединения к нужным адресам в интернете.
ТСПУ видит трафик к IP выбранного сервиса с корректным TLS и SNI - ничем не отличается от обычного видеозвонка.
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 тесты на реальных провайдерах
5. Структура репозитория
Корень
| Файл/папка | Что это |
|---|---|
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 |
cmd/olcrtc/
| Файл | Что делает |
|---|---|
main.go |
Точка входа. Загружает YAML конфиг (olcrtc config.yaml), настраивает логирование, подавляет шум LiveKit/pion в не-debug режиме, запускает session.Run или session.Gen. Graceful shutdown по SIGTERM/SIGINT с 5-секундным таймаутом |
main_test.go |
Юнит-тесты CLI: валидация конфига, режимы, edge cases |
cmd/olcrtc-cgo/
| Файл | Что делает |
|---|---|
main.go |
C-shared библиотека. Экспортирует функцию Ping() для десктопных клиентов: запускает короткоживущий olcRTC клиент, ждёт SOCKS listener, делает HTTP ping через него и возвращает latency в миллисекундах. Используется для проверки связности из нативных приложений |
internal/app/session/
| Файл | Что делает |
|---|---|
session.go |
Главная точка конфигурации. RegisterDefaults() регистрирует все carriers, links, transports. Validate() проверяет все настройки. Run() роутит в server.Run или client.Run. Gen() генерирует Room ID для auth-провайдеров с RoomCreator и ретраями |
session_test.go |
Тесты валидации конфига |
internal/config/
| Файл | Что делает |
|---|---|
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 |
Тесты загрузки и маппинга |
internal/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/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=<name> (сменить ветку), --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://<Auth>?<Transport>@<RoomID>#<Key>$<MIMO> |
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
Использование:
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
API:
Start(carrier, roomID, keyHex string)- запустить туннельStop()- остановитьIsRunning() boolSetProtector(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- DataChannelwbstream_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 таргеты
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)
git clone https://github.com/openlibrecommunity/olcrtc --recurse-submodules
cd olcrtc
# на сервере (VPS):
./script/srv.sh
# на клиенте:
./script/cnc.sh
Мануальный запуск
# генерация ключа
openssl rand -hex 32
# создать конфиг (пример: wbstream + vp8channel)
cat > server.yaml <<EOF
mode: srv
link: direct
auth:
provider: wbstream
room:
id: "ROOM_ID_HERE"
crypto:
key: "REPLACE_WITH_64_HEX"
net:
transport: vp8channel
dns: "1.1.1.1:53"
data: data
EOF
# запустить сервер
./olcrtc server.yaml
# конфиг клиента
cat > client.yaml <<EOF
mode: cnc
link: direct
auth:
provider: wbstream
room:
id: "ROOM_ID_HERE"
crypto:
key: "REPLACE_WITH_64_HEX"
net:
transport: vp8channel
dns: "1.1.1.1:53"
socks:
host: "127.0.0.1"
port: 8808
data: data
EOF
# запустить клиент
./olcrtc client.yaml
Docker
# Mount your YAML config into the container:
docker run -v ./server.yaml:/etc/olcrtc/server.yaml \
olcrtc/server:local /etc/olcrtc/server.yaml
14. YAML конфигурация
olcrtc читает конфигурацию из единственного YAML-файла. CLI-флагов нет.
olcrtc config.yaml
Полная схема описана в configuration.md. Примеры:
Основные поля
| YAML путь | Описание |
|---|---|
mode |
srv - сервер, cnc - клиент, gen - генерация Room ID |
link |
Всегда direct |
auth.provider |
telemost, wbstream, jitsi или none |
room.id |
Room ID |
crypto.key |
Ключ шифрования hex 64 символа |
net.transport |
datachannel, vp8channel, seichannel, videochannel |
net.dns |
DNS сервер, например 1.1.1.1:53 |
data |
Путь к директории данных |
debug |
Verbose логи |
Только клиент (mode: cnc)
| YAML путь | Описание |
|---|---|
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.
15. URI-формат и подписки
URI формат
Соглашение для клиентских приложений. Сам olcrtc не парсит - используется в сторонних клиентах.
olcrtc://<Auth>?<Transport><payload>@<RoomID>#<Key>$<MIMO>
Где <payload> - опциональный блок <key=value&...> с параметрами транспорта.
Примеры:
olcrtc://wbstream?vp8channel<vp8-fps=60&vp8-batch=64>@room-01#d823fa...$RU
olcrtc://wbstream?datachannel@room-01#d823fa...$RU / DC does not work in guest flow
olcrtc://wbstream?seichannel<fps=60&batch=64&frag=900&ack-ms=2000>@room-01#d823fa...$RU
Формат подписки (sub.md)
Текстовый файл со списком серверов. Хостится на сервере как plain text.
#name: Zarazaex Free RU
#update: 1778011200
#refresh: 10m
#icon: 🇷🇺
olcrtc://wbstream?vp8channel<vp8-fps=60&vp8-batch=64>@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).
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, 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:
# при запуске cnc.sh - в поле DNS ввести:
8.8.8.8:53
# или
77.88.8.8:53
При ручном запуске — указать другой DNS в 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 на самой машине.
Диагностика: выполнить прямо на сервере:
curl -v --connect-timeout 5 https://157.240.205.60
curl -v --connect-timeout 5 https://149.154.167.41
Если таймаут - проблема на уровне хостинга.
Решение:
- Сменить хостинг-провайдера или локацию на того, кто не блокирует исходящий трафик.
- Использовать на сервере исходящий SOCKS5 прокси через YAML конфиг:
socks:
proxy_addr: "1.2.3.4"
proxy_port: 1080
Это ошибка не на стороне olcRTC - он корректно логирует ошибки и продолжает работу. Соединения к незаблокированным адресам проходят без проблем. Проблема на стороне хостинга или фаервола.
Контакты
- Telegram канал: @openlibrecommunity - бесплатный прокси в закрепе, обновления, анонсы
- Telegram автора: @zarazaexe
- Email: zarazaex@tuta.io
- GitHub: openlibrecommunity
- Android клиент: alananisimov/olcbox
- Белые списки (еженедельное обновление): openlibrecommunity/twl