Repository navigation
Description
Found by code review and reproduced in a lab; never seen in production. This is a robustness fix: the upstream panel and bridge do not send such a batch today (see below).
Summary
When one batch lists the same email more than once, the Xray backend does not apply the last entry. UpdateUsers (reached through SyncUsersChunked, controller/users_chunk.go:37) compares every entry with the state from before the batch and applies all removals before any add, and Config.syncUsers (full sync and startup config) never takes off a user that an earlier entry added. The result follows an earlier entry.
Reproduction (real node, real Xray)
Real node built from dev 9ccd2638c7fe, REST, real Xray 26.3.27, macOS arm64; one trojan inbound; core state read with xray api inbounduser. Each step is one PUT /users/sync/chunked with one chunk:
batch for 1.alice |
before | last entry wants | core after, dev 9ccd2638 |
|---|---|---|---|
[on trojan, no inbounds] (added then removed) |
absent | absent | present |
[no inbounds, on trojan] (removed then kept) |
present | present | absent |
All requests answered HTTP 200. The core keeps the wrong state until a later sync for that user (read from the code, not reproduced).
Not reachable from the upstream panel or bridge today
Found by reading, not run against a real panel. node_bridge_py c5632b65a10a queues one entry per email per node (PasarGuardNodeBridge/storage.py:159-166, pending[user.email] = ...) before it sends SyncUser / SyncUsersChunked, and panel dev f92acaf51cec sends users through the bridge (app/node/__init__.py:293,415,428). Any other client of the node's gRPC or REST API can send such a batch (read from the code; the lab used REST).
Config.updateUsers has the same ordering, but no RPC reaches it: its only caller is UpdateUsersAndRestart (backend/xray/user.go:439), and nothing outside the backends calls that (git grep at 9ccd2638).
Root cause (dev 9ccd2638)
backend/xray/config.go:112(buildInboundUpdates) handles every entry, so one email can be both inupdate.accountsand inremoveEmailSet.backend/xray/user.go:387changedAccountscompares each entry with the pre-batch state;user.go:392applies every removal before the adds atuser.go:407.backend/xray/config.go:66Config.syncUsers->Inbound.syncUsers(:143) only adds users that list the inbound.
Affected versions
dev 9ccd2638c7fe and main v0.5.7 739eab94a934.
Proposed fix
Keep only the last entry per email before building the per-inbound updates and before rebuilding an inbound's clients. PR #134.
Activity