A proxy tunnel that routes TCP traffic through Salute Jazz WebRTC infrastructure. It is meant for cases where classic VPN protocols are easy to detect or block, while browser-based video traffic is still allowed.
jazztun runs a local SOCKS5 proxy and sends TCP traffic through a Salute Jazz room to a remote server. Your browser or app talks to 127.0.0.1:1080; the remote host becomes the exit point.
Why this works in places where regular VPNs do not: the transport path looks like a normal browser video session built on Jazz signaling, TURN, and WebRTC DataChannels, not a conventional VPN tunnel.
To reach resources blocked in Russia, the remote server must be outside Russia. When Salute Jazz itself is allowed or explicitly whitelisted in a network, jazztun benefits from that allowance.
┌────────────────┐ SOCKS5 ┌────────────────┐
│ Browser / App ├────────────────►│ jazztun client │
└────────────────┘ └──────┬─────────┘
│
│ AES-256-GCM encrypted mux frames
▼
Salute Jazz signaling + WebRTC DataChannel
│
▼
┌──────┴─────────┐
│ jazztun server │
└──────┬─────────┘
│
▼
Remote TCP destination
- TCP tunneling over a Salute Jazz room
- Local SOCKS5 proxy on the client
- Optional RFC 1929 username/password auth for the local SOCKS5 proxy
- AES-256-GCM encryption for tunnel frames
- Multiple parallel transport peers with
-peers N - Session-scoped transport pairing with
-session - Multiple independent tunnel pairs can share one Jazz room
- Per-stream affinity to preserve TCP byte order
- Credit-based mux flow control
- Optional custom DNS resolver on the server side
- Optional upstream SOCKS5 proxy for server egress
- No UDP associate
- No bind support
- Access control is effectively the room URL, the shared key, and the matching session namespace
- Full peer teardown and rebuild through
WatchConnectionis covered by tests; the live reconnect run recovered on the same WebRTC session
Pre-built binaries for Linux, Windows, and macOS (Apple Silicon) are published on the Releases page.
The container image for the server is published to GitHub Container Registry:
docker run --rm -it ghcr.io/kavun-sama/jazztun-server:latest -room newEach release includes checksums.txt. Verify it before running the binaries:
sha256sum -c checksums.txtRequirements:
- Go
1.25or newer - Network access to Salute Jazz signaling and ICE/TURN endpoints
- A remote host outside Russia if you need access to resources blocked from within Russia
- The remote host must be able to reach the TCP targets you want to proxy
Linux or macOS:
go build -o server ./cmd/server
go build -o client ./cmd/clientWindows PowerShell:
go build -o server.exe .\cmd\server
go build -o client.exe .\cmd\clientPrint the binary version:
./server -version
./client -versionBuild the server image locally:
docker build -t jazztun-server:local .Or pull the published image:
docker pull ghcr.io/kavun-sama/jazztun-server:latestRun it directly:
docker run --rm -it jazztun-server:local -room new -peers 4Run the published image directly:
docker run --rm -it ghcr.io/kavun-sama/jazztun-server:latest -room new -peers 4Or use the provided compose file:
docker compose -f docker-compose.server.yml up --buildThe compose file accepts these environment variables:
JAZZTUN_ROOM, defaultnewJAZZTUN_KEYJAZZTUN_SESSIONJAZZTUN_PEERS, default4JAZZTUN_DNS, default1.1.1.1:53JAZZTUN_SOCKSJAZZTUN_VERBOSE, set to any non-empty value to enable-v
- Prepare a remote host outside Russia.
- Copy
jazztunthere. - Start
serveron the remote host. - Copy the printed room URL and key.
- Start
clienton your local machine with the same room URL and key. - Point your browser or app to the local SOCKS5 proxy.
- Verify that the exit IP is the remote server IP.
If one room is only used by one server/client pair, you can ignore -session. If you want several independent pairs inside the same Jazz room, give each pair its own -session value and use the same value on both sides of that pair.
Example remote host setup:
mkdir -p ~/jazztun
cd ~/jazztun
chmod +x ./server
./server -room new -peers 4 -vExample local client start:
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4 -listen 127.0.0.1:1080 -vIf you want the local SOCKS5 proxy to be usable by other devices in your LAN:
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -listen 0.0.0.0:1080 -socks-user demo -socks-pass strongpassOne Jazz room can now carry several independent jazztun pairs at once.
- Same room URL + same key + same
-sessionvalue: same tunnel pair - Same room URL + same key + different
-sessionvalue: separate tunnel pair - Same room URL + same key + same
-sessionvalue on two clients at once: conflict, the extra client is ignored
Example:
./server -room new -peers 2 -session alpha
./client -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 2 -session alphaSecond independent pair in the same room:
./server -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 2 -session beta
./client -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 2 -session betaStart the server first:
./server -room new -peers 4 -vThe server will print the room URL and the generated key.
Start the client with the same room URL and key:
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4 -vBy default, the local SOCKS5 proxy listens on 127.0.0.1:1080.
./server -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4
./client -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4Typical server commands:
./server -room new -peers 4 -v
./server -room new -peers 4 -session alpha
./server -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -dns 8.8.8.8:53
./server -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -socks 127.0.0.1:9050Typical client commands:
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4 -v
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -session alpha -peers 4
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -listen 127.0.0.1:1081
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -listen 0.0.0.0:1080 -socks-user demo -socks-pass strongpassServer:
-room: room URL ornew-key: 64-character hex key; autogenerated if omitted on the server-session: optional namespace for sharing one Jazz room across multiple independent tunnel pairs-duo: shorthand for two transport peers-peers: number of transport peers to open; overrides-duo; use this to tune throughput-dns: DNS resolver for remote lookups, default1.1.1.1:53-socks: upstream SOCKS5 proxy used by the server for outbound dialing-v: verbose logging-version: print the binary version and exit
Client:
-room: room URL-key: same 64-character hex key as the server-session: optional namespace that must match the server when sharing one Jazz room-listen: local SOCKS5 listen address, default127.0.0.1:1080-socks-user: enable RFC 1929 username/password auth for the local SOCKS5 proxy-socks-pass: password paired with-socks-user-duo: shorthand for two transport peers-peers: number of transport peers to open; overrides-duo; use this to tune throughput-v: verbose logging-version: print the binary version and exit
Run the full validation set:
go test ./...
go vet ./...Package-level examples:
go test ./internal/crypto -v
go test ./internal/mux -v
go test ./internal/transport/jazz -v- Start
server. - Start
clientwith the same room URL and key. - Check the exit IP through the local SOCKS5 proxy:
curl.exe --socks5-hostname 127.0.0.1:1080 https://ifconfig.me/ipIf the tunnel is working, the returned IP should be the remote server IP.
On the remote server:
mkdir -p /root/bench
dd if=/dev/zero of=/root/bench/100m.bin bs=1M count=100 status=none
python3 -m http.server 8088 --directory /root/benchOn the client machine:
curl.exe --max-time 15 --socks5-hostname 127.0.0.1:1080 -o NUL -w "speed=%{speed_download} bytes/s time=%{time_total}s code=%{http_code}`n" http://SERVER_IP:8088/100m.binObserved throughput on the current codebase:
- single stream: roughly
33-43 Mbit/s - aggregate throughput with
-peers 6and eight parallel downloads: about110 Mbit/s
Room URL rejected:
- use the full invite link, including
?psw=...
Tunnel connects but traffic does not pass:
- verify that the key matches on both sides
- verify that
-sessionmatches on both sides when you use it - verify that the client is using SOCKS5, not HTTP proxy mode
- verify that the server can resolve and reach the target host
Another client unexpectedly does not connect in the same room:
- check whether both clients are using the same
-session - same session means same logical tunnel pair; use a different
-sessionfor each independent pair
Frequent reconnects:
- active streams resume across the tested blackout/reconnect path
- a full peer teardown and rebuild through
WatchConnectionis covered by tests; the live reconnect run recovered on the same WebRTC session
Server-side DNS issues:
./server -room new -dns 8.8.8.8:53Server egress through another SOCKS5 proxy:
./server -room new -socks 127.0.0.1:9050jazztun поднимает локальный SOCKS5-прокси и отправляет TCP-трафик через комнату Salute Jazz на удалённый сервер. Приложение или браузер подключается к 127.0.0.1:1080, а удалённая машина становится точкой выхода трафика.
Почему это работает там, где обычный VPN режут: транспорт выглядит как обычная browser-сессия видеозвонка на Jazz signaling, TURN и WebRTC DataChannels, а не как классический VPN-туннель.
Чтобы получать доступ к ресурсам, заблокированным в РФ, удалённый сервер должен находиться за пределами РФ. Если сам Salute Jazz в сети доступен или внесён в белый список, jazztun получает это преимущество вместе с ним.
┌────────────────────┐ SOCKS5 ┌────────────────┐
│ Браузер / программа├──────────────►│ jazztun client │
└────────────────────┘ └──────┬─────────┘
│
│ mux-кадры, зашифрованные AES-256-GCM
▼
Salute Jazz signaling + WebRTC DataChannel
│
▼
┌──────┴─────────┐
│ jazztun server │
└──────┬─────────┘
│
▼
Удалённый TCP-сервис
- TCP-туннель поверх комнаты Salute Jazz
- локальный SOCKS5-прокси на клиенте
- опциональная RFC 1929 username/password auth для локального SOCKS5-прокси
- AES-256-GCM для кадров туннеля
- несколько параллельных transport peer-ов через
-peers N - session-scoped pairing через
-session - несколько независимых пар могут делить одну Jazz-комнату
- привязка каждого stream к одному peer-у для сохранения порядка TCP-байтов
- credit-based flow control в mux
- кастомный DNS-резолвер на стороне сервера
- опциональный upstream SOCKS5-прокси для исходящих подключений сервера
- нет UDP associate
- нет bind
- контроль доступа фактически держится на room URL, общем ключе и совпадающем
-session - полный teardown и rebuild peer-а через
WatchConnectionпокрыт тестами; в живом reconnect-тесте восстановление произошло на той же WebRTC-сессии
Готовые бинарники для Linux, Windows и macOS (Apple Silicon) публикуются на странице релизов.
Контейнерный образ сервера публикуется в GitHub Container Registry:
docker run --rm -it ghcr.io/kavun-sama/jazztun-server:latest -room newВ каждом релизе есть checksums.txt. Перед запуском лучше проверить контрольные суммы:
sha256sum -c checksums.txtТребования:
- Go
1.25или новее - сетевой доступ к сигналингу Salute Jazz и ICE/TURN-инфраструктуре
- удалённая машина за пределами РФ, с которой можно достучаться до целевых TCP-хостов, если нужен доступ к заблокированным в РФ ресурсам
Linux или macOS:
go build -o server ./cmd/server
go build -o client ./cmd/clientWindows PowerShell:
go build -o server.exe .\cmd\server
go build -o client.exe .\cmd\clientВывести версию бинарника:
./server -version
./client -versionСобрать образ локально:
docker build -t jazztun-server:local .Или скачать уже опубликованный образ:
docker pull ghcr.io/kavun-sama/jazztun-server:latestЗапустить напрямую:
docker run --rm -it jazztun-server:local -room new -peers 4Запустить опубликованный образ напрямую:
docker run --rm -it ghcr.io/kavun-sama/jazztun-server:latest -room new -peers 4Или использовать готовый compose-файл:
docker compose -f docker-compose.server.yml up --buildПеременные окружения для compose:
JAZZTUN_ROOM, по умолчаниюnewJAZZTUN_KEYJAZZTUN_SESSIONJAZZTUN_PEERS, по умолчанию4JAZZTUN_DNS, по умолчанию1.1.1.1:53JAZZTUN_SOCKSJAZZTUN_VERBOSE, любое непустое значение включает-v
- Подготовьте удалённый сервер за пределами РФ.
- Скопируйте туда
jazztun. - Запустите
serverна удалённой машине. - Скопируйте из вывода room URL и ключ.
- Запустите
clientлокально с тем же room URL и ключом. - Укажите в браузере или приложении локальный SOCKS5-прокси.
- Проверьте, что внешний IP совпадает с IP удалённого сервера.
Если комнату использует только одна пара server/client, флаг -session можно не указывать. Если в одной и той же Jazz-комнате должны жить несколько независимых пар, каждой паре нужно своё значение -session, одинаковое на обеих сторонах.
Пример запуска на удалённой машине:
mkdir -p ~/jazztun
cd ~/jazztun
chmod +x ./server
./server -room new -peers 4 -vПример запуска клиента локально:
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4 -listen 127.0.0.1:1080 -vЕсли локальный SOCKS5 должен быть доступен другим устройствам в локальной сети:
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -listen 0.0.0.0:1080 -socks-user demo -socks-pass strongpassТеперь одна Jazz-комната может нести несколько независимых пар jazztun.
- один room URL + один ключ + одинаковый
-session: одна и та же пара туннеля - один room URL + один ключ + разный
-session: разные независимые пары - один room URL + один ключ + одинаковый
-sessionу двух клиентов одновременно: конфликт, лишний клиент игнорируется
Пример:
./server -room new -peers 2 -session alpha
./client -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 2 -session alphaВторая независимая пара в той же комнате:
./server -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 2 -session beta
./client -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 2 -session betaСначала запускается сервер:
./server -room new -peers 4 -vСервер выведет room URL и сгенерированный ключ.
Потом запускается клиент с тем же room URL и ключом:
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4 -vПо умолчанию локальный SOCKS5 слушает 127.0.0.1:1080.
./server -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4
./client -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4Типовые команды сервера:
./server -room new -peers 4 -v
./server -room new -peers 4 -session alpha
./server -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -dns 8.8.8.8:53
./server -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -socks 127.0.0.1:9050Типовые команды клиента:
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -peers 4 -v
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -session alpha -peers 4
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -listen 127.0.0.1:1081
.\client.exe -room "https://salutejazz.ru/ROOM?psw=..." -key YOUR_KEY -listen 0.0.0.0:1080 -socks-user demo -socks-pass strongpassСервер:
-room: room URL илиnew-key: 64-символьный hex-ключ; на сервере может быть сгенерирован автоматически-session: опциональный namespace, если одна Jazz-комната делится между несколькими независимыми парами-duo: быстрый режим на два transport peer-а-peers: количество transport peer-ов; перекрывает-duo; этим флагом обычно настраивается пропускная способность-dns: DNS-резолвер для удалённых lookup, по умолчанию1.1.1.1:53-socks: upstream SOCKS5-прокси для исходящих подключений сервера-v: подробные логи-version: вывести версию и завершиться
Клиент:
-room: один room URL-key: тот же 64-символьный hex-ключ, что и у сервера-session: опциональный namespace; при общем room URL должен совпадать с сервером-listen: адрес локального SOCKS5, по умолчанию127.0.0.1:1080-socks-user: включает RFC 1929 username/password auth для локального SOCKS5-прокси-socks-pass: пароль для-socks-user-duo: быстрый режим на два transport peer-а-peers: количество transport peer-ов; перекрывает-duo; этим флагом обычно настраивается пропускная способность-v: подробные логи-version: вывести версию и завершиться
Полная проверка:
go test ./...
go vet ./...Проверка по пакетам:
go test ./internal/crypto -v
go test ./internal/mux -v
go test ./internal/transport/jazz -v- Запустите
server. - Запустите
clientс тем же room URL и ключом. - Проверьте внешний IP через локальный SOCKS5:
curl.exe --socks5-hostname 127.0.0.1:1080 https://ifconfig.me/ipЕсли туннель работает, вернётся IP удалённого сервера.
На удалённом сервере:
mkdir -p /root/bench
dd if=/dev/zero of=/root/bench/100m.bin bs=1M count=100 status=none
python3 -m http.server 8088 --directory /root/benchНа клиентской машине:
curl.exe --max-time 15 --socks5-hostname 127.0.0.1:1080 -o NUL -w "speed=%{speed_download} bytes/s time=%{time_total}s code=%{http_code}`n" http://SERVER_IP:8088/100m.binНаблюдаемые значения на текущем коде:
- один поток: примерно
33-43 Mbit/s - aggregate throughput на
-peers 6и восьми параллельных загрузках: около110 Mbit/s
Не принимается room URL:
- нужен полный invite link, включая
?psw=...
Туннель поднялся, но трафик не идёт:
- проверьте, что ключ на обеих сторонах одинаковый
- проверьте, что
-sessionсовпадает на обеих сторонах, если вы его используете - проверьте, что приложение использует именно SOCKS5, а не HTTP proxy
- проверьте, что сервер может резолвить и открывать целевой хост
В той же комнате второй клиент не подключается:
- проверьте, не используют ли оба клиента один и тот же
-session - один
-sessionсоответствует одной логической паре; для независимой второй пары задайте другое значение
Частые reconnect:
- активные потоки переживают протестированный blackout/reconnect сценарий
- полный teardown и rebuild peer-а через
WatchConnectionпокрыт тестами; в живом reconnect-тесте восстановление произошло на той же WebRTC-сессии
Проблемы с DNS на сервере:
./server -room new -dns 8.8.8.8:53Нужно выпускать трафик сервера через другой SOCKS5:
./server -room new -socks 127.0.0.1:9050
English
Русский