Commit Graph
939 Commits
Author SHA1 Message Date
Tunglies 6e08a77a84 refactor(backend): specta ipc bindings (#8195)
* refactor(ipc): generate bindings with Specta

* fix(profile): project IPC inputs to consumed fields

* fix(events): remove unused refresh payloads

* fix(webdav): return only consumed backup metadata

* fix(network): trim unused interface metadata

* fix(sysinfo): return only displayed system fields

* chore(knip): check generated runtime exports

* fix(proxy): confirm runtime chain updates before UI changes

* fix(clipboard): propagate proxy environment copy failures

* refactor(dns): remove ignored runtime patch

* refactor(core): trim unused upgrade response fields

* refactor(ipc): avoid redundant response copies

* refactor(proxy): clarify automatic proxy query naming

* chore(tests): remove added IPC regression tests

* refactor(ipc): enforce typed command boundaries

* chore(contract): check generated bindings before push

* refactor(ipc): remove unused proxy liveness payloads
2026-10-12 06:16:05 +00:00
MrHardandClaude Opus 5.5 84312a5cd1 fix(sysproxy): bypass *.local by default on Windows (#8054)
Hostnames such as mi13.local are resolved by mDNS on the local network,
which the proxy core cannot do, so they failed with 'couldn't find ip'
while the system proxy was on. The macOS default already bypasses
*.local.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:32:27 +00:00
wonfen 4337bd8575 fix(core): clarify and localize existing core errors 2026-10-09 10:03:12 +08:00
cb751052a4 feat(tray): add a per-group latency test (#8173)
* feat(tray): add a per-group latency test

The tray's proxy group submenus offered no way to test a group: refreshing
the delays shown there meant opening the dashboard and running a test from
the proxy page.

Add a "Test Latency" entry at the head of each group submenu. It measures
the group through the proxy page's own view, so member resolution and
provider attribution match the page exactly: provider nodes are health
checked on their provider, anything else goes through the proxy delay
endpoint, and members the page cannot resolve are skipped. Nested groups
are therefore tested through their currently selected node, as the page
does. The menu is rebuilt when the run finishes.

The test URL follows the page's chain -- the group's own test URL, then the
configured default, then the built-in one -- including the page's validity
check, so a group with a malformed URL falls back where the page falls back.

A group test started from the app rebuilds the tray as well, so the two
views do not drift.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(tray): keep latency tests and displayed results consistent

* fix(tray): isolate latency histories and preserve usable menus

* fix(tray): keep settings usable when proxy reads fail

* fix(tray): show selected node latency for proxy groups and display timeouts as T/O

Co-authored-by: wonfen <eiwonfen@gmail.com>

* fix(tray): refresh proxy latency on menu interaction

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: wonfen <eiwonfen@gmail.com>
2026-10-09 09:11:07 +08:00
Tunglies 89076f01c3 refactor(rust): adopt stable APIs through Rust 1.99 (#8193) 2026-10-07 16:20:33 +00:00
Tunglies d01f43cc78 refactor(deps): replace once_cell with std OnceLock and LazyLock (#8136) 2026-10-07 07:38:56 +00:00
Tunglies 720dc13e96 refactor(backend): downgrade lock (#8135)
* refactor(updater): drop write-only pending_* slots

* refactor(notify): fold sequence counter into entries lock

* refactor(port): fold startup-core-blocked flag into reason slot

* refactor(service): drop redundant provider sync serial lock

* refactor(port): store startup block reason in a OnceLock

* refactor(core): replace macOS network monitor slot with armed latch
2026-10-07 07:38:56 +00:00
Tunglies 69b76cc6a9 refactor(frontend): contract flow (#8131)
* feat(contract): export wire contract from backend and generate frontend types

Adds a zero-dependency `frontend-contract` bin that serializes the real
NoticeStatus variants and FrontendEvent payloads, scripts/gen-contract.mjs
which emits the committed src/services/contract.ts, and a CI job that
regenerates it and fails on a non-empty diff. events.ts now derives its
event map from the generated contract instead of hand-typed shapes.

* feat(events): route all backend events through a single event bus and store

Adds src/services/bus (the only listen/emit site: one handler per contract
event plus the frontend-internal test-all event) and the React
context/reducer app store. Migrates the five scattered subscriptions
(layout events, pending failures, proxy refresh, profile update/timer,
test-all), deletes the old glue (events.ts, use-listen, use-layout-events,
LoadingCacheProvider), moves run state into the store, and adds an
architecture gate script wired into CI. Behavior is unchanged; the
run-state query's poll/focus safety net is preserved by the store
provider.

* refactor(notice): make the notice table exhaustive and drop dead wire entries

D5 cleanup, frontend and backend in one commit: removes the six
config_validate::* statuses that never receive a backend send and the
set_config::ok send (enum variants, golden list 41→34, send point in
restart_clash_core), their frontend handlers, and the five i18n keys only
those handlers used. The notice table is now typed as a Record over the
generated NoticeStatus union, so a status without a frontend entry fails
to compile. The info status (sent by the silent updater for breaking
change notices, previously dropped with a console warning) now shows an
info toast — the one deliberate behavior delta.

* feat(mutate): single funnel for user-intent commands and invoke gate

Adds src/services/mutate.ts: one path for optimistic apply, services-layer
invoke, Busy handling, failure rollback, and the error toast, with busy
bookkeeping mirrored into the app store. The patch hooks (verge, clash,
profiles) now route through it. Moves the remaining direct invokes
(unlock ×3, DNS ×4) into cmds.ts and turns on the invoke-only-in-services
architecture gate in CI.

* refactor(settings): route settings-domain commands through the mutate funnel

Ports port saving, webdav/local backup operations, core change/restart/
upgrade, service install/uninstall/repair flows, the system-proxy toggle
queue, the DNS override toggle, and app lifecycle commands onto the single
funnel. Bespoke flows keep their own error handling via errorNotice:false;
simple flows gain the funnel's default error toast and revalidation.

* refactor(profiles): route profile operations through the mutate funnel

Ports profile create/import/reorder/delete/update/patch/save-file and
enhance calls onto the funnel; retry and editor flows keep their own
error handling with errorNotice:false. Per-uid ids keep the update
worker pool concurrent under the funnel's in-flight dedupe.

* refactor(proxies): route proxy/home/misc commands through the mutate funnel

Ports mode switching (page and home card), chain runtime updates, node
selection with tray sync, selection recording, provider updates and
syncs, and lightweight-mode entry onto the funnel; every site keeps its
existing optimistic/revert and error-handling behavior.

* docs: add frontend architecture guardrails for src

Layering rules for the contract flow: generated contract as the only
wire-typing source, event bus as the only listen/emit site, pure-reducer
store for event-derived state, the mutate funnel for user intent, and
invoke only in the services layer — each backed by the CI gates.

* fix(events): close run-state race and restore single error toasts

Self-review findings: the bus now re-reads the run-state snapshot in
onSubscribed so an event racing the store's initial read cannot leave a
stale snapshot (the ordering the old layout listener guaranteed), and the
patch hooks pass errorNotice:false so GuardState/page handlers remain the
only error toast, matching the pre-migration behavior.

* fix(frontend): preserve queued intents and fresh runtime snapshots

* refactor(frontend): trim unused mutation and busy state APIs

* docs(frontend): focus guardrails on architecture ownership
2026-10-07 07:38:55 +00:00
Tunglies 8328e34208 refactor(backend): declarative effects (#8128)
* refactor(core): type notice statuses behind the frozen wire contract

* refactor(utils): unified retry and strategy-list helpers

* refactor(feat): declarative effect registry for verge and clash patches

* refactor(core): derive refresh announcements from transaction commit

* refactor(core): fold tray, hotkey, autostart updates into effect ensures

* refactor(utils): migrate remaining bounded retry loops

* docs: add src-tauri architecture guardrails

* refactor(core): coalesce burst refresh announcements

* refactor(core): complete bounded service retry migration

* ci(backend): add architecture ban gates

* test(feat): freeze representative effect registry mappings

* docs(backend): record dead wire contract entries

* chore(core): document watcher exit conditions

* fix(backend): preserve applied state across later failures

* fix(config): retain runtime after an applied core replacement

* refactor(backend): flatten refresh publication and retry policies

* docs(backend): focus guardrails on architecture ownership

* refactor(effects): use one execution order

* fix(config): publish retained runtime and reconcile proxy settings
2026-10-07 07:38:55 +00:00
❤是纱雾酱哟~ 78691ea5c8 fix(updater): clear cache when auto-check is disabled (#8091)
Assisted-by: wonfen
2026-10-07 13:16:34 +08:00
wonfen dba0f471bf fix(updater): retry slow update downloads from other sources and honour cancel 2026-10-06 14:21:09 +08:00
wonfen c341b9b238 fix(updater): install the already downloaded update instead of fetching it again 2026-10-06 14:21:09 +08:00
wonfen 6ec35c8195 fix(macos): restore system DNS reliably after TUN
Detached tasks spawned on every config generation set and restored the
placeholder DNS without ordering, so a set could back up the placeholder
itself and a later restore reported success while leaving it in place.

Reconcile from the applied config under the lifecycle lock, and restore
whenever the core stops. Keep one backup per network service, never back
up the placeholder, and restore only while the service still holds it.
Scripts verify every read and write and are killed after 5 s.

Refs #8070, #8168
2026-10-06 12:47:08 +08:00
wonfen a9a3cfa3a7 fix(proxy): resolve provider members within each group's providers
Same-name nodes from different proxy-providers were marked ambiguous and
greyed out. Resolve each group member against the group's own providers
(use / include-all, exclude-type) and bind the n-th occurrence of a name to
the n-th candidate, matching Mihomo.

Closes #8021
2026-10-03 22:49:39 +08:00
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>❤是纱雾酱哟~
14770df157 chore(deps): update rust toolchain to v1.99.0 (#8147)
* chore(deps): update rust toolchain to v1.99.0

* fix(rust): handle breaking changes introduced by compiler

- Trailing semicolon in macro should not be used in expression position
- A deprecated method was renamed for consistency
  - Before: `std::sync::atomic::Atomic::<u64>::fetch_update`
  - After: `std::sync::atomic::Atomic::<u64>::try_update`

Signed-off-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>

---------

Signed-off-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>
2026-10-03 12:31:08 +00:00
wonfen 57c5484080 fix(service): stop waiting on a service that no longer starts with Windows
When the service's start type was changed from automatic, startup waited
about two minutes for it before falling back to Sidecar. Detect this
immediately, skip the TUN readiness wait, and offer a reinstall that
restores automatic start.

Refs #8129
2026-10-03 20:40:51 +08:00
wonfen 4a0882fc18 fix(singleton): repair Windows lock file permissions in place 2026-10-03 15:59:39 +08:00
wonfen f958dccb25 fix(service): explain sidecar failures and clear recovered notices 2026-10-03 10:21:54 +08:00
wonfen 52d616302c fix(service): explain a startup core rejection instead of the generic fallback
When the startup probe finds the service refusing the approved core, the
app falls back to Sidecar with only the generic "could not start the
core" notice. The reason and repair guide appeared only after a
reinstall, which cannot fix a writable volume root (#8045).

Carry the fallback reason to the window and show the same permission
notice as a rejected installation when the core was refused.

Refs #8045
2026-10-02 10:38:09 +08:00
wonfen c4f9d65e07 fix(service): don't ask to repair the service when the proxy clear fails 2026-09-29 10:09:21 +08:00
wonfen 6a752994cf fix(service): fail fast when the Windows service is stopped
A registered but stopped service made every version probe back off for
about two minutes, blocking startup before the window and core (#8059).
Check the SCM first; a service that is starting, or not yet started this
boot, still goes through the IPC retries.
2026-09-25 11:55:01 +08:00
wonfen daae17a40c fix(service): surface a service core that stopped for good
When the service watchdog gave up or the core vanished, the owner
monitor marked the core stopped without telling the user, and on macOS
left the system proxy on the dead mixed port (#8029).

- On SameOwnerFailure, clear the proxy through the helper while the
  session is still ours; displaced or unreachable owners still leave the
  machine-wide setting alone.
- Retain the stop as a core failure with its own message, recorded
  under the lifecycle lock and shown now or on next focus; a running
  core resets the notice dedup.
- Log each watchdog restart the service reports, since launchd discards
  the service's own log.
2026-09-25 00:06:59 +08:00
wonfen 1f140dfd3a fix(service): show how to reclaim an app data root the service refuses
When the service refuses the app data root because another uid owns it
(code 1012), show a sticky notice with the chown command for this
account. Repairing the service cannot fix this.

Refs #8026
2026-09-24 22:10:42 +08:00
wonfen 04d74ac48f fix(service): offer repair when a residual helper blocks Sidecar
A macOS helper left running without IPC made Sidecar refuse to start
while the service counted as not installed, so no repair was offered
(#8031, #8043). Treat such a helper as install evidence and record the
Sidecar refusal as unavailable so the repair dialog appears.
2026-09-24 20:24:40 +08:00
wonfen 61c33c6113 fix(core): retain startup diagnostics until recovery
Show unresolved startup failures when the window becomes visible and clear them when either core mode starts successfully. Cover recovery followed by exit and WebView recreation, use neutral service permission wording, and document the Windows startup fix.
2026-09-24 18:43:28 +08:00
wonfen fbc4b516b8 fix(service): fall back after installation verification rejects cores 2026-09-24 18:43:26 +08:00
wonfen fd06fbb3b7 fix(service): adopt IPC 2.7.3 and correct dev fallback handling 2026-09-22 17:37:26 +08:00
wonfen ed0b801573 fix(service): unify installation and safe sidecar handoff
Delegate service and core installation to Service IPC so replacement uses
one elevation. Share fallback occupancy checks and retain the execution
guard until the Sidecar exits.

Track missing approved cores and start refusals as unavailable, and show
when no core is running. Build matching service tools from the local IPC
dependency and pass both core executables through the Dev installer.
2026-09-22 14:55:24 +08:00
wonfen bffe91db5e fix(service): prompt reinstallation after path permission rejection
Address #7983 with a recovery dialog for Windows service error 1003, including startup refusals received before the UI subscribes. Reinstall the service and require service mode before reporting recovery.
2026-09-22 09:02:56 +08:00
wonfen 93b7bb8fe8 fix(linux): stage approved cores when installing service
Pass available cores and their SHA-256 digests explicitly during service installation and repair, instead of relying on automatic discovery that can skip untrusted source locations. Preserve the arguments across root, pkexec, and sudo execution paths.

Fixes #7995
2026-09-22 08:41:55 +08:00
wonfen 3b958acb22 fix(config): isolate conflicting provider cache paths
Fixes #7989
2026-09-21 23:20:48 +08:00
wonfen b7197f6f4b fix(windows): fall back to sidecar after confirmed service failure 2026-09-21 21:48:13 +08:00
wonfen 07825afb1e Reapply "feat(dns): protect provider DNS from unconfirmed overrides (#7902)"
This reverts commit df79ed3efb.
2026-09-21 21:22:45 +08:00
oomeow 5059cdcb9d feat: build in asn mmdb (#7974)
* chore(scripts): add ASN mmdb resolution to prebuild tasks

* docs: update Changelog.md

* docs: update Cangelog.md

* feat: include ASN database in runtime resources
2026-09-21 13:03:03 +00:00
Tunglies af0f502f98 fix(macos): silence notification when hiding window with Cmd+W 2026-09-21 20:37:24 +08:00
Tunglies 8982525fd2 fix(updater): detect stable releases from build metadata versions 2026-09-21 19:11:30 +08:00
wonfen 81ce247ecc fix(service): repair initial owner identity and extend readiness wait 2026-09-21 17:32:48 +08:00
Tunglies b469bee352 fix(logging): include paths in file failure logs 2026-09-21 15:57:31 +08:00
Tunglies df79ed3efb Revert "feat(dns): protect provider DNS from unconfirmed overrides (#7902)"
This reverts commit 35a2b6c75c.

Closes #7994
Closes #7993
2026-09-21 15:08:33 +08:00
wonfen c6a4de1c6a fix(service): require exact version match and uninstall before reinstall 2026-09-21 07:52:51 +08:00
oomeow 2a7972fa5b fix(core): drain sidecar events before readiness probe to avoid start… (#7966)
* fix(core): drain sidecar events before readiness probe to avoid startup stall

* refactor(core): simplify core readiness generation handling

* docs: update Changelog.md
2026-09-20 09:43:31 +00:00
wonfen 9a879dd8d8 fix(service): fall back to elevated sidecar on path permission rejection
Preserve TUN settings and restore proxy operation after fallback.
Deliver fallback notices after UI subscription and stop retrying
deterministic bundle refusals. Publish fallback state atomically
and avoid conflicting service startup notifications.
2026-09-17 00:00:39 +08:00
wonfen f58e0a0af5 fix(service): stage approved cores during Windows service installation 2026-09-15 16:09:54 +08:00
wonfen d289a608d9 refactor(sysproxy): delegate macOS network monitoring to sysproxy-rs 2026-09-14 22:10:33 +08:00
wonfen bea03e1a09 fix: preserve custom TUN fields and sync service provider caches
Limit GUI authority to saved TUN fields while preserving custom overrides.

Sync provider caches after service startup, reloads and updates.
Publish copies atomically with session, runtime and cache metadata checks.
2026-09-11 20:44:58 +08:00
Tunglies 8812923eda perf(enhance): cut fixed config-generation cost
Every settings toggle reparsed the profile and chain files, ran four
Boa interpreters, serialized the runtime config twice, and re-ran
mihomo -t over bytes the core had just accepted. Builtin and template
scripts now run without Boa, custom scripts bind the config through
JSON.parse and take ownership instead of per-script clones, the config
serializes once, and mihomo -t is skipped unless the bytes, core
build, or geo assets changed. Heavy parses and serializations moved
to spawn_blocking. Large-subscription toggles drop from hundreds of
milliseconds to ~140ms with no new retained state.
2026-09-10 06:32:51 +08:00
wonfen 35a2b6c75c feat(dns): protect provider DNS from unconfirmed overrides (#7902)
Fixes #7292
2026-09-09 23:19:56 +08:00
Rusty Ravenandoomeow 380d03cb97 fix(macos): discard a core cache the current user cannot write (#7883)
* fix(macos): discard a core cache the current user cannot write

* docs(changelog): add the unwritable core cache fix

* docs: update changelog to include Linux in cache.db ownership fix

---------

Co-authored-by: oomeow <oomeow@outlook.com>
2026-09-09 11:34:43 +00:00
wonfen 3253828bf0 feat/fix: service core staging (#7899)
* feat(service): hand upgraded cores to the service through the installer

The service now only executes administrator-staged core copies, so an
in-app core upgrade has to deliver the new bytes there before the
restart asks for them; otherwise a service-mode restart keeps running
the previous core while the UI reports the new version.

After publishing the downloaded core, service-mode upgrades invoke the
elevated service installer with --install-core <path> --sha256 <hex>.
The digest is computed over the published file and rides the elevated
command line, which no other local account can alter, so a file
swapped after hashing is refused rather than staged. Sidecar-mode
upgrades run the file directly and prompt for nothing.

Rollback distinguishes staging that never ran, was refused, or
succeeded: only a successful staging obliges the rollback to restore
the approved copy too (deliberately not gated on the running mode,
which reads NotRunning after the very crash being rolled back), and a
refused staging restores the file so a retry does not see the new
version on disk and conclude there is nothing to do.

Requires clash-verge-service-ipc >= 2.6.7. The pinned dependency and
the packaged service release must move together with this change, or
the old installer silently treats --install-core as a plain install.

* feat(installer): attest bundled cores to the service at install time

prebuild computes sha256 digests of the downloaded mihomo sidecars
into packages/windows/core-hashes.nsh (gitignored), included through
the NSIS installerHooks path. After unpacking and before starting the
service, the installer stages both cores into the service's approved
directory with those digests, so what gets attested is exactly what
this installer shipped: a core swapped on disk in a user-writable
install directory is refused, not published. Failure is logged and
does not abort the install; a later elevated service install can
stage the cores again.

* chore(deps): require clash-verge-service-ipc v2.6.8

v2.6.8 is the first service whose installer understands
--install-core --sha256; the staging this branch introduces is inert
against anything older, because the old installer treats the flag as a
plain service installation and reports success. The service release and
this pin therefore have to ship in the same app release.

* fix(service): heap-allocate the core hashing buffer

clippy::large_stack_arrays denies the 64 KiB stack array.
2026-09-09 18:25:25 +08:00
Tunglies 2f370d6490 refactor(log): in-place writer lifecycle and span-based core observability (#7889)
* refactor(log): migrate logging to tracing-estuary pipeline

Swap flexi_logger + clash-verge-logger wiring for the tracing-estuary
crate: buffered file sink with explicit panic/exit drains, live level
switching, and tokio-trace/chrome-trace behind crate features.

AsyncLogger moves in-house (VecDeque ring); file format and rotation
stay unchanged.

* fix(log): reset sidecar writer in place and re-pin service-ipc v2.6.7

Rebuilding the sidecar FileLogWriter on config changes leaked flexi's
unstoppable flusher thread plus an fd each time; the writer is now flushed
and reset in place, matching what the service crate does.
get_clash_logs_by_service drops the CompactString conversion layer now that
service-ipc v2.6.7 returns plain Vec<String>, and AsyncLogger is renamed
LogRing to match.

* refactor(log): span-based observability and log noise cleanup

Instrument spans carry the structure flat lines never could: the core-start
spine (mode, readiness/owner generations, pid, config file), proxy apply and
clear routes with guard generations, service IPC sessions with refusal codes
and outcomes, profile-switch terminal outcomes, subscription fetch strategies,
core upgrades with per-proxy version probes, backup flows, timer tasks, config
generation uids, and the startup setup flow; running-mode changes emit one
guarded transition event each.
~90 hand-written noise lines are gone or demoted (entry+success pairs, polled
getter echoes, phase brackets, window sub-steps, platform install chatter);
errors keep their anyhow chain, operation name, and item id, and swallowed
retry results surface per attempt.
2026-09-08 05:51:04 +08:00