Changelog
Product milestones for the XCTL control plane, Client agent, and admin console. For upgrade steps, see the upgrade guide.
Line provisioning moves to logical nodes
Dedicated inbound provisioning (legacy Mode A) is removed from the roadmap. New lines and per-order routing now go through logical nodes and the Admin API instead.
- Adapter integrations should create or bind logical nodes rather than per-order inbounds
- WHMCS and panel plugins align with the logical-node lifecycle
- Docs updated for upgrade paths and integration overview
This keeps multi-tenant routing consistent and avoids one-off inbound sprawl on XUI nodes.
Panel integrations and TLS inline certificate fixes
V2RaySocks panel_full
- Hysteria2 root-level
act=configparsing and inbound build on the V2RaySocks adapter path - WHMCS plugin: VLESS tcp/tls and HY2 SNI/obfs subscription shapes
- Runbook added for end-to-end
panel_fullvalidation
TLS inline certificates
- Fixed empty-line PEM edge cases when fullchain or imported certs reached panel wire format
PEMToLines+prepareDesiredCertshard validation before sync- Remark-based cert claim and Master fingerprint dedup to stop duplicate TLS refs on the same host
OpenAPI (InboundSyncPayload, DesiredInbound, TLSCertRef) and WebSocket protocol docs were updated to match.
Dashboard traffic analytics, Hysteria2, and WireGuard
Business traffic statistics
- New
node_traffic_dailyaggregation table (logical node ↔ host, 90-day retention) - Inbound-stats reporter computes deltas from cumulative XUI counters
GET /admin/overview/traffic-summarywith 1d / 7d / 30d / 90d ranges: totals, daily trend, and rankings by server, logical node, protocol, and region- Admin dashboard ops charts now use this API; live NIC vitals remain for real-time speed graphs only
Hysteria2
- Client
hysteria_authand XUIclients[].authalignment hysteria2://share links (Salamander, allowInsecure)- newV2board and V2RaySocks panel fallbacks for HY2 auth fields
WireGuard
wireguard_peerstable andWireGuardPeerService- Config sync injects
peers[]into desired inbounds .confdownload via admin and user APIs; peers disabled on user suspend/delete- User detail dialog: WireGuard config download in the admin console
Device limits and relay true-IP handling
Online device enforcement and relay topologies are now documented and partially shipped across Master, templates, and the admin UI.
Device limits
- Users page: over-limit visualization and risk-state storage split from core user records
device_limitsemantics aligned with alive reporting- Template-level
acceptProxyProtocolwith per-host override for landing nodes behind relays
Relay / true client IP
- Realm-panel → landing IP propagation in alive paths
- Xray
acceptProxyProtocoloption documented as an alternative to Nginxset_real_ip_fromon some layouts - Ops checklist for shared-IP abuse vs strict device caps
Implementation tracked in PR-1/2/3; see the device-limit docs for rollout order (visibility first, full enforcement second).
Host groups and node mapping (PR-7)
The control plane now models host groups and node mapping so logical nodes can fan out across physical hosts without manual per-host edits.
Console
- Host group CRUD, membership, and reconcile flows in the admin UI
- Logical node detail: host binding tab with manual override and reconcile
- Node mapping v2 form, mapping table, sync, and export (E2E validation ongoing)
Master
- Migration 008 + Admin API for node groups
- Migration 013 + host-group reconcile; soft-delete and purge regression tests
- Pull safety invariants for multi-tenant user sync
Config sync safety hardening
A full audit closed several classes of accidental user or inbound deletion during panel pull and host lifecycle events.
Master
- User pull no longer uses ETag/304; stale cached bodies cannot skip reconciliation
reconcileUnionAfterMappingPullsonly prunes when a fresh200body is available- Host soft-delete runs
ReconcileHostBindingsincluding manual direct binds - Host group delete: rows removed first, then reconcile — no orphan bindings left behind
Client
Runnerrequires a second confirmation before pruning on empty desired state or narrowed scope- Orphan client cleanup:
user.invalidate+PruneOrphanClients+ panel pull reconcile when union is zero
Regression coverage: panel_group_pull_test.go, runner_test.go, host_group_service_test.go.
Platform admin, licensing, and observability
Generic control-plane operations that sit above panel-specific sync logic.
Settings & security
master_settingsregistry with DB-backed cache and/admin/settingsAPI- Admin login + JWT + optional TOTP 2FA (
migrations/009) - Secret fields encrypted at rest (
enc:v1:) with API redaction
License client (P4)
- Remote validate + AES decrypt into
xuictl_license - Grace / degraded modes instead of hard exit on transient center outages
max_hosts/max_users/max_logical_nodesenforced on create- Admin license panel + banner; host/user/logical-node create blocked when degraded
Alerts (P3)
AlertDispatcherwith Telegram delivery and dedup windows- Host offline, stale vitals, webhook
event_dead, and license expiry hooks POST /admin/notification/testfrom system settings
Dashboard & audit (P5)
GET /admin/overviewwith per-module failure degradationGET /admin/audit-logswith tenant scoping and secret redaction on read- Admin UI:
/xuictl/dashboardstats layout and/xuictl/audit-logswith shared pagination component
Protocol catalog, templates, and config sync pipeline
The data-driven inbound pipeline replaced hand-edited per-host JSON as the source of truth.
Protocol catalog
protocol_catalogtable with seeded capability matrix (internal/xui/catalog)- Admin API: list, get, patch, import/export with audit on mutating calls
- Transactional import and
MarkDeprecatedExceptguardrails
Protocol templates
- JSON
settings/streamtemplates with quoted{{placeholder}}rendering - Multi-version templates, dry-run API, and delete protection when still referenced by logical nodes
Logical nodes
- CRUD + host bind/unbind; auto-generated Reality keys and shortIds when missing
render.Composebuilds the full inbound set per nodePOST .../renderandPOST .../syncpushinbound.syncover the agent WebSocket
Client inbound syncer
- Diff by tag prefix, ordered Add → Update → Delete, hash cache to skip no-op updates
POST /server/UniProxy/inbound-syncreports binding status back to Master
Subscription output
internal/xui/sharegenerators for VLESS, VMess, Trojan, Shadowsocks (+ xhttp/kcp matrix)- Users
GET .../subscribenow emits real share links from enabled logical nodes - Admin
issue-subscribe-urlfor support tooling (raw, Clash, Sing-box, etc.)
Phase 4 code review fixes (revision stale guard, NodeVars merge PATCH, duplicate tag handling) landed the same week.
Orphan cleanup and AgentHub control messages
Closed the loop when panel users disappear or panel API tokens rotate mid-flight.
Orphan clients
- Cleanup
syncedstate transitions emituser.invalidateto edge agents - Client
PruneOrphanClientsremoves stale XUI clients after invalidate - Inbound delete triggers
OnAppliedreconcile - Scheduled panel pull reconcile including union=0 edge case
AgentHub
- Reconcile ops broadcast
user.invalidate - PATCH
panel_api_tokenpushesconfig.updateso agents drop etag and forcePullUsers - Master
/ws/agentregistry with connection-scoped IDs (race-safe register/unregister)
Panel token tooling
GET .../probefor pre-flight panel reachabilityscripts/e2e-vps.shgate script for VPS bring-up- Migration
004_add_panel_api_token_idfor token lineage
These changes sit between the protocol-catalog pipeline and host-group work; they reduced “ghost users” on nodes after mapping changes.
Certificate Center shipped
TLS for inbounds and edges can be managed from the Certificate Center instead of hand-editing every node.
- Issue, renew, and attach certificates from the admin console
- ACME workflows with platform settings under
certificates.* - DNS-01 support: Aliyun, DNSPod, and Tencent Cloud providers
- Delete protection, CA-friendly names, and
last_errorsurfaced in the UI
OpenAPI and WebSocket protocol docs were synced with the new certificate events.
Config sync queue and SSH provisioning
Operations that touch real hosts are now queued, streamed, and observable from the console.
Config sync queue
config_sync_taskstable andConfigSyncQueueService- Bind, unbind, patch, delete, and manual sync enqueue instead of failing inline when a host is offline
waiting_hoststate with flush on agent reconnect- Admin
/admin/config-sync-queue+ UI batch modal - Logical node delete no longer returns 409 “unbind first” — work is queued
SSH provisioning
POST /admin/hosts/provisionNDJSON stream for one-click install- SSH credential vault, test-ssh, install sessions, ops actions, and Web SSH entry
- Recycle bin: soft delete, restore, purge with streamed progress
GET .../probeand panel token rotation helpers for field rollout
Inbound observability
node_hostsinbound client count and traffic snapshot columns- Client reporter →
POST /server/UniProxy/inbound-statsfor per-binding cards in the UI
Users API and OpenAPI contract gate
M1 backend surface: tenant-scoped user lifecycle with a frozen REST contract.
Users REST API
POST /api/v1/userswith idempotency keys and subscribe URL in the response- Cursor-paginated list, get, patch, soft delete by
biz_ref reset-uuid,reset-traffic, traffic read, and subscribe endpoints- HMAC-signed subscription URLs (
?token=or Bearer)
Master skeleton
GET /healthz,/readyz,/version- Bearer auth with tenant scope via adapter tokens
- MariaDB + GORM migrations with
xuictl_table prefix
Quality gate
- Schemathesis contract tests on the M1 OpenAPI subset — 56/56 passing
make regression-usersfor user lifecycle regressions- Prism mock + local Docker dev stack (
make dev-up) for panel integrators
Client agent at this stage was still Mode B mocks; real XUI sync followed in the April milestone.
Client agent: real XUI sync and Master WebSocket
The Client agent graduated from mock placeholders to production-shaped sync against real 3x-ui panels.
- REST session + centralized Batch API against XUI
- WebSocket link to Master with
host.status.tickvitals - UniProxy HTTP push/status/alive/pull on the Client transport path
- Traffic collector, alive reporter, and panel alive push back to Xboard-style panels
- Reload batching and user syncer aligned with the architecture spec
Master M2 backend phases A–E are closed; multi-node field validation is next.
Admin console foundation
The self-hosted admin UI (frontweb) reached a demo-ready slice on top of the Master Admin API.
- Tenants, users, permission groups, and Adapter token management
- Logical nodes, protocol catalog, and protocol templates (multi-version + dry-run)
- Physical hosts: CRUD, SSH credentials, streamed SSH provision, ops actions, and recycle bin
- Config sync queue, events console, and audit-oriented admin tokens
- Static export build wired into
make web-buildfor co-deployment with Master
Users REST API (CRUD, traffic, subscribe URL) and OpenAPI contract tests (56/56 on the M1 surface) shipped in the same milestone.