Scope B (Windows agent):
- probe.go split into shared core + probe_linux.go (verbatim Linux
sampler, build-tagged) + probe_windows.go (Win32 sampler: GetSystemTimes
CPU diff, GlobalMemoryStatusEx, fixed-drive df totals, GetIfTable2
counted set, MachineGuid boot identity, registry facts; load and
TCP/UDP counts stay zero - x/sys v0.47.0 exports no wrapper)
- update intents are acknowledged and skipped on Windows (the release
assets are Linux binaries; Windows nodes update via install-agent.ps1)
- cmd/xui-agent-windows: SCM service host (svc.Run + mgr install/
uninstall/start/stop) mirroring main.go's agent lifecycle wiring
- install-agent.ps1: downloads the exe, writes probeOnly agent.json,
registers and starts the service, rerun-safe
Scope C (version display):
- config.GetAgentDisplayVersion (and the master getter) compose from
the bare number so a V-form injection no longer doubles the letter
(AV2.1.0 -> A2.1.0)
- node list view repairs stored AV-prefixed reports at assembly so
deployed agents read uniformly without an update
Scope D (production fix):
- share-link reality sni: when the inbound has a sing-box fragment,
sni is the fragment's tls.server_name - the only name the sing-box
REALITY server serves. The previous rule picked a uniformly random
serverNames entry per generation, which xray tolerated but sing-box
refuses as an invalid connection. sid keeps its random pick: the
converter carries the whole shortIds list into the fragment, so
every listed sid is served.
- 0: sing-box gen overrides fragment listen/port from the inbound row
(empty listen -> "::"), Update/Add recompute the stored fragment;
non-loopback E2E against the real core binary.
- 1: agent fetches the sing-box core from the Gitea release when
missing, self-checks it, and only then latches the switch.
- 2: node list shows the sing-box core version (heartbeat field,
persisted on the node row), xray version as fallback.
- 3: public-page fallback returns 404 to anonymous visitors when the
page is disabled.
- 4: settings restart hint bar removed, dead i18n keys cleaned.
- 5: two PUT calls missing JSON headers fixed (audit: only those two),
batch probe-task creation endpoint + UI.
- 6: cf-probe retirement card and coverage pipeline removed.
- 7: generated vless links no longer carry spx (panel export + sub);
parser still accepts spx; golden fixtures updated.
- probe batch: dedicated probe settings tab (public-page settings,
tasks, per-node meta), retention_days editable, per-node traffic
calibration + stat-interface pinning, runtime metrics split into a
lightweight fast report (default 3s, 1-60s adjustable).
- 8: sing-box core processes start with GOMEMLIMIT=32MiB, GOGC=50.
- 9: dead /getTwoFactorEnable docs entries removed (frontend call and
contract entry already gone at base; verified by grep).
- SingboxService builds the full sing-box config from the new model:
stored fragments re-projected against the identity layer (converter
projections factored into internal/singbox/project.go, shared by
converter and generator), agent singbox_outbounds/routing_rules
override, forwarding compiled to sing-box outbounds+route rules,
default private-CIDR reject rule (geoip:private replacement),
tun dns landing in the config dns block (closes the B1 'B2' pending).
- trafficStatsSwitches wiring: experimental.v2ray_api.stats lists are
generated per category only when both direction switches are on;
all-off yields no experimental block (no backfill, per the switches'
documented semantics).
- Agent /config endpoint additionally serves singbox_config +
singbox_config_hash; xray payload untouched, sing-box build failures
log-and-omit.
- SubService.GetLinkFromModel regenerates share links from identity +
singbox_config/singbox_preserved only (legacy generators reused over
rebuilt settings/stream views). Golden fixtures 26/26 byte-equal;
production backup DB: 107/107 links equal (55 byte-equal, 52 equal
modulo the legacy generator's own random Reality sni/sid pick),
generated config passes sing-box check.
- Converter preservation amendments (panel-side, for blocked/discarded
material link generation still needs): blockedTls for xhttp/kcp,
verbatim streamNetwork/streamSecurity, vlessEncryption presence,
ssPassword for removed ciphers, wireguardDns, socksAuth/socksAccounts
provenance for exact mixed/socks user refresh.
Co-Authored-By: Muse Spark <noreply@muse.dev>
1. per-client links (LinkProvider.LinksForClient) now apply the
inbound's managed Host records with the same precedence as
subscriptions and export-all (host endpoints after fallback
projection); zero-host inbounds keep the legacy output.
2. agent node writes no longer store the form placeholder
address='agent', port=2053: CreateFromRequest/UpdateFromRequest
sanitize it to the node's resolved agent address (update) or
empty (create / unreported), port 0; normalize gains a lenient
endpoint mode for agent writes only.
3. runtime Remote.baseURL refuses the legacy 'agent' placeholder
with a clear no-panel-endpoint error instead of splicing it into
a dialable https://agent:2053 URL.
Agent nodes store the create form's placeholder in nodes.address
('agent', port 2053; NodeFormModal.buildPayload). The export fallback
chain treated it as a real node address, so every host-less inbound on
an agent node produced links like vless://...@agent:31324.
SubService now loads agent_nodes bindings alongside nodes (latest
last_seen per node, same rule as the node list) and resolves an
agent-managed node's export address through the single convergence
point agentNodeAddress(nodeID): telemetry IPv4, then IPv6, then ""
so the chain continues to listen/public/request host and can never
emit the placeholder. Host-bound inbounds and traditional nodes are
untouched. GetInboundOptions applies the same substitution via
agentAdvertisedAddresses so the frontend share/QR/WireGuard feed
never sees the placeholder either.
GetLink had no socks-family case, so an attached client's
subscription contained no link for the inbound at all, and
getInboundsBySubId's protocol list omitted socks/mixed entirely, so
such a subscription 404'd when the SK5 inbound was the client's
only one. genSocksLink emits socks://user:pass@host:port with the
same credential pair the core config carries — user is the client
email, pass is the client password, both percent-encoded. The
inbound's own fixed account is an operator credential and is never
emitted.
- nodes: port node-type (traditional/agent) picker back from web-src into
the embedded frontend; backend annotates NodeView.nodeType from
agent_nodes bindings; fix agent-node update path (skip reachability
probe and apiToken requirement for agent-managed nodes); restore i18n
keys in all 13 locales; install command now uses the Gitea mirror
- jobs: expire agent-bound nodes whose agent_nodes.last_seen is older
than max(3x heartbeat interval, 120s) = 180s; last_seen=0 (never
registered) is left untouched; transitions reuse UpdateHeartbeat +
node event broadcast
- config: GetBaseVersion honors ldflags-injected buildVersion; release
builds inject 3.7.0-agent.1 so the panel no longer reports a phantom
update against the Gitea release tag
- logger/server: remove the three log viewers (panel/xray/amneziawg),
their API endpoints and the 10240-entry in-memory log buffer; file
logging via lumberjack unchanged; tests assert via a new optional
logger hook instead of the removed buffer
- server: remove system history and xray metrics modules end to end
(endpoints, sampling appends in RefreshStatus, per-minute persist
cron, gob snapshot/restore, XrayMetricsService); realtime status and
per-node history are untouched
- settings: subPort may be 0/unset - subscription links are generated
without a port while the subscription service keeps binding the
default 2096; the settings form declares the cleared state via
onNumber's new onClear option instead of snapping back to 1