Skip to content

Xray интеграция

Xray предоставляет gRPC API для управления конфигурацией в реальном времени без перезапуска. Панель не трогает конфиги напрямую — она общается с агентом, который запущен рядом с Xray на каждой ноде.

Панель (Go) → gRPC → Агент (Go) → Xray gRPC API → Xray-core

Лёгкий Go-сервис. Запускается в одном docker-compose с Xray.

service NodeAgent {
// Управление клиентами
rpc AddClient(AddClientRequest) returns (ClientResponse);
rpc RemoveClient(RemoveClientRequest) returns (ClientResponse);
rpc UpdateClient(UpdateClientRequest) returns (ClientResponse);
// Статистика
rpc GetTrafficStats(GetTrafficStatsRequest) returns (TrafficStats);
rpc GetSystemStats(Empty) returns (SystemStats);
// Управление инбаундами
rpc AddInbound(AddInboundRequest) returns (InboundResponse);
rpc RemoveInbound(RemoveInboundRequest) returns (InboundResponse);
rpc GetInbounds(Empty) returns (InboundsResponse);
}
message AddClientRequest {
string inbound_tag = 1;
string uuid = 2; // для VLESS/VMess
string password = 3; // для Trojan/SS
string email = 4; // уникальный тег в Xray (формат: user_id@panel)
string flow = 5; // "xtls-rprx-vision" для VLESS+XTLS
int64 expire_time = 6; // unix timestamp, 0 = без ограничений
}
message TrafficStats {
string email = 1;
int64 bytes_up = 2;
int64 bytes_down = 3;
}
message SystemStats {
float cpu_percent = 1;
int64 mem_used_mb = 2;
int32 connections = 3;
int64 uptime_sec = 4;
}

Агент общается с Xray через его встроенный gRPC API (порт 10085 по умолчанию).

Используемые сервисы Xray

Section titled “Используемые сервисы Xray”
HandlerService → AddInbound, RemoveInbound, AddUser, RemoveUser, AlterInbound
StatsService → GetStats, QueryStats

Пример: добавить пользователя в VLESS инбаунд

Section titled “Пример: добавить пользователя в VLESS инбаунд”
// Внутри агента
func (a *Agent) addVlessClient(inboundTag, uuid, email, flow string) error {
client := &proxyman_command.AddUserOperation{
User: &protocol.User{
Email: email,
Account: serial.ToTypedMessage(&vless.Account{
Id: uuid,
Flow: flow,
}),
},
}
_, err := a.handlerClient.AlterInbound(ctx, &proxyman_command.AlterInboundRequest{
Tag: inboundTag,
Operation: serial.ToTypedMessage(client),
})
return err
}

Пример: получить статистику трафика

Section titled “Пример: получить статистику трафика”
// QueryStats с паттерном "user>>>email>>>traffic"
resp, err := a.statsClient.QueryStats(ctx, &stats_command.QueryStatsRequest{
Pattern: "user>>>",
Reset_: true, // сбрасывает счётчик после чтения (дельта)
})

Reset_: true — критически важно. Xray считает трафик накопительно, панель читает дельту и сбрасывает.

Синхронизация пользователей

Section titled “Синхронизация пользователей”

Добавление (новая подписка / новая нода)

Section titled “Добавление (новая подписка / новая нода)”
1. Получить все активные node_inbounds для нод, доступных по плану
2. Для каждого inbound → gRPC AddClient к агенту
3. UPDATE user_inbound_sync SET sync_status = 'synced'
4. При ошибке → sync_status = 'error', retry через Asynq

Удаление (истекла подписка / бан)

Section titled “Удаление (истекла подписка / бан)”
1. Получить все user_inbound_sync WHERE user_id = ? AND sync_status = 'synced'
2. Для каждого → gRPC RemoveClient
3. UPDATE sync_status = 'removed'

Xray идентифицирует клиентов по полю email (не обязательно реальный email). Используем формат:

{user_id}@astral

Это позволяет однозначно привязать статистику трафика к пользователю в панели.

Конфигурация инбаунда (пример VLESS+WS+TLS)

Section titled “Конфигурация инбаунда (пример VLESS+WS+TLS)”

Хранится в node_inbounds.settings и stream_settings как JSONB:

// settings
{
"clients": [],
"decryption": "none"
}
// stream_settings
{
"network": "ws",
"security": "tls",
"tlsSettings": {
"certificates": [
{
"certificateFile": "/etc/ssl/certs/domain.crt",
"keyFile": "/etc/ssl/private/domain.key"
}
]
},
"wsSettings": {
"path": "/vless"
}
}

При добавлении новой ноды агент получает полный конфиг инбаунда и применяет его через AddInbound.

Каждые 5 минут:
1. Для каждой активной ноды → gRPC GetTrafficStats (с Reset_=true)
2. Парсим email тег → достаём user_id
3. INSERT INTO traffic_snapshots
4. UPSERT INTO daily_traffic (bytes += delta)
5. UPDATE subscriptions SET traffic_used_bytes += delta
6. Если traffic_used_bytes >= traffic_limit_bytes → suspend подписку → sync
# docker-compose.yml на ноде
services:
xray:
image: ghcr.io/xtls/xray-core:latest
ports:
- "443:443"
volumes:
- ./config.json:/etc/xray/config.json
- ./ssl:/etc/ssl
agent:
image: ghcr.io/your-org/astral-agent:latest
environment:
XRAY_API_ADDR: "xray:10085"
AGENT_PORT: "50051"
AGENT_TOKEN: "${AGENT_SECRET_TOKEN}" # Для auth на панели
ports:
- "50051:50051"
depends_on:
- xray

Панель подключается к node.host:50051, передаёт AGENT_TOKEN в gRPC metadata.

Xray API иногда меняется между версиями. Агент хранит версию Xray и при старте репортит её панели. Панель логирует несовместимые версии.