Поток событий
GET /events на контроллере передаёт события ядра в момент, когда они происходят: панель узнаёт, что у пользователя кончилась квота, что слушатель не запустился или что обновление провайдера не удалось, не опрашивая /traffic/users, /limits/users и остальное.
Подключение
Заголовок раздела «Подключение»Поток устроен как /logs и /traffic:
- WebSocket, если запрос просит upgrade: одно JSON-событие в текстовом сообщении. Браузер сначала получает одноразовый билет через
POST /auth/ws-ticket, затем передаёт?ticket=при upgrade. В запрос билета включите те же фильтры событий; для каждого переподключения нужен новый билет. - Server-Sent Events с
Accept: text/event-stream: строкиevent: <тип>иdata: <json>. - JSON по строкам в остальных случаях: chunked-ответ
application/x-ndjson, по событию в строке.
Маршрут закрыт secret контроллера и подчиняется правилам external-controller-cors, как и все остальные.
curl -N -H "Authorization: Bearer $SECRET" 'http://127.0.0.1:9090/events?types=user.limit,inbound.state'?types= принимает типы через запятую; без него передаются все. На неизвестный тип ответ 400.
События
Заголовок раздела «События»Каждое событие — JSON-объект с type, time (RFC 3339) и data:
{"type":"user.limit","time":"2026-10-14T12:00:00.123Z","data":{"inbound":"vless-in","user":"alice","reason":"quota","action":"closed","detail":"quota of 1000000000 bytes used up"}}| Тип | Когда | data |
|---|---|---|
auth |
Отклонённая аутентификация на входящем или пользователь с нового адреса. | result (success, failure), count; неудача: protocol, local, remote, reason (no_credentials, wrong_credentials, rejected_handshake, authentication_timeout); успех: inbound, user, remote; repeated, overflow (см. ниже). |
user.limit |
Пользователь израсходовал квоту или истёк (его соединения закрываются), либо соединение отклонено по expired, quota, max-conns или max-ips. |
inbound, user, reason, action (closed, rejected), detail, remote (при отказе), suppressed. |
user.changed |
Изменились пользователи входящего: через API пользователей или перезагрузкой, обновившей пользователей на месте. | inbound, added, removed, changed (имена). |
config.reload |
Конфиг применён, или новый не разобрался (PUT /configs, SIGHUP). |
status (ok, error), error. |
provider.update |
Провайдер прокси или правил скачан (по интервалу, по изменению файла или через API). | name, kind (proxy, rule), vehicle, status (ok, error), changed (false, если содержимое то же), error. |
inbound.state |
Слушатель из секции listeners запустился, не смог слушать или остановлен (удалён или пересоздан перезагрузкой). |
inbound, type, state (started, failed, stopped), error. |
chain.health |
Проверка здоровья группы-цепочки нашла её в другом состоянии, чем раньше: цепочка оборвалась, отказал другой узел или она снова работает. Первая проверка сообщает только об оборванной цепочке. | chain, alive, failedHop, position (с 1), error. |
tun.forwarding |
Windows, TUN с auto-route: на интерфейсе, к которому ядро привязывает свои соединения, включился Forwarding или WeakHostSend, и они возвращаются в TUN (on), или это прошло (cleared). Приходит один раз на изменение, а не при каждой проверке; см. TUN в Windows. |
interface (псевдоним, как его принимает Set-NetIPInterface -InterfaceAlias), family (ipv4, ipv6), forwarding, weakHostSend, state (on, cleared), reason у cleared: off (флаги выключены), not-egress (ядро больше не привязывается к интерфейсу), gone (интерфейса больше нет), stopped (TUN остановлен). |
dropped |
Поток читался слишком медленно, и события для него потеряны; приходит раз в 5 секунд, пока это происходит, при любом фильтре. | count. |
Агрегация auth
Заголовок раздела «Агрегация auth»Сканер не должен залить поток, поэтому события auth агрегируются окнами по 5 секунд. Первая попытка адреса клиента (порт не учитывается) с данным исходом приходит сразу с count: 1; её повторы в окне приходят одним событием с repeated: true и их count в конце окна. За окно отслеживается до 256 клиентов; попытки остальных суммируются в одно событие на исход с overflow: true.
Успех — это пользователь, у счётчика которого появился адрес, ещё не онлайн для него (адрес из max-ips), поэтому успехи сообщаются только при включённой статистике пользователей или лимитах. Не каждое соединение пользователя — событие.
Отказ user.limit сообщается не чаще раза в секунду на пользователя, как и строка лога; suppressed — число отказов между ними (все они учтены в /traffic/users).
Доставка
Заголовок раздела «Доставка»Доставка — по возможности: у каждого потока буфер на 256 событий, и отстающий поток теряет события, а не тормозит ядро; об этом сообщает событие dropped. События, случившиеся, пока поток не был открыт, не хранятся: после переподключения прочитайте состояние из /traffic/users, /limits/users и /inbounds/{name}/users.
Без открытых потоков хуки стоят одну атомарную загрузку. В метриках есть citrus_event_subscribers, citrus_events_published_total{type} и citrus_events_dropped_total.
Что не сообщается
Заголовок раздела «Что не сообщается»- Лимиты скорости (
speed-up,speed-down) формируют трафик непрерывно, у них нет момента для события. - Проверки здоровья прокси: их состояние есть в
/proxiesи вcitrus_proxy_alive.
