Files
olcrtc/docs/about.md
s0me0ne-25 538d74fa79 Update about.md
Clarification and refinement of the end user usage scenario
2026-05-11 21:44:38 +03:00

1036 lines
56 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# olcRTC - полная документация
> **olcRTC** (OpenLibreCommunity RTC) - инструмент обхода интернет-блокировок через паразитирование на легальных WebRTC-сервисах видеозвонков, уже находящихся в российских белых списках.
>
> Проект: [github.com/openlibrecommunity/olcrtc](https://github.com/openlibrecommunity/olcrtc)
> Лицензия: WTFPL
> Статус: **Beta**
---
## Содержание
1. [Почему olcRTC существует](#1-почему-olcrtc-существует)
2. [Идея и история создания](#2-идея-и-история-создания)
3. [Как это работает](#3-как-это-работает)
4. [Архитектура](#4-архитектура)
5. [Структура репозитория](#5-структура-репозитория)
6. [Auth-провайдеры](#6-auth-провайдеры)
7. [Transports - транспорты](#7-transports--транспорты)
8. [Шифрование](#8-шифрование)
9. [Мультиплексирование](#9-мультиплексирование)
10. [SOCKS5 прокси](#10-socks5-прокси)
11. [Mobile / Android](#11-mobile--android)
12. [Go-либа (pkg/olcrtc)](#12-go-либа-pkgolcrtc)
13. [Python PoC скрипты](#13-python-poc-скрипты)
14. [Сборка и деплой](#14-сборка-и-деплой)
15. [CLI - все флаги](#15-cli--все-флаги)
16. [URI-формат и подписки](#16-uri-формат-и-подписки)
17. [Матрица совместимости](#17-матрица-совместимости)
18. [CI/CD](#18-cicd)
19. [Что планируется сделать - Issues](#19-что-планируется-сделать--issues)
20. [Контрибуторы](#20-контрибуторы)
21. [Частые ошибки](#21-частые-ошибки)
---
## 1. Почему olcRTC существует
В России работают ТСПУ (технические средства противодействия угрозам). В мобильных сетях провайдеры перешли в режим **белых списков**: ТСПУ дропает все пакеты, кроме явно разрешённых IP-адресов и SNI.
Фильтрация двухуровневая:
- **L3** - по IP-адресу назначения. Не разрешён → пакет физически не уходит дальше второго хопа.
- **L7** - по SNI в TLS ClientHello. Есть в чёрном списке → RST.
Классические обходы через VPS ломаются когда VPS не попадает в белый список. Yandex Cloud, VK Cloud, Timeweb в списке - но провайдеры активно банят инстансы используемые как прокси.
**Решение olcRTC**: не пытаться попасть в белый список - использовать сервисы, которые там уже есть навсегда. Телемост, SaluteJazz и 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 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 образ сервера, SaluteJazz PoC от community-контрибутора `0xcodepunk`.
**2026-04-12..14** - большой рефакторинг: golangci-lint, Jazz провайдер с protobuf-style пакетами, автогенерация Room ID для Jazz, Windows скрипты от `DeNcHiK3713`.
**2026-04-19..20** - архитектурный рефакторинг: выделение слоёв `auth` / `transport` / `link` (архитектурно `carrier` слой отвечает за WebRTC сессию), 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-формат и формат подписок, `-client-id` для привязки клиента к серверу, SOCKS5 аутентификация.
**2026-05-07..10** - финальная полировка: исправлен throughput bug в vp8channel (ограничение было в 32 раза ниже реального), документация, SEI конфигурация, `-socks-user`/`-socks-pass`.
### Статья на Хабре
Проект описан в двух статьях на Хабре:
- *«Это - всё что вам надо знать о белых списках»* - технический анализ как работает фильтрация, 63k IP в белом списке из 46 млн российских, методы обхода
- *«BAREBONE2022: чтобы заблокировать этот протокол придётся запретить MAX и Yandex»* - описание идеи olcRTC, первые замеры скорости
---
## 3. Как это работает
```
Браузер/приложение
│ (обычные TCP соединения)
SOCKS5 :8808 ← cnc (клиент), работает на вашей машине
│ ChaCha20-Poly1305
│ smux поверх muxconn
Transport (datachannel / vp8channel / seichannel / videochannel)
Auth (jazz / wbstream / telemost)
│ WebRTC DataChannel или VideoTrack
SFU Яндекса / Сбера / WB ← сервер в белом списке у всех провайдеров
Transport (datachannel / vp8channel / seichannel / videochannel)
srv (сервер), работает на вашем VPS
│ (обычный TCP/DNS)
Интернет
```
Клиент (`cnc`) стоит на устройстве, интернет-подключение которого работает по белому списку - чаще всего это мобильный телефон - и поднимает локальный SOCKS5. Любой браузер или приложение подключается к нему как к обычному прокси. Трафик мультиплексируется через smux, шифруется ChaCha20-Poly1305 и передаётся через выбранный транспорт поверх WebRTC SFU.
Сервер (`srv`) можно установить на любое устройство с интернетом без белого списка - ваш VPS, домашний компьютер или даже другой телефон. Он подключается к той же комнате видеозвонка, получает зашифрованный поток и от своего имени делает TCP соединения к нужным адресам в интернете.
ТСПУ видит трафик к IP Яндекса/Сбера/WB с корректным TLS и SNI - ничем не отличается от обычного видеозвонка.
---
## 4. Архитектура
Проект разбит на чёткие слои. Каждый слой можно заменить независимо.
```
pkg/olcrtc/ публичная Go-либа (net.Conn, CreateRoom)
cmd/olcrtc/ CLI entrypoint, парсинг флагов
internal/app/session/ конфигурация, валидация, роутинг в server/client
│ │
internal/server/ internal/client/ бизнес-логика: SOCKS5, smux
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/auth/ реестр auth-провайдеров
├── builtin/ регистрация engine/auth адаптеров
└── bytestream.go ByteStream, VideoTrack capability
internal/engine/ WebRTC движки (wire-level SFU протоколы)
├── livekit/ LiveKit SDK (wbstream использует)
├── salutejazz/ SaluteJazz (salutejazz.ru)
└── goolom/ Yandex Goolom (telemost.yandex.ru)
internal/auth/ сервисные auth-провайдеры (HTTP login flows)
├── telemost/ Yandex Telemost → engine/goolom
├── salutejazz/ SaluteJazz → engine/salutejazz
└── wbstream/ WB Stream → engine/livekit
internal/crypto/ ChaCha20-Poly1305 AEAD
internal/names/ генератор имён участников
internal/protect/ Android VPN protect() интеграция
internal/logger/ структурированное логирование
internal/link/ интерфейс Link + реестр
internal/e2e/ E2E тесты на реальных провайдерах
```
---
## 5. Структура репозитория
### Корень
| Файл/папка | Что это |
|---|---|
| `readme.md` | Краткое описание, команды сборки, ссылки |
| `about.md` | Этот документ |
| `SECURITY.md` | Политика безопасности |
| `magefile.go` | Система сборки на Mage (аналог Makefile для Go). Таргеты: `build`, `cross`, `mobile`, `docker`, `podman`, `lint`, `test`, `e2e` |
| `Dockerfile` | Многоэтапный образ: Alpine build → Alpine runtime с непривилегированным пользователем `olcrtc` |
| `docker-compose.server.yml` | Compose для серверного режима |
| `.gitmodules` | Субмодуль `internal/transport/videochannel/gr` - кастомные кодеки QR и tile |
| `.golangci.yml` | Конфиг линтера golangci-lint |
| `.github/workflows/ci.yml` | CI: тесты, покрытие, E2E, lint, сборка CLI для всех платформ, сборка Android AAR |
### `cmd/olcrtc/`
| Файл | Что делает |
|---|---|
| `main.go` | Точка входа. Парсит флаги (`flag.FlagSet`), настраивает логирование, подавляет шум LiveKit/pion в не-debug режиме, запускает `session.Run` или `session.Gen`. Graceful shutdown по SIGTERM/SIGINT с 5-секундным таймаутом |
| `main_test.go` | Юнит-тесты CLI: валидация флагов, режимы, edge cases |
### `internal/app/session/`
| Файл | Что делает |
|---|---|
| `session.go` | Главная точка конфигурации. `RegisterDefaults()` регистрирует все engines, auth-провайдеры, links, transports. `Validate()` проверяет все флаги. `Run()` роутит в `server.Run` или `client.Run`. `Gen()` генерирует Room ID для jazz/wbstream с ретраями через `auth.RoomCreator` |
| `session_test.go` | Тесты валидации конфига |
### `internal/server/`
| Файл | Что делает |
|---|---|
| `server.go` | Серверная сторона туннеля. Подключается к комнате как второй участник звонка. Создаёт `muxconn``smux.Session`. Для каждого входящего smux-стрима читает JSON `ConnectRequest` от клиента с адресом назначения, устанавливает TCP соединение и гоняет байты туда-обратно. Поддерживает SOCKS5 прокси для исходящего трафика. Умеет переподключаться при разрыве |
| `server_test.go` | Тесты серверной логики |
### `internal/client/`
| Файл | Что делает |
|---|---|
| `client.go` | Клиентская сторона. Поднимает SOCKS5-сервер. Для каждого входящего подключения: SOCKS5 handshake (поддержка RFC 1929 username/password auth), создаёт smux-стрим, шлёт JSON `ConnectRequest` с адресом, гоняет байты. Переподключается при разрыве WebRTC сессии |
| `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 у auth-провайдера (DataChannel), просто форвардит байты. Лимит payload: 12KB |
| `vp8channel/transport.go` | Данные кодируются в VP8 видеофреймы. Поверх auth-провайдера строится 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` описывает что умеет auth-провайдер: ByteStream и/или VideoTrack |
| `bytestream.go` | `ByteStream` и `VideoTrack` интерфейсы |
| `carrier_test.go` | Тесты |
| `builtin/register.go` | Регистрирует все engine/auth комбинации в реестре auth |
| `builtin/engine_adapter.go` | Адаптер `engine.Session``auth.Provider` + `registerDirect()` для режима без auth |
### `internal/engine/`
Wire-level SFU протоколы. Каждый умеет Connect/Send/Close/WatchConnection/SetEndedCallback.
| Пакет | Что делает |
|---|---|
| `engine.go` | Интерфейс `Session` + реестр. `New()` создаёт сессию по имени |
| `livekit/` | LiveKit SDK. Самый стабильный движок — минимальная прослойка поверх lksdk |
| `salutejazz/` | SaluteJazz. Signaling через HTTP API. Protobuf-style DataChannel пакеты. KCP, автопереподключение |
| `goolom/` | Yandex Goolom (Telemost backend). Signaling через WebSocket. Двухуровневый keepalive. Автопереподключение с refresh токена |
### `internal/auth/`
Сервисные HTTP auth-провайдеры. Каждый реализует `auth.Provider` (`Engine() string`, `Issue() Credentials`). Опционально — `auth.RoomCreator` (`CreateRoom()`).
| Пакет | Что делает |
|---|---|
| `auth.go` | Интерфейсы `Provider`, `RoomCreator`, `Credentials`. Реестр |
| `telemost/` | Yandex Telemost: логин гостя, получение Goolom токена. Не поддерживает создание комнат |
| `salutejazz/` | SaluteJazz: создание комнаты, получение SDP токена. Поддерживает `CreateRoom()` |
| `wbstream/` | WB Stream: регистрация гостя, создание стрима. Поддерживает `CreateRoom()` |
### `pkg/olcrtc/`
Публичная Go-либа для встраивания olcrtc в другие приложения (sing-box и т.п.).
| Файл | Что делает |
|---|---|
| `olcrtc.go` | `Config`, `Session`, `New()`, `Dial() net.Conn`, `CreateRoom()`, `RegisterDefaults()` |
| `conn.go` | `net.Conn` реализация: `Read` из io.Pipe (кормится из `OnData`), `Write``engine.Send`, `Close` закрывает пайп и сессию. При `SetEndedCallback` пайп закрывается с `ErrSessionEnded` |
### `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 тесты на реальных провайдерах. Матрица всех auth × transport комбинаций. Запускается с флагом `-olcrtc.real-e2e`. В CI запускается на каждый push |
### `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 |
| `jazz_poc_datachannel.py` | PoC DataChannel через SaluteJazz |
| `jazz_info.py` | Информация о Jazz конференции |
| `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. Задаёт вопросы про auth/transport/room/key, собирает образ, запускает контейнер. Флаги: `--branch=<name>` (сменить ветку), `--no-cache` (очистить Go-кеш перед сборкой) |
| `cnc.sh` | Интерактивный скрипт запуска клиента через Podman |
| `docker/olcrtc-entrypoint.sh` | Docker entrypoint: читает env переменные, формирует CLI флаги, запускает `olcrtc` |
| `docker/olcrtc-healthcheck.sh` | Docker healthcheck: проверяет что процесс запущен |
### `data/`
| Файл | Что делает |
|---|---|
| `names` | Список русских имён для генератора имён участников |
| `surnames` | Список русских фамилий |
### `docs/`
| Файл | Что делает |
|---|---|
| `fast.md` | Быстрый старт через скрипты (Podman) |
| `manual.md` | Мануальная сборка: Go, mage, кросс-компиляция, все шаги |
| `settings.md` | Матрица совместимости auth×transport, все CLI флаги с описанием, готовые команды |
| `uri.md` | URI формат для клиентских приложений: `olcrtc://<Auth>?<Transport>@<RoomID>#<Key>%<ClientID>$<MIMO>` |
| `sub.md` | Формат подписок: список серверов в одном файле с метаданными |
---
## 6. Auth-провайдеры
Auth-провайдер — это WebRTC сервис видеозвонков, через который идёт туннель. Все три в белых списках у российских провайдеров.
### SaluteJazz (`jazz`)
- Сервис видеозвонков от Сбера: `salutejazz.ru`
- Не требует регистрации для участника (только организатор)
- DataChannel работает, но Jazz **банит IP** за паттерны трафика характерные для DataChannel туннеля
- VideoTrack работает стабильно
- Поддерживает автогенерацию Room ID (`-mode gen`)
- Инициализация звонка изнутри автоматически реализована
### Yandex Telemost (`telemost`)
- Сервис видеозвонков от Яндекса: `telemost.yandex.ru`
- **Удалил DataChannel** - его больше нет в Telemost
- VideoTrack работает
- Требует создания комнаты вручную через сайт (нет автогенерации)
- Двухуровневый keepalive: WebSocket ping + app-level ping
### WB Stream (`wbstream`)
- Сервис трансляций от Wildberries: `stream.wb.ru`
- **Рекомендуется** - самый стабильный
- Минимальная прослойка, почти прямой relay
- Работает со всеми транспортами: datachannel, vp8channel, seichannel, videochannel
- Поддерживает автогенерацию Room ID (`-mode gen`)
- Инициализация звонка автоматически
---
## 7. Transports - транспорты
Transport определяет как именно данные упаковываются в WebRTC поток.
### datachannel
Самый простой и быстрый. Данные идут напрямую через WebRTC DataChannel (SCTP over DTLS).
- Лимит payload: 12KB на сообщение (ограничение SFU)
- Надёжный, упорядоченный (SCTP гарантирует)
- Работает с jazz (нежелательно - банят) и wbstream
- **Лучшая комбинация: `wbstream + datachannel`**
### vp8channel
Данные упаковываются в VP8 видеофреймы. Поверх этого строится KCP - надёжный протокол с повторной передачей, работающий поверх ненадёжного канала.
- Работает везде где есть VideoTrack (jazz, telemost, wbstream)
- Большой пинг из-за батчинга фреймов
- KCP параметры: MTU 1400, окно 4096, conv ID `0xC0FFEE01`
- Рекомендуется: `-vp8-fps 60 -vp8-batch 64` (если возникают проблемы с производительностью или стабильностью, попробуйте снизить FPS вдвое, например до 30)
### 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 попыток
- Не работает с telemost
- Рекомендуется: `-fps 60 -batch 64 -frag 900 -ack-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 видеопоток. Самый медленный транспорт, но работает везде.
---
## 8. Шифрование
Весь туннельный трафик шифруется **ChaCha20-Poly1305** (XChaCha20-Poly1305 через `golang.org/x/crypto`).
- Ключ: 32 байта, передаётся как hex строка (64 символа)
- Генерация: `openssl rand -hex 32`
- Каждое сообщение: случайный nonce (24 байта) prepend к ciphertext + AEAD тег
- Ключ должен совпадать на сервере и клиенте
- Шифрование происходит в `muxconn` - до передачи в transport/auth
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`
- 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`, `-socks-proxy-port`).
---
## 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(auth, roomID, clientID, keyHex string)` - запустить туннель
- `Stop()` - остановить
- `IsRunning() bool`
- `SetProtector(p SocketProtector)` - Android VPN bypass (VpnService.protect)
- `SetLogWriter(w LogWriter)` - получать логи в Kotlin/Java
По умолчанию использует `vp8channel` транспорт (наиболее совместимый). Если auth - wbstream или jazz и DataChannel доступен - переключается на `datachannel`.
`protect.go` - механизм Android VPN protect: перед каждым `connect()` вызывается Kotlin-коллбэк который вызывает `VpnService.protect(fd)`. Без этого трафик olcRTC может рекурсивно идти через тот же VPN.
---
## 12. Go-либа (pkg/olcrtc)
`pkg/olcrtc` - публичная Go-либа для встраивания olcrtc в другие Go-программы (sing-box, кастомные клиенты).
Каждый провайдер реализует метод `DefaultServiceURL()`, что позволяет библиотеке автоматически заполнять адрес SFU при выборе провайдера.
```go
import "github.com/openlibrecommunity/olcrtc/pkg/olcrtc"
olcrtc.RegisterDefaults()
// создать комнату (wbstream или jazz)
roomID, err := olcrtc.CreateRoom(ctx, "wbstream")
// подключиться через auth-провайдер
sess, err := olcrtc.New(ctx, olcrtc.Config{
Auth: "wbstream",
RoomID: roomID,
DNSServer: "1.1.1.1:53",
})
// или напрямую к SFU без auth
sess, err := olcrtc.New(ctx, olcrtc.Config{
Engine: "livekit",
URL: "wss://sfu.example/",
Token: "<jwt>",
})
// получить net.Conn (блокирует до готовности data channel)
conn, err := sess.Dial(ctx)
// conn реализует net.Conn — передаётся в sing-box или любой io.ReadWriter
// когда сессия умирает — Read() возвращает ErrSessionEnded
```
**`CreateRoom`** поддерживается только провайдерами `wbstream` и `jazz`. Telemost не поддерживает создание комнат (возвращает `ErrRoomCreationUnsupported`).
**`WatchConnection`** запускается автоматически внутри `Dial` — при падении сессии `Read` разблокируется с ошибкой.
---
## 13. Python PoC скрипты
Исторический слой - с этого всё начиналось. Используются для исследования API провайдеров и проверки гипотез.
**Telemost:**
- `telemost_poc_datachannel.py` - первый рабочий туннель, обнаружен лимит 8KB DataChannel (молча дропает больше)
- `telemost_poc_videochannel.py` - QR в видео, `vcsend.py` - передача файлов
- `telemost_info.py` - полный дамп SDP, ICE серверов, участников
**Jazz:**
- `jazz_poc_datachannel.py` - DataChannel через Jazz SFU
- `jazz_info.py` - информация о конференции
**WB Stream:**
- `wbstream_poc_datachannel.py` - DataChannel
- `wbstream_poc_videochannel.py` - видеоканал
- `wbstream_info.py` - информация
Для запуска: `pip install -r code/requirements.txt`
---
## 14. Сборка и деплой
### Зависимости
- Go 1.25+
- 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 buildCLIB # CLI + b-codec (клонирует внешний репо, собирает libb.so)
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
mage test # go test -race ./...
mage e2e # E2E тесты (нужны реальные провайдеры)
mage clean # удалить build/
```
### Быстрый старт через скрипты (Podman)
```sh
git clone https://github.com/openlibrecommunity/olcrtc --recurse-submodules
cd olcrtc
# на сервере (VPS):
./script/srv.sh
# на клиенте:
./script/cnc.sh
```
### Мануальный запуск
```sh
# генерация ключа
openssl rand -hex 32
# генерация room ID (для jazz/wbstream)
./olcrtc -mode gen -auth wbstream -dns 1.1.1.1:53 -amount 1 -data data
# сервер
./olcrtc -mode srv -auth wbstream -transport datachannel \
-id ROOM_ID -client-id default -key HEX_KEY \
-link direct -dns 1.1.1.1:53 -data data
# клиент
./olcrtc -mode cnc -auth wbstream -transport datachannel \
-id ROOM_ID -client-id default -key HEX_KEY \
-link direct -dns 1.1.1.1:53 -data data \
-socks-host 127.0.0.1 -socks-port 8808
```
### Docker
```sh
docker run -e OLCRTC_AUTH=wbstream \
-e OLCRTC_ROOM_ID=... \
-e OLCRTC_KEY=... \
olcrtc/server:local
```
---
## 15. CLI - все флаги
### Обязательные (для всех режимов)
| Флаг | Описание |
|---|---|
| `-mode` | `srv` - сервер, `cnc` - клиент, `gen` - генерация Room ID |
| `-auth` | `telemost`, `jazz`, `wbstream`, `none` |
| `-transport` | `datachannel`, `vp8channel`, `seichannel`, `videochannel` |
| `-id` | Room ID |
| `-client-id` | Идентификатор клиента, должен совпадать на srv и cnc. Один client-id может держать бесконечное количество соединений, но SFU ограничивает полосу на участника — оптимально 1 client-id = 1 пользователь (не обязательно) |
| `-key` | Ключ шифрования hex 64 символа |
| `-link` | Всегда `direct` |
| `-data` | Всегда `data` |
| `-dns` | DNS сервер, например `1.1.1.1:53` |
### Прямой режим
Позволяет подключиться напрямую к любому LiveKit/Goolom/SaluteJazz SFU.
При использовании `-auth none` флаги `-engine`, `-url` и `-token` обязательны.
При использовании именованного провайдера эти флаги заполняются автоматически из `DefaultServiceURL()`, но могут быть переопределены вручную.
| Флаг | Описание |
|---|---|
| `-engine` | Движок: `livekit`, `goolom`, `salutejazz` |
| `-url` | WebSocket URL SFU |
| `-token` | Токен доступа |
### Необязательные
| Флаг | Описание |
|---|---|
| `-debug` | Verbose логи |
### Только для клиента (`-mode cnc`)
| Флаг | По умолчанию | Описание |
|---|---|---|
| `-socks-host` | `127.0.0.1` | Адрес SOCKS5 |
| `-socks-port` | `1080` | Порт SOCKS5 |
| `-socks-user` | - | Логин (опционально) |
| `-socks-pass` | - | Пароль (опционально) |
### Только для сервера (`-mode srv`)
| Флаг | Описание |
|---|---|
| `-socks-proxy` | Адрес SOCKS5 прокси для исходящего трафика |
| `-socks-proxy-port` | Порт этого прокси |
### Режим генерации (`-mode gen`)
| Флаг | Описание |
|---|---|
| `-auth` | `jazz` или `wbstream` (telemost не поддерживает) |
| `-amount` | Количество комнат для генерации |
### vp8channel
| Флаг | Default | Описание |
|---|---|---|
| `-vp8-fps` | 25 | FPS VP8 потока |
| `-vp8-batch` | 1 | Кадров за тик |
### seichannel
| Флаг | Default | Описание |
|---|---|---|
| `-fps` | 20 | FPS H264 потока |
| `-batch` | 1 | Кадров за тик |
| `-frag` | 900 | Размер фрагмента в байтах |
| `-ack-ms` | 2000 | ACK timeout в мс |
### videochannel
| Флаг | Default | Описание |
|---|---|---|
| `-video-codec` | `qrcode` | `qrcode` или `tile` |
| `-video-w` | 1920 | Ширина |
| `-video-h` | 1080 | Высота |
| `-video-fps` | 30 | FPS |
| `-video-bitrate` | `2M` | Битрейт |
| `-video-hw` | `none` | `none` или `nvenc` |
| `-video-qr-recovery` | `low` | ECC: `low`/`medium`/`high`/`highest` |
| `-video-qr-size` | 0 (авто) | Размер фрагмента QR в байтах |
| `-video-tile-module` | 4 | Размер тайла в пикселях 1..270 |
| `-video-tile-rs` | 20 | Reed-Solomon паритет % 0..200 |
---
## 16. URI-формат и подписки
### URI формат
Соглашение для клиентских приложений. Сам `olcrtc` не парсит - используется в сторонних клиентах.
```
olcrtc://<Auth>?<Transport><payload>@<RoomID>#<Key>%<ClientID>$<MIMO>
```
Где `<payload>` - опциональный блок `<key=value&...>` с параметрами транспорта.
**Примеры:**
```
olcrtc://wbstream?datachannel@room-01#d823fa...%android-01$RU / olc free sub
olcrtc://wbstream?vp8channel<vp8-fps=60&vp8-batch=64>@room-01#d823fa...%android-01$RU
olcrtc://telemost?seichannel<fps=60&batch=64&frag=900&ack-ms=2000>@room-01#d823fa...%client$RU
```
### Формат подписки (sub.md)
Текстовый файл со списком серверов. Хостится на сервере как plain text.
```text
#name: Zarazaex Free RU
#update: 1778011200
#refresh: 10m
#icon: 🇷🇺
olcrtc://wbstream?datachannel@room-01#key%client-id$RU / free
##name: RU-1
##ip: 1.2.3.4
##comment: basic free node
```
Клиентские приложения читают этот файл и предлагают список серверов пользователю (аналог подписок в v2ray/sing-box).
---
## 17. Матрица совместимости
| Transport | telemost | jazz | wbstream |
|---|:---:|:---:|:---:|
| datachannel | - | `*` | `+` |
| vp8channel | `+` | `+` | `+` |
| seichannel | - | `+` | `+` |
| videochannel | `+` | `+` | `+` |
- `+` работает
- `-` не поддерживается
- `*` работает, но jazz банит IP за паттерны datachannel трафика
**Рекомендуется:** `wbstream + datachannel` - максимальная скорость, минимальный пинг, без бана.
**Скорость по убыванию:** `datachannel` > `vp8channel` > `seichannel` > `videochannel`
**Рекордный замер:** на связке `wbstream + datachannel` (test by `x2827262628281872727`) зафиксированы пинг **7 мс** и скорость **792.62 Mbps на вход / 749.69 Mbps на выход** - максимум, измеренный через olcRTC.
<img src="asset/speedtest.png" alt="speedtest" width="400">
---
## 18. CI/CD
`.github/workflows/ci.yml` - GitHub Actions, запускается на каждый push/PR в master.
| Job | Что делает |
|---|---|
| `test` | `go test -count=1 ./...` |
| `coverage` | `go test --cover ./...` |
| `real-e2e` | E2E матрица всех auth×transport на реальных провайдерах (25 мин таймаут) |
| `lint` | golangci-lint |
| `build-cli` | `mage cross` - кросс-компиляция для 9 платформ, артефакты в Actions |
| `build-android` | `mage mobile` - Android AAR, артефакт в Actions |
Go версия в CI: 1.25.x
---
## 19. Что планируется сделать - Issues
### Открытые
**Issue #22 - реализовать поддержку stream.wb.ru** `enhancement`
WB Stream - текущий приоритет. Основа уже реализована, остаётся:
- [ ] Симуляция XHR телеметрии (маскировка под легитимный клиент)
- [ ] Симуляция задержек и обрезание до размера реальных сообщений
- [ ] Система завершения звонка
- [ ] Авто перезапуск звонка если идёт слишком долго
- [ ] Юзать TLS стек Chrome как naiveproxy
**Issue #2 - реализовать поддержку telemost.yandex.ru** `enhancement`
- [ ] Симуляция XHR телеметрии
- [ ] Симуляция задержек
- [ ] Инициализация звонка изнутри автоматически
- [ ] Система завершения звонка
- [ ] Авто перезапуск звонка
- [ ] TLS стек Chrome
**Issue #1 - реализовать поддержку salutejazz.ru** `enhancement`
- [ ] Симуляция XHR телеметрии
- [ ] Симуляция задержек
- [ ] Система завершения звонка
- [ ] Авто перезапуск звонка
- [ ] TLS стек Chrome
### Закрытые (уже сделано)
| Issue | Что было |
|---|---|
| #44 | Very high ping - исправлен throughput bug vp8channel |
| #40 | Подключение нескольких устройств - реализовано через client-id |
| #39 | Oracle VPS поддержка |
| #38 | Стандартный URI формат - реализован |
| #37 | Jitsi Meet - не планируется |
| #33 | iOS клиент - в планах |
| #27 | Инструкция - написана |
| #26 | SIP003 transport - не планируется |
| #25 | TLS/DTLS фингерпринтинг |
| #9 | Нормальный мультиплексор - реализован (smux) |
| #3 | macOS/Linux/Android/Windows поддержка - реализована |
---
## 20. Контрибуторы
| Контрибутор | Коммиты | Вклад |
|---|---|---|
| **zarazaex69** (zarazaex@tuta.io) | 417 | Автор проекта. Вся архитектура, все транспорты, auth-providers, 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 |
| **s0me0ne-25** | 4 | Расширение датасета имён и фамилий, правки документации |
| **Kot-nikot** | 3 | Фиксы |
| **HLNikNiky** / Sesdear | 2 | URI добавление, фиксы |
| **Denis Suchok** / DeNcHiK3713 | 1 | Windows Podman скрипты |
| **0xcodepunk** | 1 | SaluteJazz PoC DataChannel (issue #10) |
| **scalebb2** | 1 | - |
---
## 21. Частые ошибки
### `i/o timeout` при резолве или "днслик" (DNS leak)
**Симптомы:**
- Клиент не может зарезолвить адрес SFU (например `stream.wb.ru`).
- DNS запросы "утекают" мимо туннеля.
**Причина:** порт 53 (стандартный DNS) может перехватываться или блокироваться провайдером.
**Решение:** использовать DNS сервер на 443 порту. Многие публичные DNS серверы (например Google) поддерживают запросы на 443 порту.
```sh
# в скрипте или через флаг -dns укажите:
8.8.8.8:443
```
---
### Сборка вылетает / Ошибка компиляции (не хватает ОЗУ)
**Симптомы:** процесс сборки (`mage build`, `go build` или внутри Docker/Podman) внезапно прерывается с ошибкой `signal: killed` или `out of memory`.
**Причина:** для сборки проекта (особенно с тяжелыми зависимостями или в контейнере) может не хватать оперативной памяти (нужно минимум 2ГБ, лучше 4ГБ+).
**Решение:** включите **SWAP** (файл подкачки). На Linux это делается так:
```bash
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# чтобы сохранить после перезагрузки:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```
---
### `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
```
При ручном запуске:
```sh
./olcrtc -mode cnc ... -dns 8.8.8.8:53
```
После смены DNS в логах должна появиться строка:
```
SOCKS5 server listening on 0.0.0.0:8808
```
### `dial tcp4 : i/o timeout` на сервере (VPS блокирует исходящий трафик)
**Симптомы:**
В логах сервера (`-mode srv`) появляются строки вида:
```
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 прокси (`-socks-proxy`/`-socks-proxy-port`), который не заблокирован:
```sh
./olcrtc -mode srv ... -socks-proxy 1.2.3.4 -socks-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)