feat(happ): split-профиль клиента из тех же наборов, что и bypass - #23
Open
tony-adamson wants to merge 4 commits into
Open
feat(happ): split-профиль клиента из тех же наборов, что и bypass#23tony-adamson wants to merge 4 commits into
tony-adamson wants to merge 4 commits into
Conversation
The subscription endpoint ships a client-side routing profile in the routing: response header, and the managed default tunnels everything. The override file that changes it was mentioned in one line of the architecture docs, without a schema, an example, or the difference from server-side bypass routing. Bypass routing only changes where the server sends a request; the client still tunnels it, so a Russian site keeps seeing the VPS address. On a node without a cascade the catch-all outbound is already direct, so bypass cannot help there. Split routing is only reachable through the client profile, which made this the most common thing operators had to rediscover on their own. Adds a Configuration section in en/ru/zh-CN covering the two environment variables, GlobalProxy semantics, a complete split-tunnel example, and the three mistakes that are easy to make: Telegram matching by IP rather than domain, LastUpdated needing to grow, and geo database URLs being per-subscriber. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0121QZ51XSKaiKcRrtoVQTDg
… request
The managed routing profile points HAPP at /sub/<token>/geoip.dat, which is
scoped to one subscriber. HAPP_ROUTING_JSON_FILE, in contrast, is served to
every subscriber, so an operator who wants a custom profile has to either embed
one subscriber's token in a file everyone receives, or host the geo databases
somewhere else entirely.
Adds {{GEOIP_URL}}, {{GEOSITE_URL}} and {{LAST_UPDATED}} placeholders that
subhttp.sh resolves per request. Substitution runs before schema validation, so
an unresolved placeholder fails the https_url check and the managed default is
served rather than a broken profile — including the local-only case where no
public base URL exists.
{{LAST_UPDATED}} resolves to the newest mtime among xrayebator and the two geo
databases, which removes the manual step of raising the value after every edit;
HAPP re-imports a profile only when it grows.
Verified: bash -n on xrayebator and on the generated subhttp.sh; substitution
checked for the resolved, no-placeholder and empty-base-url cases.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0121QZ51XSKaiKcRrtoVQTDg
…ass bundles
Bypass routing already carries curated bundles of Russian services that should
not travel through the VPN: banks, marketplaces, gosuslugi, VK, Yandex, Mail.ru,
streaming, Steam. Those bundles only affect the server's own egress though. The
client keeps tunnelling the request, so on a node without a cascade — where the
catch-all outbound is already direct — Ozon, the banks and Gosuslugi still see
the VPS address and answer with a country stub.
Adds 'HAPP subscription -> 7) Маршрутизация клиента', which writes
.happ_routing.json from the very same bundles, so both halves of the split are
driven by one list. GlobalProxy stays "true": anything unknown keeps tunnelling,
and only the bundles plus geoip:ru are moved into DirectSites and DirectIp. That
keeps the failure mode safe — an unlisted blocked service still works, it is just
not accelerated.
Geo URLs use the {{GEOIP_URL}} / {{GEOSITE_URL}} / {{LAST_UPDATED}} placeholders,
so an operator-wide profile carries no per-subscriber token. Existing overrides
are backed up before being replaced, and removing the file restores the managed
default.
Verified: bash -n; generator emits 258 direct domains covering ozon.ru,
sberbank.ru, gosuslugi.ru and vk.com; the profile is rejected by
_happ_validate_routing_json while placeholders are unresolved and accepted after
substitution.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0121QZ51XSKaiKcRrtoVQTDg
…fits The routing profile reaches the client in a response header. The generated location /sub/ left proxy_buffer_size at the nginx default of 4k, which the managed profile fits into only because DirectSites is empty. The generator added in the previous commit emits 258 domains, which lands the header around 8 KB. nginx then answers 502 and logs 'upstream sent too big header', so the subscription stops working for every client at once — found while rolling this out on a live server. Raises the buffers in both generated /sub/ blocks and documents the constraint, including the case where an operator puts their own reverse proxy in front. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0121QZ51XSKaiKcRrtoVQTDg
Author
|
Дополнил PR ещё одним коммитом: при выкатке на живой сервер выяснилось, что генератор ломает подписку. Профиль уезжает клиенту в заголовке ответа, а в генерируемом Поднял буферы в обоих блоках 🤖 Generated with Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Проблема
В проекте уже есть кураторские наборы доменов для bypass: банки, маркетплейсы, Госуслуги, ВК,
Яндекс, Mail.ru, стриминги, Steam. Это ровно список «сюда не надо через VPN», собранный вручную.
Но bypass — серверное правило: оно меняет то, куда запрос отправляет VPS. Клиент всё равно
заворачивает трафик в туннель, поэтому адресат видит адрес VPS. А на ноде без каскада общий outbound
и так
direct, то есть bypass там не меняет вообще ничего.Практический результат: пользователь включает bypass с группой «маркетплейсы» и всё равно получает
от Ozon заглушку. Убрать её может только профиль на стороне клиента.
Что в PR
Пункт
HAPP-подписка → 7) Маршрутизация клиента, который пишет.happ_routing.jsonиз тех женаборов, что и серверный bypass. Обе половины разделения начинают опираться на один список.
Важно, что
GlobalProxyостаётся"true": незнакомый заблокированный сервис продолжает идти черезтуннель и работать, а напрямую уходят только наборы и
geoip:ru. Обратная полярность (по умолчаниюdirectплюс список того, что проксировать) выглядит заманчиво, но там любой забытый сервис молчаломается сразу у всех пользователей. Мы на это наступили дважды, на Telegram и на WhatsApp.
Ссылки на geo-базы используют подстановки из предыдущего PR, поэтому общий профиль не содержит чужой
токен. Предыдущее переопределение бэкапится, удаление файла возвращает дефолт.
Поверх #22.
Проверено
bash -nна всех скриптахozon.ru,sberbank.ru,gosuslugi.ru,vk.com_happ_validate_routing_json, пока плейсхолдеры не разрешены, ипринимается после подстановки
🤖 Generated with Claude Code
https://claude.ai/code/session_0121QZ51XSKaiKcRrtoVQTDg