Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,75 @@ Default bundle groups:

The menu is interactive: arrows move the selection, space toggles a group, Enter applies.

## HAPP client routing profile

The subscription endpoint returns a client-side routing profile to HAPP in the `routing:` response
header. The managed default, `xrayebator-default`, sets `GlobalProxy: "true"`, so every destination
except private IPv4 ranges is tunnelled.

That is a different knob from [Bypass routing](#bypass-routing). Bypass changes where **the server**
sends a request; the client still tunnels it, so the destination sees the VPS address. On a node
without a cascade the catch-all outbound is already `direct`, so bypass cannot change what a Russian
site sees. Keeping domestic traffic out of the tunnel is only possible in the client profile.

### Override file

| Variable | Default | Meaning |
|---|---|---|
| `HAPP_ROUTING_ENABLED` | `true` | Set to `false` to stop sending the `routing:` header |
| `HAPP_ROUTING_JSON_FILE` | `/usr/local/etc/xray/.happ_routing.json` | Operator override |

If the override file exists and passes HAPP schema validation, it replaces the managed default for
every subscriber. Malformed JSON is ignored in favour of the default rather than broadcast. Restart
`xrayebator-sub.service` is not required: the file is read per request.

Verdicts are evaluated per destination. `GlobalProxy: "false"` makes `direct` the default and sends
only `ProxySites` and `ProxyIp` through the tunnel; `GlobalProxy: "true"` inverts that and treats
`DirectSites` and `DirectIp` as the exceptions.

### Split-tunnel example

Domestic traffic stays on the mobile carrier, blocked services go through the VPS:

```json
{
"Name": "xrayebator-split",
"GlobalProxy": "false",
"RemoteDNSType": "DoH",
"RemoteDNSDomain": "https://cloudflare-dns.com/dns-query",
"RemoteDNSIP": "1.1.1.1",
"DomesticDNSType": "DoU",
"DomesticDNSDomain": "",
"DomesticDNSIP": "77.88.8.8",
"Geoipurl": "https://example.com/geo/geoip.dat",
"Geositeurl": "https://example.com/geo/geosite.dat",
"LastUpdated": "1788700000",
"DnsHosts": { "cloudflare-dns.com": "1.1.1.1" },
"DirectSites": ["domain:ru", "domain:xn--p1ai"],
"DirectIp": ["geoip:private", "geoip:ru"],
"ProxySites": ["domain:google.com", "domain:youtube.com", "domain:instagram.com"],
"ProxyIp": ["149.154.160.0/20", "91.108.4.0/22", "91.105.192.0/23"],
"BlockSites": [],
"BlockIp": [],
"DomainStrategy": "IPIfNonMatch",
"FakeDNS": "false"
}
```

Three things are easy to get wrong:

- **Telegram travels to IP addresses, not domains.** Domain rules never match it. Take the ranges
from <https://core.telegram.org/resources/cidr.txt> instead of writing them from memory; the list
changes, and a missing range silently degrades media downloads while chat still works.
- **`LastUpdated` must grow.** HAPP re-imports a profile only when the value is higher than the one
it already stored.
- **Geo databases must be reachable by the client.** The managed default points at
`/sub/<token>/geoip.dat`, which is per-subscriber. An operator-wide override cannot embed one
subscriber's token, so host the databases somewhere every client can fetch them.

While a client downloads new geo databases the previous profile keeps running, so a failed download
leaves routing unchanged rather than broken.

## Cascade and upstream nodes

The cascade is a server-side outbound and routing mode, not a new client profile. The client keeps
Expand Down
69 changes: 69 additions & 0 deletions docs/ru/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,75 @@ Bypass добавляет правила в Xray routing, чтобы выбра

Меню интерактивное: стрелки двигают выбор, пробел включает и выключает группу, Enter применяет.

## Профиль маршрутизации клиента HAPP

Эндпоинт подписки возвращает клиенту профиль маршрутизации в заголовке ответа `routing:`.
Управляемый профиль по умолчанию, `xrayebator-default`, задаёт `GlobalProxy: "true"`, поэтому в
туннель уходит всё, кроме приватных диапазонов IPv4.

Это не то же самое, что [Bypass routing](#bypass-routing). Bypass меняет то, куда запрос отправляет
**сервер**; клиент всё равно заворачивает его в туннель, и адресат видит адрес VPS. На ноде без
каскада общий outbound и так `direct`, поэтому bypass не меняет то, что видит российский сайт.
Не пускать домашний трафик в туннель можно только профилем клиента.

### Файл переопределения

| Переменная | По умолчанию | Значение |
|---|---|---|
| `HAPP_ROUTING_ENABLED` | `true` | `false` полностью отключает заголовок `routing:` |
| `HAPP_ROUTING_JSON_FILE` | `/usr/local/etc/xray/.happ_routing.json` | Переопределение оператором |

Если файл существует и проходит проверку схемы HAPP, он заменяет управляемый профиль для всех
подписчиков. Некорректный JSON игнорируется в пользу профиля по умолчанию, а не рассылается
клиентам. Перезапуск `xrayebator-sub.service` не нужен: файл читается на каждый запрос.

Вердикт выносится для каждого адресата. При `GlobalProxy: "false"` поведением по умолчанию
становится `direct`, а в туннель уходят только `ProxySites` и `ProxyIp`. При `GlobalProxy: "true"`
всё наоборот, и исключениями работают `DirectSites` и `DirectIp`.

### Пример раздельной маршрутизации

Домашний трафик остаётся у оператора, заблокированные сервисы идут через VPS:

```json
{
"Name": "xrayebator-split",
"GlobalProxy": "false",
"RemoteDNSType": "DoH",
"RemoteDNSDomain": "https://cloudflare-dns.com/dns-query",
"RemoteDNSIP": "1.1.1.1",
"DomesticDNSType": "DoU",
"DomesticDNSDomain": "",
"DomesticDNSIP": "77.88.8.8",
"Geoipurl": "https://example.com/geo/geoip.dat",
"Geositeurl": "https://example.com/geo/geosite.dat",
"LastUpdated": "1788700000",
"DnsHosts": { "cloudflare-dns.com": "1.1.1.1" },
"DirectSites": ["domain:ru", "domain:xn--p1ai"],
"DirectIp": ["geoip:private", "geoip:ru"],
"ProxySites": ["domain:google.com", "domain:youtube.com", "domain:instagram.com"],
"ProxyIp": ["149.154.160.0/20", "91.108.4.0/22", "91.105.192.0/23"],
"BlockSites": [],
"BlockIp": [],
"DomainStrategy": "IPIfNonMatch",
"FakeDNS": "false"
}
```

Три места, где легко ошибиться:

- **Telegram ходит по IP-адресам, а не по доменам.** Доменные правила его не ловят. Диапазоны нужно
брать со страницы <https://core.telegram.org/resources/cidr.txt>, а не по памяти: список меняется,
а пропущенный диапазон тихо портит загрузку медиа, пока переписка продолжает работать.
- **`LastUpdated` обязан расти.** HAPP переимпортирует профиль только когда значение больше уже
сохранённого.
- **Geo-базы должны быть доступны клиенту.** Управляемый профиль ссылается на
`/sub/<token>/geoip.dat`, а этот путь привязан к конкретному подписчику. Общее переопределение не
может содержать чужой токен, поэтому базы нужно выложить туда, откуда их скачает любой клиент.

Пока клиент скачивает новые geo-базы, продолжает работать предыдущий профиль, поэтому неудачная
загрузка оставляет маршрутизацию прежней, а не ломает её.

## Каскад и upstream-ноды

Каскад — серверный режим outbound и routing, а не новый профиль клиента. Клиент продолжает
Expand Down
62 changes: 62 additions & 0 deletions docs/zh-CN/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,68 @@ UFW 由安装脚本自行管理:安装 `ufw` 包,若 UFW 未启用则执行

菜单是交互式的:方向键移动光标,空格切换分组,回车应用。

## HAPP 客户端路由配置

订阅端点通过响应头 `routing:` 向 HAPP 下发客户端路由配置。托管的默认配置 `xrayebator-default`
设置了 `GlobalProxy: "true"`,因此除私有 IPv4 段之外的所有目标都会走隧道。

这与[分流路由](#分流路由)不是同一个开关。分流路由改变的是**服务端**把请求发往何处;客户端仍然会把它
送进隧道,目标站点看到的依旧是 VPS 的地址。在没有级联的节点上,兜底 outbound 本来就是 `direct`,
所以分流路由无法改变俄罗斯站点看到的结果。只有客户端配置才能让本国流量不进入隧道。

### 覆盖文件

| 变量 | 默认值 | 含义 |
|---|---|---|
| `HAPP_ROUTING_ENABLED` | `true` | 设为 `false` 则不再下发 `routing:` 响应头 |
| `HAPP_ROUTING_JSON_FILE` | `/usr/local/etc/xray/.happ_routing.json` | 运维方的覆盖文件 |

如果覆盖文件存在并通过 HAPP 结构校验,它会对所有订阅者取代托管的默认配置。格式错误的 JSON 会被忽略
并回退到默认配置,而不会下发给客户端。无需重启 `xrayebator-sub.service`:该文件在每次请求时读取。

判定是按目标逐个进行的。`GlobalProxy: "false"` 时默认行为是 `direct`,只有 `ProxySites` 和
`ProxyIp` 走隧道;`GlobalProxy: "true"` 则相反,`DirectSites` 和 `DirectIp` 成为例外。

### 分流示例

本国流量留在运营商网络,被封锁的服务经由 VPS:

```json
{
"Name": "xrayebator-split",
"GlobalProxy": "false",
"RemoteDNSType": "DoH",
"RemoteDNSDomain": "https://cloudflare-dns.com/dns-query",
"RemoteDNSIP": "1.1.1.1",
"DomesticDNSType": "DoU",
"DomesticDNSDomain": "",
"DomesticDNSIP": "77.88.8.8",
"Geoipurl": "https://example.com/geo/geoip.dat",
"Geositeurl": "https://example.com/geo/geosite.dat",
"LastUpdated": "1788700000",
"DnsHosts": { "cloudflare-dns.com": "1.1.1.1" },
"DirectSites": ["domain:ru", "domain:xn--p1ai"],
"DirectIp": ["geoip:private", "geoip:ru"],
"ProxySites": ["domain:google.com", "domain:youtube.com", "domain:instagram.com"],
"ProxyIp": ["149.154.160.0/20", "91.108.4.0/22", "91.105.192.0/23"],
"BlockSites": [],
"BlockIp": [],
"DomainStrategy": "IPIfNonMatch",
"FakeDNS": "false"
}
```

三个容易出错的地方:

- **Telegram 按 IP 地址通信,而不是域名。** 域名规则匹配不到它。请从
<https://core.telegram.org/resources/cidr.txt> 获取网段,不要凭记忆填写:该列表会变化,遗漏一个
网段会悄悄拖慢媒体下载,而聊天看起来仍然正常。
- **`LastUpdated` 必须递增。** 只有当该值大于已保存的值时,HAPP 才会重新导入配置。
- **geo 数据库必须能被客户端访问。** 托管的默认配置指向 `/sub/<token>/geoip.dat`,该路径与具体订阅者
绑定。面向全体的覆盖文件无法内嵌某一个订阅者的 token,因此请把数据库放在任何客户端都能下载的位置。

客户端下载新的 geo 数据库期间,先前的配置继续生效,因此下载失败只会保持路由不变,而不会使其损坏。

## 级联与上游节点

级联是服务端的出站与路由模式,而不是新的客户端配置档。客户端仍然连接当前 VPS:
Expand Down