Skip to content

Refactor/singbox config layout - #12

Merged
SealinGp merged 10 commits into
mainfrom
refactor/singbox-config-layout
Sep 26, 2026
Merged

SealinGp merged 10 commits into
mainfrom
refactor/singbox-config-layout

Conversation

@SealinGp

Copy link
Copy Markdown
Owner

No description provided.

leozhang added 10 commits September 26, 2026 10:57
Eleven packages declared a name different from their directory
(singbox -> service, outbounds/rules -> noderules, traffic -> trafficflow,
installation -> installer, identity -> user, ...). Each is renamed to its
directory. Importers were rewritten qualifier-by-qualifier; where the new
name would shadow a local identifier the old name is kept as an explicit
import alias instead. No behaviour change.
- Delete config/ruleset_refs.go: the typed rule-set cascade had no callers
  once deletes moved to the raw-JSON walk in configuration. Its RuleSetRef
  and RefScope/RefAction types move there, and its test cases are ported to
  the raw implementation so the behaviour stays pinned.
- Delete the typed write path on config.Manager (GetConfig, SaveConfig,
  UpdateConfig, ValidateConfig, GetBackupConfig, GetVersion). Every save
  already goes through SaveDocument/UpdateConfigSection, which fail closed.
- Replace the two loose parseVersion copies (singbox, installation) with
  config.DisplayCoreVersion, built on the same ParseCoreVersion that drives
  capability gating.
config held selector/urltest group building, group-reference pruning and
node-tag minting even though only outbounds and subscription used them.
They move to two leaf packages under outbounds, so both outbounds and
outbounds/rules can import them without a cycle:

- outbounds/nodegroup: BuildGroupOutbounds, PruneGroupReferences, the
  endpoint/opt-in classification and the Filter/Group specs
- outbounds/nodetag: fingerprinted tag minting and endpoint keys

Manager.UpdateOutbounds becomes outbounds.addOutbounds, its only caller's
package. config.NewManagerWithCore lets other packages run the real
validate-and-rename path against a fake core, which the first outbounds
tests use: legacy-tag skips, no-op adds that write nothing, and renames and
deletes carried into selector/urltest members.
Every config package serves sing-box, so they now live beside the process
controller instead of as unrelated top-level packages:

  singbox/                       process controller (unchanged)
  singbox/core                   the binary: version, check, capabilities
  singbox/config                 document store (config.Manager)
  singbox/config/history         version history
  singbox/config/sections        dns/route/inbound/experimental editors
                                 (was app/pkg/configuration)
  singbox/config/outbounds       outbound editor + nodegroup/nodetag/rules
                                 (was app/pkg/outbounds)

core is split out of config so the controller and the installer can read
the core version without the document store, and so there is one parser.
The store keeps package name config, so config.Manager reads the same
everywhere; only import paths change.

app/architecture/layering_test.go pins the direction: core is a leaf, the
store does not know its editors, sections and outbounds never import each
other, and nodegroup/nodetag stay leaves. The go:generate path moved with
the registry; regenerated inventories differ only in their header path.
The config and version-history handlers were the one place the HTTP layer
called config.Manager directly, each classifying errors by hand. They now
go through config.Service, which owns the id rules and maps store failures
onto fault kinds, so these routes share respondOperationError with every
other module. Response codes and messages are unchanged and pinned by a
handler test.

app/architecture now bans the Manager's document and history methods in
handlers, and CLAUDE.md documents the new singbox/ tree and the write
paths that replace the removed typed UpdateConfig.
clashapi is the client for the running sing-box's own API, the process-side
counterpart of singbox/core (the binary). It moves from the generic
integrations/ folder, which held nothing else, to singbox/clashapi.

The HTTP-adapter ban that matched /integrations/ now names the new path,
and the layering test makes clashapi a leaf and keeps the config packages
from depending on a running sing-box.
SystemType and DetectSystemType lived in the sing-box controller package,
but they classify the HOST: the controller picks its init backend from
them, the installer picks opkg vs the install script, and the HTTP layer
picks the login default and navigation layout. They now sit in
platform/sysinfo beside the Distribution display name.

The installer's only reason to import the controller was this detection,
so it now depends on singbox/config and singbox/core alone. The HTTP
handler keeps the value as the plain string it serialises, so the routes
still import no platform package.
Every file in it manages a sing-box artifact: the binary (install script or
opkg), the Clash dashboard it serves as external_ui, the first config.json,
and the setup state recording which of those are done. It now sits beside
config at singbox/install, with state and state/repo below it.

With host detection moved to platform/sysinfo in the previous commit, the
package imports only singbox/config and singbox/core; the layering test now
forbids it the controller and clashapi. The Phase 0 import alias
`installer` is gone from the two handlers that no longer need it.
The Add/Edit DNS Rule dialog had no control for query_type, so a rule
matching on record types could only be written by hand. It is now a
multi-select of the common types (A, AAAA, HTTPS, SVCB, ...), rendered
apart from the rule-set/domain either-or because sing-box ANDs it with
every other condition.

sing-box decodes a DNSQueryType from a number or a known NAME and rejects
a numeric string, so utils/dnsQueryType converts at the boundary: numbers
become strings for the select and numeric strings go back out as numbers.
A value outside the common list stays visible as a selected option rather
than vanishing on open and being dropped on save.

No version gate: the field exists in the pinned 1.12.12 option struct, the
minimum supported core.
PrimeVue's Dialog defaults draggable to true and starts a drag on any
mousedown in the header, so selecting the title to copy it moved the whole
dialog instead. The volt wrapper now declares draggable itself with a false
default; a caller can still opt in with :draggable="true".
@SealinGp
SealinGp merged commit 782a199 into main Sep 26, 2026
1 of 2 checks passed
@SealinGp
SealinGp deleted the refactor/singbox-config-layout branch October 7, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant