Operator production-readiness, surfaced by dogfooding a real paid service on
Iceslab: kill or rotate a leaked subscription link, reset a user's traffic on
demand, and scope API tokens to least privilege. Plus multi-hop cascades
validated end to end on real nodes for the first time (RU entry to EU exit, the
client's egress IP becomes the exit's), which surfaced and closed two real
defects along the way, a read-only demo build of the panel for the landing page,
the full set of well-known client detection rules with an inline enable toggle,
a per-profile reach count that ignores deleted users, and a batch of form and
stats polish.
Added
- Revoke and rotate a subscription link. Kill a leaked or abused link (it
returns 403 until rotated), or rotate it to issue a fresh token that kills the
old link immediately. From the Users page or the API. - On-demand traffic reset. Zero a user's used traffic and lift a traffic
limit in one action (period-billing top-up) without waiting for a cron reset;
a limited user goes straight back to active and is re-provisioned. - API tokens enforce scopes. A token can be least-privilege now: one scoped
to users plus subscription-read cannot reach settings, nodes, or token
management. Existing full-access tokens are unaffected; pick a scope preset
when creating a token. - Import an existing subscription token on user create. Operators migrating
in keep their clients' current links instead of forcing a re-import. - Read-only demo build of the panel. A VITE_DEMO_MODE build serves the real
UI from local fixtures (auto-login, every change a no-op) for embedding on a
landing page, and reads ?lang= for its start language. Tree-shaken out of the
normal build. - All well-known subscription clients are seeded. The User-Agent detection
rules now cover the clients that previously had no rule and fell through to the
base64 list they cannot parse: Surge and Surfboard, Quantumult X, Loon, Outline,
XKeen, plus Karing, Throne and FoXray, and explicit plain-list rules for
Shadowrocket, Streisand, V2Box and Happ. Idempotent seed. - Inline enable/disable toggle on the subscription rules table. Turn a rule
on or off in place without opening the editor; optimistic with rollback on
failure.
Fixed
- Enabling a cascade now reaches the nodes. Creating, editing or deleting a
cascade emitted no event, so the chaining fragments only lived in the database
and never pushed to the hop nodes until some unrelated profile or binding edit
fired a re-sync. The cascade service now re-pushes every node that is or was a
hop (so disabled or removed hops also drop their fragments). - The node-agent opens the cascade link port itself. The inter-hop link
listens on a high port that install-time firewall rules do not know about, so
it previously had to be opened by hand (ufw allow from <entry-ip> ...) or the
forward was silently dropped. The panel now sends the link port and the peer
hop's address with the cascade fragment, and the agent opens UFW for it,
restricted to that peer (resolving a hostname to its IP, falling open only if
it cannot be pinned). Applied on push and re-ensured on boot. - A node reporting one user twice no longer rolls back its stats. When a node
reported the same user on two protocols (for example VLESS plus Shadowsocks),
the cumulative-snapshot upsert received a duplicate row and Postgres aborted the
whole transaction, so that node recorded nothing. The duplicates are now summed
into one entry per user. This was why cascade nodes showed zero traffic. - Per-profile user reach excludes deleted users. A profile's "users with
access" count included soft-deleted users (their squad membership is kept for
restore), so one live user could read as four. The reach query now joins live
users only. - Implausible per-poll traffic deltas are discarded. A node reporting more
than a terabyte for one user in a single 30-second poll is re-billing its
lifetime counter (an outdated agent), not real traffic; the panel now drops such
a delta and logs it instead of corrupting quotas and node history. - Form and tab polish. New profiles default to Xray (REALITY) instead of
Hysteria, the REALITY option rows bottom-align instead of staggering, the long
node bootstrap payload wraps inside its box instead of overflowing, and the
favicon is wired into the page.