The rehearsal's inbound-level totals never moved: the agent
reports sing-box tags (inbound-<port>) while the master's agent
traffic handler matches inbound rows by their panel tag — which
the B1 model renamed to the n<node>-in- scheme, so the lookup
silently matched nothing (per-user totals, keyed by email, were
unaffected; production on V1 keeps the old tag form and is not
hit until the V2 migration itself).
The converter now takes the row's panel tag as the fragment tag
(migrator passes it through), making one identity serve the DB
row, the generated config, core stats and traffic reports. Bare
fixtures without a panel tag keep the historical form.
- Delete the xray settings page (routes/menu/title), geodata UI/service,
XraySettingController/Service and routing-sync write paths; the stored
xrayTemplateConfig row loses its last readers (GetXrayConfigTemplate now
returns the embedded factory template; B1 already deleted the row).
- Settings GeneralTab gains the four trafficStatsSwitches switches
(settings.trafficStatsSwitches, B1) with the dual warning copy; entity
AllSetting carries the key end to end (TestTrafficStatsSwitchesPlumbing).
- Tag-directory consumers (panelOutbound, mtproto egress, node/host
outbound pickers) move from POST /xray/ to GET /server/outboundDirectory.
- Shared Mux/Sockopt forms and balancer helpers relocated to lib/xray;
converter preserves sniffing ips/domains exclusions into the panel
sidecar instead of dropping them (B2 gap closed by retention).
- Freedom final-rules DB fix seeders retired; getDefaultJsonConfig
endpoint removed (no in-repo consumer); locales pruned to referenced
keys (pages.xray 331 -> 68) plus the five new settings keys.
- 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>
New stored model (legacy columns untouched, read paths unchanged
until B2):
- inbounds.singbox_config (converted sing-box inbound/endpoint
fragment JSON; empty = blocked or external-runtime) and
inbounds.singbox_preserved (unmapped items verbatim + panel-side
material + notes)
- agent_nodes.singbox_outbounds / singbox_routing_rules
Converter (internal/singbox, pure functions, ITEM6-CONVERSION-SPEC):
all panel protocols mapped; unmapped constructs are preserved and
registered, never dropped. B0 golden fixtures: 26/26 semantic match
(fragment + unmapped concept/pending sets + disposition).
One-shot migrator (internal/database, InitDB hook after initModels):
marker-gated and idempotent; SQLite file backup before any write
(failure refuses migration/startup); stats switches migrate out of
the template BEFORE the xrayTemplateConfig row is discarded
(SCOPE 4.3.1 order); identity layer is the only client truth with
spec 7-1 mirror reconciliation (adopt/fill/conflict all reported);
fallbacks merged from table + embedded settings, deduped; agent
overrides converted with per-entry issues; everything in one
transaction, marker written last; JSON report written next to the
DB. Malformed rows/templates are hard failures (rollback, old DB
untouched).
settings.trafficStatsSwitches added with factory defaults
(inbound on, outbound off) in defaultValueMap.
Validation: synthetic agent-era DB (mirror drift, fallbacks, xhttp,
agent geo rule, non-default stats) converts as specified; old
production backup (25 inbounds) migrates with credentials, Reality
pbk derivation, traffic counters and reset cycles byte-identical;
idempotent re-run is a no-op; touched packages show zero new test
failures vs the 9-red baseline.
26 cases (protocol x representative transport): current-shape input,
expected sing-box fragment, and share-link baseline generated by running
current main@8e89960 GetLink. Blocked/partial cases (xhttp, kcp,
fallbacks, finalmask, vlessenc, removed SS ciphers, tunnel) record their
unmapped concepts under _b0 for the Q1 decision instead of fabricating
output. Link-route baselines only: JSON/Clash subscription families are
being removed wholesale per the 2026-10-23 decision, Q2 closed.