* fix(core): reopen PAC when a start attempt never happens `save_proxy_ports` closes the PAC endpoint before it stages a candidate, because the endpoint resolves the Mixed Port through the draft layer and would otherwise answer with a port nothing is listening on. Most ways out of that function are a rejection, though: the candidate is refused and the Core keeps serving on its old ports, having never been stopped. PAC is re-derived only when the Running Mode changes, and that is exactly what did not happen — so the endpoint stayed shut for the rest of the session, and every browser on PAC silently went direct. Give `core_starting` the counterpart it was missing. `core_start_settled` re-derives PAC from the Running Mode rather than restoring a saved flag, which makes it correct on all three endings — a start that succeeded, one that was abandoned, one that failed and left the Core down — and therefore safe to run from a guard on every path. * fix(config): refuse overlapping draft transactions instead of losing an edit A layer holds exactly one draft, so a second writer's `edit_draft` overwrites the first writer's staged value in place. `DraftTransaction` nonetheless rolled back by calling `discard` on the shared layer, which discarded whatever was staged there — including a change another transaction was about to commit. The other transaction then found no draft, and `apply` on an absent draft does nothing at all. It saved the committed value, returned Ok, and its side effect had already happened: auto-launch registered with the OS while the config that describes it says otherwise, and no log line either way. "Roll back only my edit" cannot be implemented over a single slot, so enforce the exclusivity the type already assumed. Beginning a transaction claims every layer and refuses rather than interleaving, which is the same shape as `try_start_config_update` and `begin_operation` elsewhere. `patch_verge` is the caller that matters: it holds its draft across `process_terminated_flags`, which restarts the Core and can take seconds. The TUN reconciliation runs on every Run State transition and can now lose that race, so it treats a busy layer as "later" rather than as a failure worth telling the user about. * fix(core): reconcile TUN availability when the setting itself changes Moving the TUN availability guard from the frontend into Rust also moved what triggers it. The old effect depended on `enable_tun_mode`; the new reconciliation is spawned from `RealEnv::publish`, so it only runs on a Run State transition. Those two sets are not the same, and the setting itself falls in the gap. Switching TUN on from the global hotkey is the case that reaches it. That path is not gated on availability — unlike the tray item and the settings switch, both of which read `tun_capable` — and the flags a TUN patch raises are answered by a Core reload, which changes no Run State at all. So on a machine with no Service and no elevation the setting stayed on: tray icon, settings switch and `enable_tun_mode` in verge.yaml all reported TUN as enabled while nothing carried its traffic, until some unrelated core lifecycle transition happened to reconcile it. Reconcile after any patch that touched the setting. The reconciliation writes configuration itself, so it goes through the plain apply rather than back through the patch-and-reconcile form; the absence of a cycle is then a property of the call graph rather than of a runtime early-return. `toggle_tun_mode` now reads the setting back instead of returning what it asked for, so the hotkey cannot report TUN as on moments before the notice saying it was turned off. * fix(core): an action already under way is not a decision to put to the user The legacy status could not distinguish a requested privileged operation from one already running, because readers blocked until the operation finished and so never observed the request at all. Run State pushes every transition instead, and `service_needs_attention` treated any pending action as a question for the user. So an install started from the settings page or the home card raised the migration dialog on top of it, and an uninstall raised one offering to reinstall the Service being removed. Both offer a button that can only fail, since the privileged-operation slot is already taken. An operation in flight is a report of progress. The dialog the user is already driving keeps itself open through its own loading state. `tun_should_be_disabled` is unaffected: it already refused to act while an operation was in flight, so the term this adds is redundant there. * fix(core): let the snapshot decide whether the Run State has settled `settled` read the operation slot twice: once to decide whether to wait, then again inside the snapshot it returned. An operation claiming the slot between those two reads was described rather than waited for. `prepare_startup` reads this. A start racing an install would see the requested install, flatten it to `InstallRequired`, and decide `Wait` — leaving the core stopped where waiting for the install to finish would have told it whether the Service came back. Read the slot once, inside the snapshot, so there is no window rather than a second check closing it. The rewrite that introduced this also fixed a lost wakeup here, and that fix stands: the waiter is still registered before the state is read. * fix(ui): re-read the Run State once its listener is live Replacing the poll with a push left a gap. `listen` resolves asynchronously, so between the first `get_runtime_state` and the subscription being registered there is a window, and a transition landing in it is not queued anywhere — it is simply missed. The next event is whenever the backend happens to move again, which during startup can be a long time; the 30-second safety net was all that stood behind it, where the old code polled every two seconds precisely through startup. The Run State is the one thing here that arrives only by event, so it is the one thing a missed event leaves stale. Re-read it once every listener is live, which closes the gap without going back to polling for it. `onSubscribed` fires even when a registration failed, because a resync is most needed when a subscription is missing, and not at all after teardown. * docs(config): state what makes the PAC endpoint's draft read safe `MixedPort::desired` reads the draft layer, so the PAC endpoint can name a port the Core has not moved to yet. That is only safe because the one path staging a listener port closes PAC across the change. Nothing said so at either end, and the two are in different modules. * fix(core): ask again when staging goes silent rather than assume it failed The Service commits the generation before it answers, so a reply that never arrives is not evidence that nothing was written: config.yaml can already hold the new configuration while this side concludes the staging never happened. The Core keeps running the previous configuration, so nothing looks wrong -- until the Core dies and the Service's watchdog restarts it in the same generation from whatever config.yaml says, quietly making live a configuration the app reported as failed. Silence is the one answer that has to be resolved rather than acted on, so ask once more before falling back. It is cheap exactly where it matters: staging plans against the manifest it wrote, so a bundle that already landed copies nothing and answers in milliseconds, and the Service's lifecycle lock makes a second ask wait for a first one still in flight instead of interleaving with it. A deadline keeps that wait off the caller, and a second silence goes where the first one used to.
Continuation of Clash Verge
A Clash Meta GUI based on Tauri.
Languages: 简体中文 · English · Español · Русский · 日本語 · 한국어 · فارسی
Preview
| Dark | Light |
|---|---|
![]() |
![]() |
Install
请到发布页面下载对应的安装包:Release page
Go to the Release page to download the corresponding installation package
Supports Windows (x64/x86), Linux (x64/arm64) and macOS 11+ (intel/apple).
我应当怎样选择发行版
| 版本 | 特征 | 链接 |
|---|---|---|
| Stable | 正式版,高可靠性,适合日常使用。 | Release |
| Alpha(废弃) | 测试发布流程。 | Alpha |
| AutoBuild | 滚动更新版,适合测试反馈,可能存在缺陷。 | AutoBuild |
安装说明和常见问题,请到 文档页 查看
TG 频道: @clash_verge_rev
Promotion
✈️ 狗狗加速 —— 技术流机场 Doggygo VPN
🚀 高性能海外技术流机场,支持免费试用与优惠套餐,全面解锁流媒体及 AI 服务,全球首家采用 QUIC 协议。
🎁 使用 Clash Verge 专属邀请链接 注册即送 3 天免费试用,每日 1GB 流量:👉 点此注册
核心优势:
- 📱 自研 iOS 客户端(业内"唯一")技术经得起考验,极大持续研发投入
- 🧑💻 12小时真人客服(顺带解决 Clash Verge 使用问题)
- 💰 优惠套餐每月仅需 21 元,160G 流量,年付 8 折
- 🌍 海外团队,无跑路风险,高达 50% 返佣
- ⚙️ 集群负载均衡设计,负载监控和随时扩容,高速专线(兼容老客户端),极低延迟,无视晚高峰,4K 秒开
- ⚡ 全球首家Quic 协议机场,现已上线更快的 Quic 类协议(Clash Verge 客户端最佳搭配)
- 🎬 解锁流媒体及 主流 AI
🌐 官网:👉 https://狗狗加速.com
🤖 GPTKefu —— 与 Crisp 深度整合的 AI 智能客服平台
- 🧠 深度理解完整对话上下文 + 图片识别,自动给出专业、精准的回复,告别机械式客服。
- ♾️ 不限回答数量,无额度焦虑,区别于其他按条计费的 AI 客服产品。
- 💬 售前咨询、售后服务、复杂问题解答,全场景轻松覆盖,真实用户案例已验证效果。
- ⚡ 3 分钟极速接入,零门槛上手,即刻提升客服效率与客户满意度。
- 🎁 高级套餐免费试用 14 天,先体验后付费:👉 立即试用
- 📢 智能客服TG 频道:@crisp_ai
Features
- 基于性能强劲的 Rust 和 Tauri 2 框架
- 内置Clash.Meta(mihomo)内核,并支持切换
Alpha版本内核。 - 简洁美观的用户界面,支持自定义主题颜色、代理组/托盘图标以及
CSS Injection。 - 配置文件管理和增强(Merge 和 Script),配置文件语法提示。
- 系统代理和守卫、
TUN(虚拟网卡)模式。 - 可视化节点和规则编辑
- WebDav 配置备份和同步
FAQ
Refer to Doc FAQ Page
Donation
Development
See CONTRIBUTING.md for more details.
To run the development server, execute the following commands after all prerequisites for Tauri are installed:
pnpm i
pnpm run prebuild
pnpm dev
pnpm dev preserves the Development Channel's installed service state: an
existing service is used, while a previously uninstalled service remains
uninstalled and the app starts in Sidecar mode. Use pnpm dev:service to
explicitly install or update the isolated development service before launch,
or pnpm dev:sidecar to force the unprivileged Sidecar workflow.
Contributions
Issue and PR welcome!
Acknowledgement
Clash Verge rev was based on or inspired by these projects and so on:
- zzzgydi/clash-verge: A Clash GUI based on tauri. Supports Windows, macOS and Linux.
- tauri-apps/tauri: Build smaller, faster, and more secure desktop applications with a web frontend.
- Dreamacro/clash: A rule-based tunnel in Go.
- MetaCubeX/mihomo: A rule-based tunnel in Go.
- Fndroid/clash_for_windows_pkg: A Windows/macOS GUI based on Clash.
- vitejs/vite: Next generation frontend tooling. It's fast!
License
GPL-3.0 License. See License here for details.

