Tunnels internet traffic through video calling platforms (VK Call, Yandex Telemost) to bypass government whitelist censorship.
Step-by-step setup guide (in Russian): docs/SETUP.md
Two tunnel modes are available: DC (DataChannel) and Video (VP8 data encoding).
Browser-based. JavaScript hooks intercept RTCPeerConnection on the call page, create a DataChannel alongside the call's built-in channels, and use it as a bidirectional data pipe.
- VK Call - Negotiated DataChannel id:2 (alongside VK's animoji channel id:1). Data flows through VK's SFU
Joiner (censored, Android) Creator (free internet, desktop)
All apps
|
VpnService (captures all traffic)
|
tun2socks (IP -> TCP)
|
SOCKS5 proxy (Go, :1080)
|
WebSocket (:9000)
|
WebView (call page) Electron (call page)
| |
DataChannel <----- SFU -----> DataChannel
|
WebSocket (:9000)
|
Go relay
|
Internet
Go-based. Pion (Go WebRTC library) connects directly to the platform's TURN/SFU servers, bypassing the browser's WebRTC stack entirely. Data is encoded inside VP8 video frames.
- VK Call - Single PeerConnection, data flows through VK's SFU
- Telemost - Dual PeerConnection (pub/sub), SFU architecture
The JS hook replaces RTCPeerConnection with a MockPeerConnection that forwards all SDP/ICE operations to the local Pion server via WebSocket. Pion creates the real PeerConnection with the platform's TURN servers.
VP8 data encoding:
- Data frames:
[0xFF marker][4B length][payload]- sent as VP8 video samples - Keepalive frames: valid VP8 interframes (17 bytes) at 25fps, keyframe every 60th frame. Keeps the video track alive so the SFU/TURN does not disconnect
- The
0xFFmarker byte distinguishes data from real VP8 (keyframe first byte has bit0=0, interframe has bit0=1, so0xFFnever appears naturally) - On the receiving side, RTP packets are reassembled into full frames. First byte
0xFF= extract data, otherwise = keepalive, ignore
Multiplexing protocol over the VP8 tunnel: [4B frame length][4B connID][1B msgType][payload]
- Message types: Connect, ConnectOK, ConnectErr, Data, Close, UDP, UDPReply
- Multiple TCP/UDP connections are multiplexed into a single VP8 video stream
Joiner (censored, Android) Creator (free internet, desktop)
All apps
|
VpnService (captures all traffic)
|
tun2socks (IP -> TCP)
|
SOCKS5 proxy (Go, :1080)
|
VP8 data tunnel (Pion) VP8 data tunnel (Pion)
| |
MockPC (WebView) MockPC (Electron)
| |
Pion WebRTC <------ SFU ------> Pion WebRTC
|
Relay bridge
|
Internet
Traffic goes through the platform's SFU servers which are whitelisted. To the network firewall it looks like a normal video call.
hooks/- JavaScript hooks for DC, Video, and Headless modes (VK and Telemost)relay/- Go relay: SOCKS5 proxy, WebSocket server, VP8 video tunnel, headless joiner, connection multiplexingheadless/vk/- Headless VK creator: creates calls via API, Pion DataChannel tunnel, no browserheadless/telemost/- Headless Telemost creator: same approach for Yandex Telemostandroid-app/- Android joiner app (WebView/headless + VpnService + Go relay)creator-app/- Electron desktop creator app
Prebuilt binaries are available on GitHub Releases.
Download and run the Electron app from GitHub Releases. It bundles the Go relay automatically.
- Open the app
- Select tunnel mode (DC or Video)
- Click "VK" or "Telemost"
- Log in, create a new call from the app
- Copy the join link, send it to the joiner
Important: The call must be created from within the Creator app. Joining an existing call from the app will not work - the JS hooks must be present from the moment the call starts.
- Download and install
whitelist-bypass.apkfrom GitHub Releases - Select tunnel mode (DC or Video)
- Paste the call link and tap GO
- The app joins the call, establishes the tunnel, starts VPN
- All device traffic flows through the call
- Go 1.26+
- gomobile (
go install golang.org/x/mobile/cmd/gomobile@latest) - gobind (
go install golang.org/x/mobile/cmd/gobind@latest) - Android SDK + NDK 29
- Java 11+
- Node.js 18+
# Full release build (Android APK + Creator app + Headless creators)
./make-release.sh
# Individual builds
./build-go.sh # Go .aar, relay binary, headless creators
./copy-hooks.sh # Copy JS hooks to android assets
./build-app.sh # Android APK
./build-headless.sh # Headless creator binaries only
./build-creator.sh # Creator Electron app (all platforms)
./build-ios.sh # Go .xcframework for iOSRequires Xcode and macOS.
./build-ios.shThis builds Mobile.xcframework into ios-proxy-app/. Then open ios-proxy-app/whitelist-bypass-proxy.xcodeproj in Xcode, select your signing team in Signing & Capabilities, and build to device.
Before committing, run ios-proxy-app/strip-signing.sh to remove your Apple developer team ID from the project.
Output in prebuilts/:
| File | Platform |
|---|---|
WhitelistBypass Creator-*-arm64.dmg |
macOS |
WhitelistBypass Creator-*-x64.exe |
Windows x64 |
WhitelistBypass Creator-*-ia32.exe |
Windows x86 |
WhitelistBypass Creator-*.AppImage |
Linux x64 |
whitelist-bypass.apk |
Android |
headless-vk-creator-linux-x64 |
Linux x64 |
headless-vk-creator-linux-ia32 |
Linux x86 |
headless-telemost-creator-linux-x64 |
Linux x64 |
headless-telemost-creator-linux-ia32 |
Linux x86 |
To build the project using Docker, execute:
docker compose -f docker-build/docker-compose.yml up This will build all components (creator-app, headless, android app) into the prebuild folder (except the macOS creator)
For a minimal VPS image that only runs the Telemost guest creator flow, use:
cp docker-vps/.env.example docker-vps/.env
# edit docker-vps/.env and set TELEMOST_JOIN_LINK
./docker-vps/run.sh up
./docker-vps/run.sh logs
./docker-vps/run.sh downThis container does not need exposed ports for the Telemost creator side. It is designed to be easy to remove and keeps writable state only in docker-vps/data/.
For Ubuntu 24 VPS setup, there is also a helper:
./docker-vps/install-docker-ubuntu24.shrelay --mode <mode> [--ws-port 9000] [--socks-port 1080]
--mode- required:joiner,creator,vk-video-joiner,vk-video-creator,telemost-video-joiner,telemost-video-creator--ws-port- WebSocket port for browser/hook connection (default 9000)--socks-port- SOCKS5 proxy port, joiner modes only (default 1080)
The Go relay is split into platform-specific files:
relay/mobile/mobile.go- Shared networking code (SOCKS5, WebSocket, framing)relay/mobile/tun_android.go- Android-only: tun2socks + fdsan fix (CGo)relay/mobile/tun_stub.go- Desktop stub (no tun2socks needed)
This allows cross-compiling the relay for macOS/Windows/Linux without CGo or Android NDK.
Pure Go creators that create calls via API without a browser. No Electron, no JS hooks - Go Pion PeerConnection handles the DataChannel tunnel directly.
# VK
cd headless/vk && go build -o headless-vk-creator .
./headless-vk-creator --cookies cookies.json [--peer-id <vk_peer_id>] [--resources <mode>] [--write-file call-vk]
# Telemost
cd headless/telemost && go build -o headless-telemost .
./headless-telemost --cookies cookies-yandex.json [--resources <mode>] [--write-file call-telemost]
# Telemost guest join to an existing room (no cookies on VPS)
./headless-telemost --join-link "https://telemost.yandex.ru/j/..." [--display-name <name>] [--resources <mode>] [--write-file call-telemost]--cookies- path to cookies exported as JSON ([{"name":"..","value":".."},...])--join-link- existing Telemost conference link; joins as a guest without Yandex cookies--display-name- guest display name for--join-linkmode (Telemost only)--peer-id- VK peer_id for the call (VK only, optional)--resources- resource mode (see below)--write-file- path to file where the active call link is appended (one link per line, created if missing)
Resource modes:
| Mode | read-buf | max-dc-buf | mem-limit | Use case |
|---|---|---|---|---|
moderate |
16KB | 1MB | 64MB | Low memory environments, VPS |
default |
32KB | 4MB | 128MB | General use |
unlimited |
64KB | 8MB | 256MB | Maximum throughput |
read-buf- TCP read buffer size. Smaller = more frequent backpressure checks, less bursty memorymax-dc-buf- pauses TCP reads when DataChannel buffered amount exceeds this. Prevents SCTP pending queue from growing unboundedmem-limit- Go runtime soft memory limit (debug.SetMemoryLimit), makes GC more aggressive near the cap