Skip to main content

sh-xray — единый VPN-шлюз (skill-hub xray) для всех AI-навыков

Один общий SOCKS5-прокси (xray-core) с НЕ-российской нодой и smart-routing (РУ/CN → напрямую, остальное → за рубеж). Пишет общий указатель ~/.xray-gate/state.json, который читают ВСЕ навыки (паттерн open_client._apply_proxy). Системный трафик НЕ трогает (только opt-in инструменты через ALL_PROXY) — РУ-сервисы не ломаются.

Команды

CLI xray-gate — на clikit (даёт --json/--help/--version):

xray-gate install            # xray binary + geoip/geosite (качает с GitHub-релиза XTLS/Xray-core)
xray-gate test               # выбрать рабочую НЕ-RU ноду (проверка реального выхода через ipinfo.io)
xray-gate start [--port N]   # поднять + записать ~/.xray-gate/state.json (порт по умолч. 10810)
xray-gate status             # alive + live exit country
xray-gate stop
xray-gate ensure             # self-heal: поднять, если процесс умер (после reboot/краша)
xray-gate onboard            # детект окружения (прокси/движки) + per-service карта routing.json
xray-gate ensure-reachable <service|--url URL>   # оркестратор доступности (см. ниже); exit 0/3/1
xray-gate autostart          # Windows Task Scheduler: ensure каждые 10 мин (переживает reboot)
xray-gate autostart --remove
xray-gate add-sub <url>      # своя VLESS-подписка (vless/base64; авто-обновление по интервалу подписки)

Оркестратор доступности ensure_reachable (единая точка встраивания)

Одна функция вместо ручной цепочки probe→detect→byedpi→Gateway — для встраивания gate в КАЖДЫЙ сервис-навык на установке/старте. Проверяет доступность цели и эскалирует: существующий egress (системный прокси + скан локальных прокси-портов Hiddify/v2rayN/clash/…

  • уже поднятые движки) → byedpi (РКН-DPI, sni-dpi) → xray-демон+подписка (гео/тупик).
from xray_gate import ensure_reachable
res = ensure_reachable("notebooklm")            # или ensure_reachable(url="https://…/", ok_codes=(200,302))
if res.ok:                     proxy = res.proxy            # None → напрямую; иначе socks5h/http
elif res.need_subscription:    ...              # агент просит подписку → xray-gate add-sub <url> (пакет НЕ падает)
else:                          ...              # тупик: res.detail / res.tried

ReachResult: ok, engine ("local|direct|system|byedpi|xray"), proxy, bind, exit_country (ЗАМЕРЕННАЯ страна выхода), need_subscription, block_kind, detail, tried (лог попыток). Предикаты has_subscription() / subscription_configured() — есть ли рабочая подписка/нода (вместо sys.exit). CLI: xray-gate ensure-reachable <service|--url URL> [--no-byedpi] [--no-xray] (exit 0 — доступно, 3 — нужна подписка, 1 — тупик). Без установки китов (свежая машина, handoff-бандл) — тот же набор через dep-free ядро: python xray_gate/_core.py <команда> (чистый stdlib, никаких зависимостей). Именно его дёргает watchdog (ensure каждые 10 мин), чтобы self-heal не зависел от китов.

Рубильник «только прямой канал» (герметичные прогоны)

Одна переменная окружения выключает кит целиком: он не перечисляет процессы, не сканирует локальные порты, не ходит в сеть и ничего не поднимает — все двери честно отдают «прямой канал».

XRAY_GATE_DIRECT_ONLY=1 pytest        # каноническое имя
NLM_NO_PROXY=1 pytest                 # историческое имя — равноправно и продолжает работать

Читаются 1/true/yes/on (регистр не важен); пустая строка и 0 — выключено.

Рубильник действует одинаково во ВСЕХ дверях: proxy_for, apply_to_env, resolve_proxy, ensure_reachable, endpoint_listening, scan_local_proxy_ports, detect_vpn_tun, adapters.* (httpx/requests/playwright/telethon), localnet.* (list_adapters, tunnel_active, physical_bind_ip, local_egress_proxy, ensure_local_egress, local_exit_geo). Проверка живёт ОДНОЙ функцией (xray_gate.switch.direct_only), и набор кита сплошняком следит, чтобы второй копии не завелось — именно расхождение копий и дало дефект 0.3.7.

Чего рубильник НЕ отменяет — явные административные команды: xray-gate up, Gateway.ensure(). Кто их набрал, тот просит поднять демон, а не «выбери мне выход».

Российский адрес для российских сервисов (движок local)

Слово «напрямую» раньше значило «мимо НАШЕГО прокси»: проба шла curl --noproxy "*". Системный TUN-адаптер (Hiddify, Outline, WARP) заворачивает такой трафик маршрутами, и --noproxy про него ничего не знает. Замер на машине владельца:

канал адрес страна оператор
direct (через TUN) 212.34.142.82 NL AS216071 SERVERS TECH FZCO
физический адаптер 81.177.232.243 RU AS48176 MTS PJSC

То есть правило «RU-сервис ходит напрямую» не выполнялось вообще: VK, OK, Дзен, RuTube, VC.ru видели голландский адрес — капча, internal-error, 401.

Обход без прав администратора, без правки маршрутов и без вмешательства в чужие процессы: исходящий сокет привязывается к адресу физического адаптера (Windows strong host model — маршрут туннеля к такому сокету неприменим). Наружу это отдано обычным proxy-URL, поэтому навыкам не нужна новая сантехника:

from xray_gate import ensure_reachable
res = ensure_reachable("vk")
res.engine        # "local"
res.proxy         # "socks5h://127.0.0.1:10812"
res.exit_country  # "RU" — ЗАМЕРЕНО, а не настроено

Замерить все каналы разом: xray-gate egress [--up] → таблица «канал → адрес → страна» (direct / физический / локальный прокси / шлюз). Снять: xray-gate egress-stop. Переопределить адрес привязки: XRAY_GATE_BIND_IP=<ip>.

РУ-плечо smart-routing самого шлюза и пула (РУ/CN → direct) тоже привязано — раньше его direct уходил в тот же туннель.

Построен на Китах (S-kits)

sh-xray — НЕ кит, а супернавык/сервис, который строится на китах: clikit даёт человеческий CLI (xray_gate/cli.py), librarykit.config_util — атомарную запись общих указателей (~/.xray-gate/state.json, ~/.xray-gate/routing.json). Ядро-движок (xray_gate/_core.py, бывший top-level gateway.py) намеренно остаётся на чистом stdlib — у обоих kit-обращений есть stdlib-фолбэк, поэтому self-heal работает даже без китов. gateway-роутер потребляет egress sh-xray через свой ProxyProvider (читает ~/.xray-gate/state.json).

Переживает перезагрузку

autostart ставит задачу планировщика (ensure каждые 10 мин, StartWhenAvailable). После reboot старый pid мёртв → ensure поднимает xray заново (при старте/в течение ~10 мин). ~/.xray-gate/state.json обновляется новым pid, все навыки снова ходят через прокси. Без autostart — поднимать вручную start.

Общий указатель ~/.xray-gate/state.json

{"http_proxy": "http://127.0.0.1:10811", "all_proxy": "socks5h://127.0.0.1:10810", "node": "...", "backend": "xray-gate"}

Потребители читают его и ходят через ОДИН egress (важно: один IP для логина И API, иначе Google убивает сессию). Всё хозяйство рантайма — под ~/.xray-gate/ (state.json/routing.json/byedpi/zapret/бинарь).

Потребление в проектах (egress «из коробки»)

xray-gate — источник пути (его знает он, не нейтральный librarykit-корень). Потребитель ставит его как egress-расширение и берёт прокси у его адаптеров. Установка (на PyPI НЕТ — из git):

uv add --optional egress "s-xray-gate @ git+https://gitlab.com/s-skills/s-xray-gate.git"  # запишет в pyproject+lock
uv sync --extra egress                                   # или: pip install git+https://gitlab.com/s-skills/s-xray-gate.git

4 способа взять прокси (нет указателя/шлюз не поднят → None → проект идёт напрямую):

import xray_gate.adapters as eg
httpx.Client(proxy=eg.httpx_proxy())                       # socks5h://127.0.0.1:10810
sess = eg.requests_session()                                # requests.Session с прокси
browser.launch(proxy=eg.playwright_proxy())                 # {"server":"socks5://..."}
TelegramClient(s, api_id, api_hash, proxy=eg.telethon_proxy())

# через нейтральный librarykit-транспорт (проект на китах):
from librarykit import HttpClient, HttpxTransport
HttpClient(HttpxTransport(proxy=eg.httpx_proxy()), auth, errmap)

# env-wide (subprocess/curl/любой ALL_PROXY-клиент):
from xray_gate import apply_to_env; apply_to_env("notebooklm")   # по карте ~/.xray-gate/routing.json

# zero-dep (без установки пакета — просто JSON):
import json, pathlib
proxy = json.loads((pathlib.Path.home()/".xray-gate"/"state.json").read_text())["all_proxy"]

Реальный пример-потребитель — bublictr (adapters/_common/browser.py): BUBLICTR_BROWSER_PROXY=shxray → xray_gate.adapters.playwright_proxy().

Портативность / другие устройства

install качает xray-core сам. add-sub <url> + test подберёт не-RU ноду. Так работает на любой машине/агенте.

Замечание по нодам

Качество зависит от подписки. Нестабильная нода → таймауты (xray ретраит, но лучше иметь несколько не-RU нод). status показывает live exit; test перевыбирает рабочую.

Metadata

Release files for s-xray-gate 0.3.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for s-xray-gate 0.3.8
File Size Uploaded
s_xray_gate-0.3.8.tar.gz 127.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for s-xray-gate 0.3.8
File Interpreter ABI Platform
s_xray_gate-0.3.8-py3-none-any.whl Python 3 none any Details

Total release size: 231.8 kB

Release files / s_xray_gate-0.3.8.tar.gz

Download URL s_xray_gate-0.3.8.tar.gz
Size 127.3 kB
Tags Source
SHA-256 checksum
How to use checksums
c5e1776105fe9ed2d5ac42308978386bea589bbea1386c13397df7f817c0d50d
BLAKE2b-256 checksum
How to use checksums
31a3451c7ca6158fdf2b12041981ff6e2463be4d5c577d67d2ec0c6aee928f7a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / s_xray_gate-0.3.8-py3-none-any.whl

Download URL s_xray_gate-0.3.8-py3-none-any.whl
Size 104.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6802fc20e8249eafa8dc39fee10ee92545f61aaf60460fe0c6f113f80617f54b
BLAKE2b-256 checksum
How to use checksums
215a731bfbac9ba12f4baf908b46d33915ce542fc92d9058f2c947e6f858b095
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.3.9

2 release files

This release

0.3.8 This release

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page