- 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>
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