Самопроверяющийся агрегатор публичных прокси-конфигураций: VLESS Reality, VMess, Trojan, Shadowsocks и Hysteria2, написан на Go
Бесплатные VPN-конфиги и подписки V2Ray для обхода блокировок и белых списков. Каждый узел перед публикацией проверяется настоящим HTTPS-запросом через собственный туннель, а не пингом. Подписки подходят для v2rayNG, Hiddify, Happ, Streisand и других клиентов.
Впервые видите ShellShift или хотите точно знать, что значит «доказан»? Начните с раздела «Как это работает» или прочитайте всё на английском.
Caution
ShellShift не VPN-сервис. Он не держит и не обслуживает ни одного сервера. Трафик идёт от вашего клиента прямо на узел, который держит кто-то другой; эти операторы неизвестны и видят то, что через них проходит. Про каждый опубликованный узел установлено ровно одно: через него завершился HTTPS-запрос с проверенной цепочкой сертификатов, в момент, который показывает дашборд.
- Документация
- Возможности
- Как это работает
- Скриншоты
- Установка
- Клиенты
- Сетевые обращения
- Архитектура
- Благодарности
- Перевод
- Участие в разработке
- Поддержка
- Ссылки
- Лицензия
- Всё, чего ждёшь от агрегатора прокси, кроме одного: ничего не публикуется на веру.
- Каждый узел поднимается внутри процесса через собственный outbound sing-box, поэтому битый узел может уронить только себя.
- Узел считается доказанным только после того, как через его туннель прошёл полный HTTPS-запрос с проверенной цепочкой сертификатов. Ответ
204по открытому HTTP доказательством не считается, а проверка сертификатов не отключается никогда. - Планирование по фактам: сначала дешёвый сигнал доступности, затем дедлайны по наблюдаемому p95 времени соединения, гонка проб под одним жёстким дедлайном и ранний выход. Безусловных пауз нет.
- Необязательный замер пропускной способности поверх задержки.
- Команда
doctorза две минуты отличает «узлы мертвы» от «эта машина не достаёт до эталонных адресов».
- VLESS (Reality и TLS), VMess, Trojan, Shadowsocks (включая 2022-blake3) и Hysteria2.
- Транспорты tcp, ws, grpc, httpupgrade / xhttp, http / h2 и quic.
- uTLS-отпечатки, плагины SIP003, публичные ключи и short ID для Reality.
- Закрытые множества: всё нераспознанное отклоняется на границе с причиной, которую можно посчитать, а не тащится дальше строкой.
Полная матрица протоколов
| Поддерживается | |
|---|---|
| Протоколы | vless · vmess · trojan · ss (Shadowsocks) · hy2 / hysteria2 |
| Транспорты | tcp (raw, none) · ws · grpc · httpupgrade (xhttp) · http / h2 · quic |
| Защита | none · tls · reality |
| uTLS-отпечатки | chrome, firefox, edge, safari, 360, qq, ios, android, random(ized) |
| Шифры Shadowsocks | AES-128/192/256-GCM, (x)chacha20-ietf-poly1305 и три шифра 2022-blake3 |
| Плагины SIP003 | obfs-local (simple-obfs) · v2ray-plugin |
Hysteria v1 отклоняется везде, намеренно и единообразно. Предшественник принимал её в одной ветке кода и игнорировал в другой, поэтому ссылки v1 выпадали из источников, но пропускались из пула и считались v2 (INV-12).
- Семь текстовых файлов подписок: всё, белый список и по одному на протокол.
- В
sub_wl.txtпопадают Reality-узлы, у которых SNI есть в белом списке цензора и маскировочный сайт проверен напрямую: он существует и отдаёт валидный сертификат на собственное имя. network.json: полный машиночитаемый срез, то есть воронка, доказательства по каждому узлу и всё, что показывает дашборд.- Ни аккаунтов, ни токенов, ни персональных ссылок. Все получают одни и те же байты.
- Набор, в который никто не прошёл, всё равно публикуется с заголовком, а не исчезает.
Публикуемые файлы
| Файл | Содержимое |
|---|---|
sub_all.txt |
все доказанные узлы, по возрастанию задержки |
sub_wl.txt |
Reality-узлы с SNI из белого списка и доказанной маскировкой |
sub_vless.txt |
VLESS поверх Reality или TLS: самый распространённый протокол и обычно самый живучий против DPI |
sub_vmess.txt |
VMess |
sub_trojan.txt |
Trojan |
sub_ss.txt |
Shadowsocks |
sub_hy2.txt |
Hysteria2 |
network.json |
воронка, доказательства по узлам и всё, что показывает карточка узла |
state/ledger.json |
память прогона; публикуется, чтобы следующий прогон забрал её обратно |
- Статический двуязычный сайт (русский и английский) со светлой и тёмной темой.
- Полоса пульса: когда получены данные и свежие ли они.
- Воронка в виде бухгалтерского тождества: каждый этап считает, что отбросил, и числа сходятся.
- Состав каталога по протоколам и странам, таблица узлов с поиском и фильтрами.
- Карточка узла, в которой ссылка, сервер, IP и учётные данные скрыты, пока не нажата кнопка «Показать».
new - Адреса узлов есть только в загружаемом срезе и никогда в HTML, так что поисковику нечего индексировать, а скриншоту нечего выдать.
- Собственная Content-Security-Policy.
- Валидация по схеме и явным белым спискам, а не по чёрным.
- Один раз разрешить имя, проверить полученный адрес и подключаться именно к нему. Это же закрывает окно для DNS rebinding.
- Защита исходящих соединений для каждого протокола и транспорта: loopback, частные сети, CGNAT, link-local (включая адрес облачных метаданных), multicast, зарезервированные диапазоны и их IPv6-аналоги запрещены.
allowinsecure,insecureиallow_insecureвыбрасываются при публикации, поэтому опубликованная ссылка никогда не просит клиента пропустить проверку сертификата.- Секреты в логах маскируются, а ID узла представляет собой SHA-256 от канонической формы, а не сами учётные данные.
- Плановая публикация каждые два часа в
:17, прямо в GitHub Pages через Pages-артефакт. Ничего сгенерированного не коммитится вmain. - Ветка-зеркало: каждый прогон ещё и перезаписывает подписки в ветке
subsодним коммитом без истории. Так у них появляется второй адрес наraw.githubusercontent.com, а ветка никогда не разрастается. - Круговорот памяти: память прогона (только хеши и статистика) публикуется вместе с сайтом и забирается следующим прогоном. Ни кэша, ни git.
- Защита публикации: прогон может заменить то, что сейчас живёт на сайте, только если его результат правдоподобен. Пустой прогон никогда не стирает подписки.
- Список источников хранится в секретах репозитория и маскируется в логах построчно.
- Готовые бинарники для Linux, macOS и Windows на amd64 и arm64 с контрольными суммами SHA-256.
new
- Источники по реальной отдаче: измеренная история показала, что один источник давал 48% всех живых узлов при 3% объёма разбора, поэтому источники упорядочены по тому, что они действительно дают, а не по кругу.
- Пул, который стареет: интервалы перепроверки растут вместе с наблюдаемой стабильностью, неудачи откладываются экспоненциально, устаревшие записи вымываются.
decode -rejects: подайте на вход любой источник и увидите каждую отклонённую строку с причиной.paramdiff: перекодирует корпус офлайн и показывает, какие query-параметры публикация теряет, добавляет или меняет.proofbench: сырое распределение ошибок доказательства по транспортам, чтобы подбирать бюджеты и параллелизм.- 56 инвариантов (
INV-00…INV-55), каждый закрывает реальный сбой, за который предшественник уже заплатил, и каждый назван по номеру в тесте.
Один статический бинарник на Go проводит один цикл сбора от начала до конца:
- Сбор: источники подписок скачиваются по HTTPS с жёсткими лимитами по байтам, строкам и времени.
- Разбор: каждая строка превращается в проверенный узел или отклоняется с причиной, которую машина может посчитать.
- Проверка на границе Zero-Trust: только разрешённые поля, одно разрешение DNS, закреплённый адрес.
- Доказательство: для одного узла внутри процесса собирается outbound sing-box, и через него должен завершиться HTTPS-запрос с проверенной цепочкой сертификатов.
- Классификация: настоящий ли маскировочный сайт у Reality? Проверяется напрямую, а не берётся из списка.
- Публикация выживших, но только если результат достаточно правдоподобен, чтобы заменить то, что уже опубликовано.
Воронка, публикуемая с каждым прогоном, это бухгалтерское тождество, а не сводка:
Collected == Rejected + Duplicates + Deferred + NotAttempted + Failed + ProvenЭтапы воронки
| Этап | Смысл |
|---|---|
| collected | строки, скачанные из всех источников |
| rejected | строки, которые разбор отклонил, с подсчётом по причинам (unknown_scheme, bad_uuid, unsupported_cipher, bad_psk_length, bad_reality_key, …) |
| decoded | строки, давшие валидный узел |
| duplicates | разобранные узлы, которые уже назвал другой источник |
| candidates | уникальные узлы после дедупликации |
| deferred | кандидаты, которых память признала не стоящими полной проверки в этом прогоне |
| policy | узлы, прошедшие границу Zero-Trust |
| reached | узлы, ответившие на дешёвый сигнал доступности |
| failed | узлы, которые проверка попробовала и отклонила |
| not_attempted | узлы, до которых очередь дошла бы, но бюджет прогона кончился раньше |
| proven | опубликованные узлы |
Именно подсчёт отказов по причинам делает регрессию парсера видимой, а не похожей на обычную текучку источников.
Note
Что точно значит «доказан»: HTTPS-запрос GET к одному из трёх известных адресов проверки связи (www.gstatic.com, cp.cloudflare.com, connectivitycheck.gstatic.com), завершённый через собственный outbound узла, с проверенной цепочкой сертификатов. Не 204 по открытому HTTP, который враждебный или captive-узел может подделать (INV-31), и никогда с отключённой проверкой (INV-30, INV-38).
| Тёмная тема | Светлая тема |
|---|---|
![]() |
![]() |
![]() |
![]() |
| Карточка узла | Телефон |
|---|---|
![]() |
![]() |
| Платформа | Стабильная |
|---|---|
| Linux | |
| macOS | |
| Windows |
Каждый архив содержит один статический бинарник, лицензию, оба README и пример конфигурации. Контрольные суммы лежат в SHA256SUMS.txt.
Ничего ставить не нужно. Откройте живой дашборд, скопируйте адрес подписки и вставьте его в клиент:
https://tuftytech.github.io/ShellShift/sub_all.txtЗамените sub_all.txt на sub_wl.txt, sub_vless.txt, sub_vmess.txt, sub_trojan.txt, sub_ss.txt или sub_hy2.txt, чтобы получить более узкий список.
Если в вашей сети заблокирован github.io, каждый прогон зеркалирует те же файлы в ветку subs:
https://raw.githubusercontent.com/TuftyTech/ShellShift/subs/sub_all.txt-
Скачайте и проверьте
Возьмите архив для своей платформы из таблицы выше, распакуйте его и сверьте с
SHA256SUMS.txt. -
Сначала получите положительные контроли
./shellshift doctor
doctorзапрашивает каждый эталонный адрес напрямую, без туннеля, а затем прогоняет сам механизм доказательства по заведомо рабочему пути. Если не прошло хоть что-то, проблема в вашей сети, а не в узлах. -
Дайте ему список источников
Скопируйте
config/sources.example.jsonвconfig/sources.jsonи добавьте массивsources, либо передайте список черезSHELLSHIFT_SOURCES(по одному URL в строке). Подробнее в разделе Конфигурация. -
Запустите цикл
# проверить и отчитаться, ничего не записывая ./shellshift run -config config/sources.json -dry-run # полный цикл: сбор, проверка, классификация, публикация в dist/ ./shellshift run -config config/sources.json -out dist -state .state/ledger.json
Все команды
| Команда | Что делает |
|---|---|
run |
полный цикл: сбор, проверка, классификация, публикация |
site |
собирает статический сайт из опубликованного network.json (без сети и без проверки) |
decode |
разбирает прокси-URI со stdin и показывает, что отклонено (-rejects, -json, -labels) |
doctor |
устанавливает положительные контроли, без которых диагноз невозможен (INV-55) |
version |
печатает версию сборки |
./shellshift site -data dist/network.json -out dist -origin https://tuftytech.github.io/ShellShift
./shellshift decode -rejects < some-feed.txtImportant
Тег сборки with_utls не опционален. sing-box держит uTLS, а вместе с ним и клиент Reality, за этим тегом. Без него каждый Reality-узел не собирается и молча отбрасывается (в одном реальном корпусе это было 540 узлов из 865), и выглядит это как деградация источников, а не как ошибка сборки. tunnel.CheckBuild() отказывается запускать прогон в таком состоянии (INV-49).
git clone https://github.com/TuftyTech/ShellShift.git
cd ShellShift
./tools/install-hooks.sh # один раз: ставит pre-commit хук гигиены
go build -tags with_utls -o shellshift ./cmd/shellshift
./tools/verify.sh # все гейты, которые гоняет CIНужен Go 1.26 или новее. Без cgo и без сопутствующих процессов.
-
Сделайте форк репозитория.
-
Добавьте секреты в Settings → Secrets and variables → Actions:
Секрет Назначение SHELLSHIFT_SOURCESсписок источников, по одному URL в строке (обязателен) SHELLSHIFT_WHITELIST_DOMAINSсписки доменов для классификации по белому списку (необязателен) SHELLSHIFT_WHITELIST_NETWORKSсписки CIDR для того же (необязателен) -
В Settings → Pages выберите для Source значение GitHub Actions.
-
Поменяйте
site_urlвconfig/run.jsonиSITE_ORIGINв.github/workflows/publish.ymlна свой адрес Pages. -
Запустите Actions → publish → Run workflow или дождитесь ближайшего слота в
:17.
Note
Плановый прогон отказывается стартовать без SHELLSHIFT_SOURCES и называет секрет по имени. Отсутствие списка для белого списка даёт только предупреждение, но оно звучит явно, чтобы «никто не прошёл» никогда не выглядело как «никто не мог пройти».
Один JSON-файл, загружаемый явно. config/sources.example.json показывает форму и намеренно не содержит ключа sources: строку, похожую на адрес источника в публичном дереве, кто-нибудь обязательно скопирует.
Поля и переопределения через окружение
| Поле | Смысл |
|---|---|
sources |
адреса источников для сбора |
whitelist_domains |
адреса списков доменов для классификации по белому списку |
whitelist_networks |
адреса списков CIDR для того же |
site_url |
опубликованный адрес, используется в ссылках подписок |
run_budget |
потолок по времени на фазу проверки (например, "6m") |
concurrency |
параллелизм проверки |
proof_concurrency |
параллелизм именно доказательных запросов |
geoip |
определять страну узла |
measure_throughput |
замерять пропускную способность в дополнение к задержке |
Три переменные заменяют соответствующие списки после чтения файла и до его валидации:
| Переменная | Заменяет |
|---|---|
SHELLSHIFT_SOURCES |
sources |
SHELLSHIFT_WHITELIST_DOMAINS |
whitelist_domains |
SHELLSHIFT_WHITELIST_NETWORKS |
whitelist_networks |
По одному URL в строке, пустые строки и комментарии # игнорируются. Два свойства сделаны намеренно, и на оба есть тесты:
- Переменная заменяет свой список, а не сливается с ним. Слияние молча оставило бы источники, которые оператор уже убрал.
- Переменная, которая задана, но пуста, считается ошибкой, а не поводом откатиться к файлу. Такой случай означает неправильно настроенный секрет.
Tip
Подойдёт любой клиент, который понимает адреса подписок, например:
- Адреса источников из вашей конфигурации: по HTTPS, с жёсткими лимитами по байтам, строкам и времени.
- Системный DNS-резолвер: один раз на узел. Полученный адрес проверяется и закрепляется для подключения.
- Сам узел: через его собственный outbound sing-box внутри процесса, и никогда на частный или зарезервированный адрес.
www.gstatic.com,cp.cloudflare.com,connectivitycheck.gstatic.com: доказательный запрос, отправленный через узел.- Маскировочные сайты Reality: прямое TLS-рукопожатие на порт 443, чтобы проверить, что сайт существует и отдаёт валидный сертификат на собственное имя.
ip-api.com: пакетное определение страны для доказанных узлов. Поставьтеgeoipвfalse, чтобы отключить.- Ваш опубликованный сайт: плановый workflow забирает с него предыдущий
state/ledger.json.О посетителях дашборда ничего не собирается и не хранится: это статические файлы.
Один статический бинарник на Go. Ни сопутствующего интерпретатора, ни sidecar-процесса, ничего не читается в момент импорта. Каждая подкоманда является чистой функцией от своих флагов и файлов, на которые ей указали.
Пакеты и несущие решения
cmd/shellshift run · site · decode · doctor · version
internal/model канонические типы; структурная идентичность узла
internal/decode URI -> проверенный узел; здесь живут INV-01..29
internal/intake скачивание источников с жёсткими лимитами, порядок по отдаче
internal/policy граница Zero-Trust: защита исходящих, резолвер, белый список, маскировка, geoip
internal/tunnel единственный интерфейс подключения; sing-box как библиотека, один outbound на узел
internal/verify вердикт: HTTPS с проверенной цепочкой; адаптивные бюджеты; пропускная способность
internal/ledger память: пул со старением, отдача источников
internal/publish наборы, network.json, статический сайт (html/template + встроенные ресурсы)
internal/pipeline один цикл сбора; планировщик параллелизма
internal/obs структурные логи с маскировкой секретов
internal/config явная загрузка конфигурации- sing-box используется как библиотека, а не как подпроцесс. Предшественник запускал один процесс sing-box на пачку из пятидесяти узлов с общей конфигурацией, так что один отклонённый outbound убивал все пятьдесят (
INV-00). - Подключению передаётся разрешённый адрес, а не имя хоста. Получив имя, outbound внутри процесса потребовал бы доступных из враждебного ввода DNS, маршрутизации и реестров сервисов sing-box (
INV-33,INV-48). - Идентичность структурная и хешированная, потому что кортеж содержит учётные данные, а ключ пишется в логи, состояние и на дашборд (
INV-21). - Сборка результата и его публикация разделены.
pipeline.Runnerничего не пишет, решаетpublish.Guard. Предшественник опустошал все файлы подписок в прогоне без выживших (INV-43). - Артефакты никогда не коммитятся в
main. Предшественник коммитил около 406 КБ перегенерированных файлов за прогон, шесть раз в день. Зеркалоsubsсостоит из одного коммита без истории, который заменяется на каждом прогоне, поэтому в нём всегда одна копия.
Тесты и гейты
./tools/verify.sh запускает gofmt, go build, go vet, тесты с детектором гонок, проверку на пропущенные тесты, govulncheck, гейты гигиены и smoke-тест CLI. Он отказывается выдавать незапущенный гейт за пройденный.
- Пропущенный тест роняет CI.
- Инвариант считается перенесённым, только когда тест называет его номер.
tools/hygiene.shпадает, если объявленныйINV-nnне назван ни в одном_test.go. - Каждый action в workflow запинен по 40-символьному SHA коммита, а не по подвижному тегу.
- Ни сгенерированный артефакт, ни бинарник, ни строка, похожая на секрет, не могут попасть в индекс.
- Два гейта точности охраняют публикацию: опции sing-box, собранные из исходного URI, сравниваются с нашими, а отдельная проверка падает, когда публикация начинает терять query-параметр без объявленной причины.
Сам каталог инвариантов хранится вне публичного дерева, потому что в нём были рабочие адреса. На чистом клоне этот гейт сообщает НЕ ПРОВЕРЕНО вместо «пройдено» и говорит об этом в последней строке.
- Дашборд говорит на русском и английском. Все строки интерфейса лежат в
internal/publish/strings.go, аTestStringsNamedFromGoExistInBothLanguagesпадает, если ключ есть в одном языке и отсутствует в другом. - Хотите ещё один язык? Добавьте таблицу рядом с
ruиenи откройте pull request.
- Прочитайте
internal/decode,internal/verifyиinternal/tunnelперед тем, как их менять: защиты там не стилистические. - Запускайте
./tools/verify.shперед пушем. Это тот же набор гейтов, что гоняет CI. - Называйте инвариант, который тест охраняет (
INV-nn), или правило, которое он кодирует (GHOST-nnn). - Комментарии объясняют почему и ссылаются на сбой, ради которого существует защита.
- Ошибка несёт причину, которую машина может посчитать, а не только сообщение.
- Отсутствующее значение отсутствует и никогда не подменяется заглушкой.
- Гейт, который не может упасть, хуже отсутствия гейта.
- Баги, идеи и вопросы идут через формы issue. Проблемы безопасности сообщайте приватно.
Автор коммитов
Коммиты подписываются как tuftyxxeW <343729923+tuftyxxeW@users.noreply.github.com> и не содержат трейлера Co-Authored-By ни для какого инструмента и ни в какой форме. GitHub строит список Contributors и по адресу автора, и по трейлерам соавторов, поэтому важны обе половины. На свежем клоне это нужно выставить до первого коммита:
git config user.name "tuftyxxeW"
git config user.email "343729923+tuftyxxeW@users.noreply.github.com"- Лучшая поддержка: отчёт о рабочем узле и понятный баг-репорт.
- Не забудьте поставить 🌟 репозиторию, если ShellShift вам пригодился ^^
- 📡 Живой дашборд
- 🐛 Issues
- 🛰️ Обращение по узлу для операторов узлов и правообладателей. Обоснованные обращения исполняются, обычно к следующему плановому прогону.
- 🔒 Сообщения о безопасности
Warning
ShellShift живёт только на github.com/TuftyTech/ShellShift и tuftytech.github.io/ShellShift. Любой другой сайт, предлагающий подписки или бинарники «ShellShift», не имеет к нам отношения, и мы не отвечаем за то, что вы там скачаете.
Проект распространяется по лицензии GNU General Public License v3.0 or later.
© Copyright (C) 2026-present TuftyTech
- ShellShift статически линкует sing-box и sing (GPL-3.0-or-later) в один бинарник,
поэтому совместное произведение тоже распространяется под GPL-3.0-or-later.
- sing-box и sing принадлежат © 2022 nekohasekai, с дополнительным условием по разделу 7 GPL:
производное произведение не может использовать их название или намекать на связь без согласия.ShellShift не связан с проектом sing-box и его авторами, не одобрен ими и не аффилирован с ними. Названия протоколов (VLESS, VMess, Trojan, Shadowsocks, Hysteria2, Reality) и клиентов используются описательно; все товарные знаки принадлежат их владельцам.





