Skip to content

Features

Juwan-Hwang edited this page Jul 17, 2026 · 30 revisions

功能特性

Zephyr 提供了一套完整的代理管理功能集,涵盖实时流量监控、智能节点选择、高级安全防护与跨平台适配。本文档对每一项核心功能进行详细的技术说明,并通过 Mermaid 图表直观展示数据流与处理逻辑。


目录


实时流量监控

Zephyr 内置了一套完全自研的实时流量可视化引擎,零第三方图表库依赖,基于原生 Canvas 2D API 从零构建双色面积图,提供流畅、低开销的实时网络流量展示。

数据流架构

流量数据从 Mihomo 内核出发,经过 HTTP Streaming 传输、逐行解析、滑动窗口缓冲,最终由 Canvas 2D 渲染引擎绘制为贝塞尔曲线面积图。以下时序图展示了完整的数据流转过程:

%%{init: {'themeVariables': {'fontSize': '10px', 'actorTextColor': '#ccc', 'signalColor': '#666', 'labelTextColor': '#999', 'labelBoxBkgColor': '#333', 'labelBoxBorderColor': '#555', 'lineColor': '#666', 'actorBkg': '#333', 'actorBorder': '#555', 'actorTextColor': '#ccc', 'signalTextColor': '#ccc', 'noteBkgColor': '#333', 'noteTextColor': '#ccc', 'noteBorderColor': '#555'}}}%%
sequenceDiagram
    participant MC as Mihomo Core
    participant HS as HTTP Stream<br/>(/traffic)
    participant WS as websocket.js<br/>(ReadableStream)
    participant SW as 滑动窗口<br/>(60 数据点)
    participant TC as modules/traffic-chart.js<br/>(渲染引擎)
    participant CV as Canvas 2D<br/>(贝塞尔面积图)

    MC->>HS: GET /traffic (chunked JSON)
    activate HS

    loop 持续推送 (每秒约 1 次)
        HS-->>WS: {"up": 1024, "down": 51200}
        WS->>WS: TextDecoder 解码
        WS->>WS: 按换行符分割
        WS->>SW: JSON.parse(data)
        SW->>SW: pushToSlidingWindow()
        SW->>SW: 移除最旧数据点 (窗口 > 60)
        SW->>TC: 通知数据更新
        TC->>TC: requestAnimationFrame 调度
        TC->>CV: 清除画布
        TC->>CV: 绘制下载面积图 (紫色)
        TC->>CV: 绘制上传面积图 (蓝色)
        CV-->>TC: 渲染完成
    end

    Note over HS,WS: 连接断开时触发指数退避重连
    WS->>HS: 自动重连 (delay = min(1s * 2^attempt, 30s))
Loading

数据采集

流量数据通过 HTTP Streaming(非 WebSocket)从 Mihomo 内核的 /traffic 端点获取。与 WebSocket 方案相比,HTTP Streaming 具备更低的协议开销和更简洁的断线重连逻辑。

// 伪代码:HTTP Streaming 数据采集流程
const response = await fetch("http://127.0.0.1:9090/traffic");
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const text = decoder.decode(value, { stream: true });
  const lines = text.split("\n").filter(Boolean);

  for (const line of lines) {
    const data = JSON.parse(line);
    // data.up -> 上传速度 (bytes/s)
    // data.down -> 下载速度 (bytes/s)
    pushToSlidingWindow(data);
  }
}

可视化渲染

特性 说明
双通道面积图 紫色表示下载流量,蓝色表示上传流量,半透明填充叠加
贝塞尔曲线平滑 数据点之间使用贝塞尔曲线插值,避免折线的视觉生硬感
60 点滑动窗口 保留最近 60 个采样点,旧数据自动移出视口
requestAnimationFrame 节流 渲染频率与显示器刷新率同步,避免无效重绘
DPR 适配 Canvas 物理像素按 devicePixelRatio 缩放,高分屏下保持锐利
// 伪代码:DPR 适配与 Canvas 初始化
const canvas = document.getElementById("traffic-chart");
const ctx = canvas.getContext("2d");
const rect = canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;

canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
// 后续绘制使用 CSS 像素坐标,Canvas 自动映射到物理像素

速度格式化

自动根据数值大小切换显示单位,保持界面简洁:

速度范围 显示格式 示例
< 1024 B/s XXX B/s 512 B/s
< 1024 KB/s X.XX KB/s 86.42 KB/s
>= 1024 KB/s X.XX MB/s 3.14 MB/s

以下决策树展示了速度值从原始字节到格式化显示的完整转换逻辑:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[输入:速度值<br/>bytes/s] --> B{值 < 1024?}
    B -->|是| C[显示为 B/s<br/>格式:XXX B/s]
    B -->|否| D[值除以 1024<br/>转为 KB/s]
    D --> E{值 < 1024?}
    E -->|是| F[显示为 KB/s<br/>保留 2 位小数<br/>格式:X.XX KB/s]
    E -->|否| G[值再除以 1024<br/>转为 MB/s]
    G --> H[显示为 MB/s<br/>保留 2 位小数<br/>格式:X.XX MB/s]

    style A fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style B fill:#3B82F6,stroke:#2563EB,color:#fff
    style E fill:#3B82F6,stroke:#2563EB,color:#fff
    style C fill:#10B981,stroke:#059669,color:#fff
    style F fill:#10B981,stroke:#059669,color:#fff
    style H fill:#10B981,stroke:#059669,color:#fff
Loading

断线重连

采用指数退避重连策略,在连接中断时自动恢复,同时避免对内核造成请求风暴:

参数 说明
初始延迟 1 秒 首次重连等待时间
最大延迟 30 秒 退避上限
最大重试次数 15 次 超过后停止重连并提示用户
解析错误阈值 连续 10 次 触发强制重连(应对流格式异常)

退避公式:delay = min(initial * 2^attempt, max_delay)

以下流程图展示了 HTTP Streaming 连接建立、数据接收循环及断线重连的完整处理逻辑:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[构造 Streaming URL<br/>http://127.0.0.1:9090/traffic] --> B[调用 fetch 发起请求]
    B --> C[获取 Response.body<br/>ReadableStream]
    C --> D[创建 TextDecoder]
    D --> E[进入数据读取循环]

    E --> F[reader.read 读取 chunk]
    F --> G{stream done?}
    G -->|是| H[触发指数退避重连<br/>delay = min 1s * 2^attempt, 30s]
    H --> I{重试次数 > 15?}
    I -->|是| J[停止重连<br/>提示用户连接中断]
    I -->|否| B

    G -->|否| K[TextDecoder 解码 chunk]
    K --> L[按换行符分割文本]
    L --> M[逐行 JSON.parse]
    M --> N{解析成功?}
    N -->|否| O[错误计数 +1]
    O --> P{连续错误 > 10?}
    P -->|是| Q[强制重连<br/>重置错误计数]
    Q --> B
    P -->|否| E

    N -->|是| R[pushToSlidingWindow]
    R --> S[移除最旧数据点<br/>窗口保持 60 个]
    S --> T[通知渲染引擎更新]
    T --> U[requestAnimationFrame 调度绘制]
    U --> E

    style A fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style B fill:#3B82F6,stroke:#2563EB,color:#fff
    style E fill:#3B82F6,stroke:#2563EB,color:#fff
    style H fill:#F59E0B,stroke:#D97706,color:#fff
    style Q fill:#EF4444,stroke:#DC2626,color:#fff
    style J fill:#EF4444,stroke:#DC2626,color:#fff
    style R fill:#10B981,stroke:#059669,color:#fff
    style T fill:#10B981,stroke:#059669,color:#fff
Loading

Core 生命周期优化 (v2.2.3)

v2.2.3 对 Mihomo Core 的启动、健康检查和终止流程进行了全面优化,提升启动速度并确保优雅退出。

健康检查指数退避

优化前 优化后 效果
固定 1s 轮询,最多 20 次 指数退避 50ms → 1s 启动等待从最坏 20s 降低到约 2s
同步 I/O 异步 I/O (tokio::net::TcpStream) 非阻塞,响应更快

连接排空 (Connection Draining)

终止 Core 前主动排空活跃连接:

  • 调用 DELETE /connections 通知内核关闭所有连接
  • 2s 超时保护,避免阻塞
  • 帮助 Mihomo 在繁忙时更快、更干净地退出

优雅终止

SIGTERM → 等待 2s → SIGKILL

替代之前的直接 SIGKILL,给予进程清理资源的机会。


智能节点选择

Zephyr 提供了直观、高效的节点选择体验,结合响应式布局、延迟可视化与 3D 交互效果,让用户在海量节点中快速定位最优选择。

节点选择流程

从加载节点列表到最终切换代理,整个交互流程涉及前端 UI 渲染、Mihomo API 调用和界面状态同步。以下流程图展示了完整的节点选择链路:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[加载节点列表] --> B[GET /proxies API]
    B --> C[解析代理组与节点数据]
    C --> D[渲染节点卡片网格]
    D --> E{用户操作}

    E -->|点击节点卡片| F[调用 switchProxy API]
    E -->|鼠标悬停 300ms| G[展开节点列表]
    E -->|滚轮滚动| H[切换当前选中节点]

    F --> I[PUT /proxies/group_name]
    I --> J[Mihomo 内核切换代理]
    J --> K[返回切换结果]
    K --> L[更新 UI 状态]
    L --> M[刷新延迟标签颜色编码]
    M --> N[同步托盘菜单状态]

    G --> O[显示完整节点列表]
    O --> P[展示延迟标签与节点名称]

    H --> Q[更新首页节点选择器显示]
    Q --> N

    style A fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style F fill:#3B82F6,stroke:#2563EB,color:#fff
    style J fill:#10B981,stroke:#059669,color:#fff
    style N fill:#F59E0B,stroke:#D97706,color:#fff
Loading

响应式网格布局

节点列表采用 1-4 列自适应网格,根据窗口宽度与屏幕分辨率动态调整列数:

窗口宽度 列数 适用场景
< 640px 1 列 窄窗口 / 小屏显示器
640px - 1024px 2 列 标准窗口
1024px - 1536px 3 列 大屏显示器
> 1536px 4 列 超宽屏 / 4K 显示器

延迟颜色编码

节点卡片上的延迟标签通过颜色直观反映连接质量:

延迟范围 颜色 含义
< 200ms 绿色 体验优良,适合实时应用
200ms - 500ms 黄色 可用,可能有轻微延迟
> 500ms 红色 延迟较高,不适合实时应用
测试中 灰色 + 加载动画 正在测量延迟
不可达 红色 + 超时标记 节点无法连接

传输层协议指示器

节点卡片上显示实际的传输层协议(TCP / QUIC / UDP / TLS),替代之前固定的 "UDP" 标签。协议信息从 Mihomo 连接数据的 chains 中提取,反映节点真实使用的底层传输方式。

排序模式

支持三种排序方式,通过节点面板顶部的排序按钮一键循环切换:

  1. 默认排序 -- 按 Mihomo 内核返回的原始顺序
  2. 按延迟排序 -- 延迟从低到高,不可达节点排末尾
  3. 按名称排序 -- 按节点名称的字典序排列

3D 悬浮效果

节点卡片在鼠标悬停时呈现 3D 透视旋转效果,通过 CSS perspective + rotateX/Y 实现,requestAnimationFrame 节流确保流畅:

/* 伪代码:3D 悬浮效果核心样式 */
.node-card {
  perspective: 1000px;
  transform-style: preserve-3d;
  transition: transform 0.15s ease-out;
}
// 伪代码:鼠标跟踪 3D 旋转
card.addEventListener("mousemove", (e) => {
  requestAnimationFrame(() => {
    const rect = card.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width - 0.5;
    const y = (e.clientY - rect.top) / rect.height - 0.5;
    card.style.transform =
      `rotateY(${x * 15}deg) rotateX(${-y * 15}deg) scale(1.02)`;
  });
});

节点轮盘选择

首页顶部的节点胶囊显示当前实际使用的出口节点名(而非代理组名),宽度从 180px 扩展到 280px以容纳长名称。点击胶囊展开轮盘(Roulette)选择器:

  • 竞态条件防护:使用 session 计数器防止快速连续点击导致的 race condition
  • 非阻塞更新:节点切换后通过 syncCoreConfig() 异步更新显示,避免 UI 卡顿
  • 自动同步:切换成功后自动同步到托盘菜单和代理列表页

以下时序图展示了节点轮盘选择的完整交互流程:

%%{init: {'themeVariables': {'fontSize': '10px', 'actorTextColor': '#ccc', 'signalColor': '#666', 'labelTextColor': '#999', 'labelBoxBkgColor': '#333', 'labelBoxBorderColor': '#555', 'lineColor': '#666', 'actorBkg': '#333', 'actorBorder': '#555', 'actorTextColor': '#ccc', 'signalTextColor': '#ccc', 'noteBkgColor': '#333', 'noteTextColor': '#ccc', 'noteBorderColor': '#555'}}}%%
sequenceDiagram
    participant U as 用户
    participant UI as 前端 UI
    participant RO as 轮盘组件<br/>(Roulette)
    participant API as Mihomo API
    participant TM as 托盘菜单

    U->>UI: 点击代理胶囊
    activate UI
    UI->>RO: 渲染轮盘遮罩层
    activate RO
    RO->>API: GET /proxies/{group_name}
    activate API
    API-->>RO: 返回节点列表
    deactivate API
    RO->>RO: 渲染可滚动节点列表
    RO-->>U: 展示轮盘界面

    U->>RO: 点击目标节点
    activate RO
    RO->>UI: 调用 switchProxy(group, node)
    activate UI
    UI->>API: PUT /proxies/{group_name}
    activate API
    API->>API: Mihomo 切换代理节点
    API-->>UI: 返回切换结果
    deactivate API
    UI-->>RO: 切换成功
    deactivate UI

    RO->>RO: 更新当前激活节点显示
    RO->>RO: 关闭轮盘遮罩层
    RO-->>U: 显示切换成功反馈
    deactivate RO

    UI->>TM: 同步托盘菜单节点状态
    deactivate UI
Loading

渲染优化

  • 滚动动画节流:所有滚动相关动画均通过 requestAnimationFrame 节流

订阅下载三级回退

Zephyr 提供功能完善的订阅管理系统,支持多种订阅格式、客户端伪装与灵活的下载策略。订阅下载采用三级回退策略,确保在各种网络环境下都能成功获取订阅。

下载流程

从 URL 验证到最终写入文件,订阅下载涵盖 SSRF 防护、多策略回退、内容解析、安全清理和加密存储等多个环节。以下流程图展示了完整的处理链路:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[用户输入订阅 URL] --> B[SSRF 防护验证]
    B --> C{URL 合法?}
    C -->|否| ERR1[拒绝请求]

    C -->|是| D[第一优先级:直连下载]
    D --> E{下载成功?}
    E -->|是| K

    E -->|否| F[第二优先级:Mihomo 代理下载]
    F --> G{下载成功?}
    G -->|是| K

    G -->|否| H[第三优先级:临时切换 Global 模式下载]
    H --> I{下载成功?}
    I -->|是| J[恢复原模式] --> K
    I -->|否| J2[恢复原模式] --> ERR2[返回下载失败错误]

    K[Base64 检测] --> L{内容为 Base64?}
    L -->|是| M[Base64 解码]
    L -->|否| N[直接使用原始内容]
    M --> O
    N --> O[YAML 解析]

    O --> P{解析成功?}
    P -->|否| ERR3[返回解析错误]
    P -->|是| Q[清除危险键]
    Q --> R[移除 script / script-path]
    Q --> S[移除 provider.path]

    R --> T[加密存储元数据]
    S --> T
    T --> U[get_machine_key 获取密钥]
    U --> V[obfuscate_string AES-256-GCM 加密 URL]
    V --> W[写入配置文件]
    W --> X[write_file_secure 设置 0o600 权限]
    X --> Y[返回配置信息]

    style B fill:#EF4444,stroke:#DC2626,color:#fff
    style D fill:#3B82F6,stroke:#2563EB,color:#fff
    style F fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style H fill:#F59E0B,stroke:#D97706,color:#fff
    style Q fill:#EF4444,stroke:#DC2626,color:#fff
    style T fill:#10B981,stroke:#059669,color:#fff
    style ERR1 fill:#991B1B,color:#fff
    style ERR2 fill:#991B1B,color:#fff
    style ERR3 fill:#991B1B,color:#fff
Loading

多格式兼容

格式 处理方式 说明
Clash YAML 直接解析 标准 Mihomo / Clash 配置格式
Base64 编码 自动检测并解码 兼容 V2Ray / Shadowrocket 等分享链接格式

自动检测逻辑:当响应内容不包含 YAML 关键标识(proxies:port:)时,尝试 Base64 解码。解码后还会验证结果是否为包含 proxies: 的有效 YAML。

客户端伪装

支持自定义 User-Agent 请求头,内置多个流行客户端的版本信息查询:

客户端 说明
Clash Verge 模拟 Clash Verge 客户端请求
Mihomo Party 模拟 Mihomo Party 客户端请求
FlClash 模拟 FlClash 客户端请求
Shadowrocket 模拟 Shadowrocket 客户端请求
自定义 用户可输入任意 UA 字符串

流量信息解析

自动解析订阅响应中的 subscription-userinfo 头部字段,展示流量使用进度:

subscription-userinfo: upload=1073741824; download=3221225472; total=107374182400; expire=1735689600
字段 说明
upload 已用上传流量(字节)
download 已用下载流量(字节)
total 总流量额度(字节)
expire 到期时间(Unix 时间戳)

其他功能

  • 智能订阅命名:优先使用 Content-Disposition 头部文件名,其次提取策略组名称
  • 重复名称自动后缀:新增订阅时若文件名已存在,自动追加递增数字后缀
  • 订阅重命名:右键菜单重命名配置文件,自动同步 last_configlast_proxy_selection
  • 订阅编辑面板:内联编辑订阅 URL、名称和更新间隔,实时验证,保留用户自定义名称
  • 订阅自动更新调度器:每个订阅可配置独立更新间隔(30 分钟 ~ 24 小时),后台 tokio 定时任务自动执行,支持手动触发全部更新
  • 批量更新:一键更新所有已添加的订阅(绕过单次速率限制)
  • 拖拽导入:支持将 .yaml / .yml 文件直接拖拽到订阅页面导入
  • 拖拽排序:订阅列表支持拖拽重新排序,排序结果自动持久化

代理节点记忆(v2)

Zephyr 自动记住每个配置文件的代理组 + 节点选择,切换配置时自动恢复:

  • v2 三元组:同时保存 group + node(v1 仅保存节点名)
  • 主组偏好savePrimaryGroupPreference / getPrimaryGroupPreference 独立保存用户选择的主组
  • 恢复优先链:primary preference > saved group > current UI group
  • v1 兼容迁移parseSelection 自动识别 v1 纯字符串和 v2 JSON 格式
  • 原子更新:使用后端原子操作更新,避免竞态条件
  • 迁移兼容:重命名配置时自动迁移 last_proxy_selectionprimary_group_preference key

主组 Resolver

Zephyr 使用确定性的 7 级优先链解析主代理组,替代旧的关键词猜测逻辑:

优先级 来源 说明
1 preferredGroupName(UI 显式选择) 用户在界面上手动切换的组,必须是 writable
2 primaryGroupPreference(持久化偏好) 通过 savePrimaryGroupPreference 保存
3 effectiveGroup(FINAL/MATCH 规则) run_config.yaml 的 rules 中提取
4 orderedGroups[0](YAML 定义顺序) proxy-groups 数组中第一个 writable 组
5 topLevelGroups[0](GLOBAL.all) GLOBAL 组的所有成员中第一个 writable 组
6 关键词评分最佳匹配 proxy/节点/选/代理 = +5;auto/url-test = -10
7 第一个 writable 组(绝对回退) 按名称排序

辅助模块

  • run-config-cache.js:TTL 缓存(5 秒)+ 请求合并 + 代际计数器防陈旧
  • buildOrderedGroups:从 run_config.yamlproxy-groups 按 YAML 顺序构建有序组列表

observedGroup 观察器

后台轮询连接数据,自动检测用户实际使用的代理组和节点:

  • 采样:取最近 30 条连接,遍历 chains 找第一个 writable group
  • 节点追踪:提取每个组内最频繁的实际出口节点,写入 observedNodeName
  • 阈值:频率 ≥ 3 且比率 ≥ 0.3 才视为有效
  • 连续确认:连续 3 次(K=3)一致结果后才更新(组变化 info 级别日志,节点变化 debug 级别)
  • 轮询间隔:5 秒
  • 特殊组排除:DIRECT、REJECT、PASS 等特殊组不参与检测
  • 用途:辅助 Resolver 确定用户实际偏好,提升主组解析准确度

TUN 虚拟网卡

Zephyr 支持通过 TUN 虚拟网卡实现系统级透明代理,接管所有网络流量,无需手动配置系统代理。macOS 平台由于权限模型限制,需要通过 osascript 进行提权操作。

macOS TUN 提权流程

以下流程图展示了 macOS 平台上 TUN 模式从检查状态到最终激活的完整提权与启动过程:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[用户请求启用 TUN] --> B[检查 CORE_STARTING 状态]
    B --> C{内核正在启动?}
    C -->|是| ERR1[返回:内核启动中,请稍后]
    C -->|否| D[检查 TUN_MODE_ACTIVE 状态]

    D --> E{TUN 已激活?}
    E -->|是| F[执行关闭 TUN 流程]
    E -->|否| G[停止当前 Mihomo 进程]

    G --> K[构造 osascript 提权命令]
    K --> L[弹出系统授权对话框]
    L --> M{用户确认授权?}
    M -->|取消| N[返回:用户取消]
    M -->|确认| O[以 root 权限启动 Mihomo]

    O --> P[轮询等待 root 进程启动]
    P --> Q{检测到 root 进程?}
    Q -->|否 - 超过 30s| R[返回:提权超时]
    Q -->|是| S[HTTP 健康检查 API 端口]

    S --> T{端口可达?}
    T -->|否 - 重试 10 次| U[等待 300ms 后重试]
    U --> S
    T -->|是| V[设置 TUN_MODE_ACTIVE = true]
    V --> W[更新托盘图标为红色]
    W --> X[通知前端 TUN 状态已更新]
    X --> Y[TUN 模式激活完成]

    style A fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style K fill:#F59E0B,stroke:#D97706,color:#fff
    style O fill:#EF4444,stroke:#DC2626,color:#fff
    style V fill:#10B981,stroke:#059669,color:#fff
    style Y fill:#10B981,stroke:#059669,color:#fff
    style ERR1 fill:#991B1B,color:#fff
    style ERR2 fill:#991B1B,color:#fff
Loading

跨平台权限管理

平台 权限获取方式 说明
macOS osascript 提权 通过 AppleScript 弹出授权对话框,用户确认后以 root 身份启动
Windows 标准权限 直接调用系统 API 创建 TUN 接口
Linux pkexec 授权 通过 pkexec 弹窗授权,为 mihomo 二进制授予 CAP_NET_ADMIN capability 并安装 polkit 规则,后续无需重复授权

托盘图标状态同步

系统托盘图标实时反映当前网络状态:

状态 图标颜色 说明
TUN 激活 红色 系统级透明代理已启用
系统代理 黄色 仅系统代理模式运行
直连 / 未启用 默认色 无代理或直连模式

DNS 防泄漏

启用 TUN 模式时,Zephyr 自动注入 dns-hijack 配置,劫持所有 DNS 流量到 Mihomo 内核,防止应用绕过代理直接查询系统 DNS。

自动注入逻辑

// 确保 dns-hijack 包含 UDP 和 TCP 两项
const ANY_UDP: &str = "any:53";
const ANY_TCP: &str = "tcp://any:53";

// 如果配置中已存在 dns-hijack,补充缺失项
// 如果不存在,创建新的 dns-hijack 列表

生成的配置

tun:
  enable: true
  dns-hijack:
    - any:53        # UDP DNS 劫持
    - tcp://any:53  # TCP DNS 劫持

防护原理

  • 劫持所有 53 端口的 DNS 流量(UDP 和 TCP)
  • 强制所有 DNS 查询经过 Mihomo 内核处理
  • 防止应用使用硬编码 DNS 服务器绕过代理
  • 与 Fake-IP 模式配合,实现零泄漏 DNS 解析

单元测试:新增 5 个测试用例覆盖各种配置场景:

  • 无 TUN 配置时自动创建 dns-hijack
  • 已有 dns-hijack 时补充缺失项
  • 非序列类型 dns-hijack 时替换为正确格式

安全设计

Zephyr 实现了一套多层纵深防御体系,覆盖网络请求安全、供应链安全、数据加密存储、文件系统防护等多个维度。其中 SSRF(服务端请求伪造)防护是网络请求安全的核心组件。

SSRF 防护链

针对 SSRF 攻击,Zephyr 实现了从 URL 解析到重定向跟踪的完整防护链。每次网络请求(包括重定向跳转)都经过严格的校验,最多跟踪 5 次重定向。以下流程图展示了完整的 SSRF 防护决策链:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[解析目标 URL] --> B{协议为<br/>HTTP/HTTPS?}
    B -->|否| ERR1[拒绝:<br/>仅允许 HTTP 和 HTTPS]
    B -->|是| C[提取主机名]

    C --> D{主机名为<br/>私有地址?}
    D -->|是| ERR2[拒绝:<br/>localhost / .local / .test 等]
    D -->|否| E[DNS 预解析]

    E --> F{解析结果为<br/>私有 IP?}
    F -->|是| ERR3[拒绝:<br/>RFC 1918 / 环回 / 链路本地]
    F -->|否| G[DNS Pinning<br/>固定解析结果]

    G --> H[发起 HTTP 请求]
    H --> I{收到重定向?}
    I -->|否| J[返回响应内容]
    I -->|是| K{重定向次数<br/>≤ 5?}

    K -->|否| ERR4[拒绝:<br/>重定向次数超过上限]
    K -->|是| L[验证重定向目标 URL]

    L --> M{重定向协议<br/>合法?}
    M -->|否| ERR5[拒绝:<br/>非法重定向协议]
    M -->|是| N{重定向主机名<br/>为私有地址?}
    N -->|是| ERR6[拒绝:<br/>重定向到私有主机]
    N -->|否| O[DNS 预解析重定向目标]

    O --> P{重定向 IP<br/>为私有?}
    P -->|是| ERR7[拒绝:<br/>重定向到私有 IP]
    P -->|否| H

    style A fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style E fill:#F59E0B,stroke:#D97706,color:#fff
    style G fill:#3B82F6,stroke:#2563EB,color:#fff
    style H fill:#10B981,stroke:#059669,color:#fff
    style J fill:#10B981,stroke:#059669,color:#fff
    style ERR1 fill:#991B1B,color:#fff
    style ERR2 fill:#991B1B,color:#fff
    style ERR3 fill:#991B1B,color:#fff
    style ERR4 fill:#991B1B,color:#fff
    style ERR5 fill:#991B1B,color:#fff
    style ERR6 fill:#991B1B,color:#fff
    style ERR7 fill:#991B1B,color:#fff
Loading

防护层说明

防护层 机制 说明
DNS 解析验证 预解析 + IP 校验 在发起请求前先解析域名,验证目标 IP
私有 IP 拦截 CIDR 匹配 拦截指向 RFC 1918 / 环回 / 链路本地地址的请求
重定向链校验 最多 5 次跟踪 跟踪 HTTP 重定向链,每跳均执行 IP 校验
协议白名单 HTTP/HTTPS only 仅允许 httphttps 协议
DNS Pinning reqwest::resolve 固定 IP 防止 DNS Rebinding(TOCTOU)攻击

私有 IP 检测范围

IPv4 检测范围:

范围 CIDR 说明
私有地址 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 RFC 1918 私有网络
环回地址 127.0.0.0/8 本机回环
链路本地 169.254.0.0/16 自动分配地址
广播地址 255.255.255.255 网络广播
未指定 0.0.0.0 通配绑定地址
文档用途地址 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 RFC 5737 文档与示例用途(TEST-NET-1/2/3)

IPv6 检测范围:

范围 CIDR 说明
环回地址 ::1 本机回环
未指定 :: 通配绑定
唯一本地地址 fc00::/7 IPv6 私有网络(ULA)
链路本地 fe80::/10 IPv6 链路本地

供应链安全

订阅下载环节实施多层校验,防止恶意配置注入:

校验项 策略 说明
SHA256 校验 哈希比对 验证下载文件的完整性
可信域名白名单 域名匹配 仅允许从已知可信域名下载内核更新(updater.rs TRUSTED_HOSTS),订阅下载使用 IP 级别 SSRF 防护
大小限制 10MB 超过限制的文件直接丢弃
路径遍历防护 5 轮 URL 解码 + 文件名净化 防止目录穿越攻击

数据加密存储

订阅元数据(metadata.json 中的订阅 URL、流量信息)使用 AES-256-GCM 加密存储:

参数 说明
加密算法 AES-256-GCM 认证加密,同时保证机密性与完整性
密钥派生 PBKDF2-HMAC-SHA256 从机器硬件指纹派生加密密钥
迭代次数 100,000 次 有效抵御暴力破解
密钥绑定 机器标识 密钥与当前设备绑定,防止元数据文件迁移泄露

⚠️ 注意:代理配置文件(.yaml)默认明文存储,但支持可选的机器绑定加密(v2.3.7+)。启用后,配置文件使用与订阅元数据相同的 AES-256-GCM 加密,密文以 djI6v2: 的 base64)前缀标识。run_config.yaml 始终明文。

文件权限控制

平台 权限设置 说明
Unix (macOS/Linux) 0600 仅文件所有者可读写
Windows DACL 通过 Windows 自由访问控制列表限制访问

命令限流

为防止命令被滥用或误操作,关键命令均设有频率限制:

命令 限流间隔 说明
start_core 3 秒 防止内核频繁启停
download_sub 5 秒 防止订阅下载过于频繁
exempt_uwp_apps 5 分钟 UWP 环回免除操作较重,需较长冷却

危险配置清除

加载外部配置时,自动清除以下危险字段,防止代码注入:

  • script / script-path -- 可能导致任意代码执行
  • provider 中的 path 字段 -- 可能导致路径遍历

配置热重载

Zephyr 的所有配置修改均支持热重载,无需重启 Mihomo 内核即可即时生效。配置更新通过 Tauri IPC 从前端传递到 Rust 后端,经过安全键保护、YAML 递归合并后写入文件,并通过 PATCH API 通知内核重新加载。

热重载流程

以下时序图展示了从用户在前端修改配置到最终生效的完整交互过程,涵盖 Tauri IPC 调用、安全键保护机制和 Mihomo API 通知:

%%{init: {'themeVariables': {'fontSize': '10px', 'actorTextColor': '#ccc', 'signalColor': '#666', 'labelTextColor': '#999', 'labelBoxBkgColor': '#333', 'labelBoxBorderColor': '#555', 'lineColor': '#666', 'actorBkg': '#333', 'actorBorder': '#555', 'actorTextColor': '#ccc', 'signalTextColor': '#ccc', 'noteBkgColor': '#333', 'noteTextColor': '#ccc', 'noteBorderColor': '#555'}}}%%
sequenceDiagram
    participant UI as 前端 UI
    participant IPC as Tauri IPC
    participant CM as config_manager<br/>(Rust 后端)
    participant FS as 文件系统
    participant API as Mihomo API<br/>(PATCH /configs)

    UI->>IPC: invoke("update_config", {patch})
    IPC->>CM: update_config(patch) 调用

    Note over CM: Step 1: JSON 转 YAML
    CM->>CM: serde_json::Value<br/>转为 serde_yaml::Value

    Note over CM: Step 2: 保存安全键
    CM->>CM: 备份 external-controller
    CM->>CM: 备份 secret

    Note over CM: Step 3: 递归合并
    CM->>CM: merge_yaml(existing, new, depth=0)
    Note over CM: 深度限制 50 层<br/>null 值表示删除键

    Note over CM: Step 4: 恢复安全键
    CM->>CM: 写回 external-controller
    CM->>CM: 写回 secret

    Note over CM: Step 5: 写入文件
    CM->>FS: 序列化为 YAML
    CM->>FS: 写入 run_config.yaml
    CM->>FS: 同步到订阅配置文件

    Note over CM: Step 6: PATCH 热重载
    CM->>API: PATCH /configs?force=true
    API-->>CM: 200 OK

    CM-->>IPC: Ok(())
    IPC-->>UI: resolve

    Note over UI: UI 刷新配置显示
Loading

递归合并算法

merge_yaml 函数实现了深度递归合并,支持嵌套结构的精确更新和键值删除:

// 伪代码:merge_yaml 递归合并实现
fn merge_yaml(base: &mut Value, overlay: &Value, depth: u32) {
    // 深度限制:防止栈溢出攻击
    if depth > 50 {
        return;
    }

    if let (Value::Mapping(base_map), Value::Mapping(overlay_map)) = (base, overlay) {
        for (key, value) in overlay_map {
            if value.is_null() {
                // null 值表示删除该键
                base_map.remove(key);
            } else if let Some(existing) = base_map.get_mut(key) {
                // 递归合并嵌套结构
                merge_yaml(existing, value, depth + 1);
            } else {
                // 新增键值对
                base_map.insert(key.clone(), value.clone());
            }
        }
    } else {
        // 非映射类型:直接覆盖
        *base = overlay.clone();
    }
}

安全键保护机制

配置更新过程中,external-controllersecret 等安全关键字段会被临时备份并在合并后恢复,防止用户配置意外覆盖安全设置:

安全键 保护原因 恢复值
external-controller 防止 API 绑定到非 loopback 地址 127.0.0.1:{port}
secret 防止移除 API 认证 随机生成的 32 个字母数字字符(Alphanumeric);如果原配置中无 secret,则不会自动添加新 secret

PATCH API 调用

PATCH /configs?force=true
Content-Type: application/json

{
  "dns": {
    "enable": true,
    "fake-ip": true,
    "nameserver": ["8.8.8.8", "1.1.1.1"]
  }
}

force=true 参数确保配置被强制重载,绕过内部缓存。


快速延迟测试

Zephyr 支持对代理节点进行快速延迟测试,帮助用户评估各节点的连接质量。

测试机制

通过 Mihomo RESTful API 对指定节点发起延迟探测:

GET /proxies/{proxy_name}/delay?url=http://www.gstatic.com/generate_204&timeout=5000
参数 说明
测试 URL http://www.gstatic.com/generate_204 Google 204 端点,轻量可靠
超时时间 5000ms 单次测试超时阈值
并发策略 并行请求 多节点同时测试,缩短总耗时

以下流程图展示了并发延迟测试的完整调度过程,从用户触发到结果展示的全链路:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[用户触发延迟测试] --> B[UI 进入乐观加载状态]
    B --> C[显示加载动画<br/>所有节点标签置灰]

    C --> D[收集待测节点列表]
    D --> E[并行发起 API 调用]
    E --> F1[GET /proxies/node_1/delay]
    E --> F2[GET /proxies/node_2/delay]
    E --> F3[GET /proxies/node_N/delay]

    F1 --> G1[返回延迟结果]
    F2 --> G2[返回延迟结果]
    F3 --> G3[返回延迟结果]

    G1 --> H[异步汇总结果]
    G2 --> H
    G3 --> H

    H --> I[逐个更新延迟标签]
    I --> J[颜色编码刷新<br/>绿色 < 200ms / 黄色 < 500ms / 红色 > 500ms]
    J --> K{用户已启用排序?}
    K -->|是| L[按延迟重新排序节点]
    K -->|否| M[保持当前顺序]
    L --> N[测试完成]
    M --> N

    style A fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style B fill:#3B82F6,stroke:#2563EB,color:#fff
    style E fill:#3B82F6,stroke:#2563EB,color:#fff
    style H fill:#F59E0B,stroke:#D97706,color:#fff
    style J fill:#10B981,stroke:#059669,color:#fff
    style N fill:#10B981,stroke:#059669,color:#fff
Loading

乐观 UI

延迟测试采用乐观 UI 模式 -- 用户触发测试后,界面立即进入"测试中"状态并展示加载动画,无需等待首个结果返回。这种设计消除了请求延迟带来的感知等待时间。

结果展示

测试完成后,延迟数值实时更新至节点卡片,并同步触发颜色编码刷新(绿/黄/红)。


多种运行模式

Zephyr 提供三种代理运行模式,通过三段式滑块控件一键切换,适配不同的使用场景。

模式说明

模式 说明 适用场景
Rule(规则分流) 根据 Mihomo 配置中的规则集进行智能分流 日常使用,推荐模式
Global(全局代理) 所有流量均通过代理服务器转发 需要全部流量走代理的场景
Direct(直连) 所有流量直接发送,不经过代理 排查问题、访问本地服务

切换方式

通过 Mihomo RESTful API 实时切换:

PATCH /configs
Content-Type: application/json

{
  "mode": "rule"
}

UI 交互

三段式滑块控件带有平滑动画过渡效果,当前激活模式高亮显示。切换操作即时生效,无需重启内核。


跨平台支持

Zephyr 基于 Tauri v2 的跨平台能力,在 Windows、macOS 和 Linux 上提供一致的功能体验,同时针对各平台特性进行深度适配。

安装包格式

平台 格式 说明
Windows NSIS (.exe) + MSI (.msi) NSIS 为主推格式,MSI 适合企业部署
macOS DMG Apple Silicon + Intel 通用二进制(Universal Binary)
Linux DEB + AppImage + RPM 覆盖 Debian/Ubuntu、Fedora/RHEL 及通用发行版

Windows 特性

  • 系统代理设置:通过注册表原子写入配置系统代理,确保写入的原子性与一致性
  • UWP 环回免除:自动为 UWP 应用添加环回免除规则,使 UWP 应用也能走代理
    • 5 分钟限流防止频繁操作
    • 操作前弹出用户确认对话框

以下流程图展示了 UWP 环回豁免的完整操作流程,从用户触发到规则生效的全链路:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[用户点击 UWP 环回豁免] --> B{限流检查<br/>距上次操作 > 5 分钟?}
    B -->|否| C[提示操作过于频繁<br/>请稍后再试]
    B -->|是| D[弹出确认对话框<br/>提示将执行 PowerShell 命令]

    D --> E{用户确认?}
    E -->|取消| F[操作取消]
    E -->|确认| G[执行 PowerShell<br/>CheckNetIsolation 命令]

    G --> H[自动豁免所有非框架 UWP 应用<br/>Get-AppxPackage &#124; IsFramework -eq $false]

    H --> K[逐个添加环回豁免规则<br/>CheckNetIsolation LoopbackExempt -a]
    K --> L{所有规则添加成功?}
    L -->|否| M[报告部分失败的应用]
    L -->|是| N[全部豁免规则添加成功]

    M --> O[刷新系统代理设置]
    N --> O
    O --> P[更新限流时间戳]
    P --> Q[操作完成<br/>UWP 应用可正常走代理]

    style A fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style B fill:#F59E0B,stroke:#D97706,color:#fff
    style G fill:#3B82F6,stroke:#2563EB,color:#fff
    style K fill:#3B82F6,stroke:#2563EB,color:#fff
    style Q fill:#10B981,stroke:#059669,color:#fff
    style C fill:#6B7280,stroke:#4B5563,color:#fff
    style F fill:#6B7280,stroke:#4B5563,color:#fff
Loading

macOS 特性

  • 通用二进制:单一代形同时支持 Apple Silicon (ARM64) 和 Intel (x86_64) 架构
  • TUN 提权:通过 osascript 调用 AppleScript 弹出系统授权对话框

Linux 特性

  • 桌面环境检测:自动识别当前桌面环境并调用对应的代理配置工具:
桌面环境 配置工具 命令示例
GNOME gsettings gsettings set org.gnome.system.proxy mode 'manual'
KDE Plasma kwriteconfig6 kwriteconfig6 --file kioslaverc --group "Proxy Settings" --key "ProxyType" 1
XFCE xfconf-query xfconf-query -c xfce4-proxy -p /mode -s manual
  • GPU 加速:启用 WebKitGTK GPU compositing,支持 Wayland 会话下的硬件加速渲染
  • AppImage 打包:使用 linuxdeploy-plugin-gtk 脚本打包,提升 GTK/WebKitGTK 兼容性
  • 自定义标题栏:与其他平台统一使用无边框窗口 + 自定义标题栏(关闭/最小化/最大化按钮)
  • AUR 自动更新:Release 发布时自动更新 Arch User Repository 包(zephyr-clash-bin

高级功能

自定义规则编辑器

Zephyr 内置了可视化的自定义规则编辑器,无需手动编辑 YAML 即可管理代理规则。

  • 14 种规则类型:DOMAIN、DOMAIN-SUFFIX、DOMAIN-KEYWORD、IP-CIDR、IP-CIDR6、GEOIP、GEOSITE、PROCESS-NAME、MATCH、SRC-IP-CIDR、SRC-PORT、DST-PORT、RULE-SET、USER-AGENT
  • Shadowrocket 规则导入:支持从 Shadowrocket 格式的规则列表一键导入
  • 可视化编辑:通过表单控件添加、编辑、删除、排序规则
  • 热重载:规则修改后通过 PATCH /configs?force=true 实时生效,无需重启内核

端口转发

支持创建 TCP/UDP 端口转发隧道,将本地端口流量转发到指定代理节点:

参数 说明
协议 TCP / UDP
监听地址 本地绑定的 IP 地址
监听端口 本地监听端口号
目标地址 转发目标的 IP 或域名
目标端口 转发目标的端口号
代理节点 使用的代理节点名称

DNS 覆写

提供独立的 DNS 配置面板,支持覆盖 Mihomo 内核的默认 DNS 设置:

  • Fake-IP 模式:启用后使用 Fake-IP 加速 DNS 解析
  • 自定义 DNS 服务器:可配置独立的 nameserver 列表
  • DNS 规则:支持按域名指定解析服务器

动态配置引擎

Zephyr 实现了一套递归渲染的动态配置引擎,用于管理 Mihomo 内核的复杂配置结构:

  • 递归渲染:根据配置的嵌套结构自动生成对应的编辑控件
  • 类型感知:根据值的类型(字符串、数字、布尔、数组、对象)自动选择合适的输入控件
  • 实时 PATCH:配置修改通过 HTTP PATCH 请求实时推送至内核
PATCH /configs?force=true
Content-Type: application/json

{
  "dns": {
    "enable": true,
    "fake-ip": true,
    "nameserver": ["8.8.8.8", "1.1.1.1"]
  }
}

轻量模式

关闭窗口时销毁 WebView 实例以释放内存,仅保留系统托盘图标。点击托盘图标恢复窗口。

  • 设置入口:Settings → General → Lightweight Mode
  • 联动逻辑:关闭"最小化到托盘"时自动禁用轻量模式
  • 内存节省:关闭后 WebView 内存完全释放,适合低内存设备

Minisign 签名验证

客户端更新下载完成后,使用 Minisign Ed25519 签名验证安装包完整性。

  • 签名公钥硬编码在 minisign_verify.rs
  • 下载 .minisig 签名文件(限制 4KB)
  • 验证失败则删除安装包并报错,防止篡改
  • CI 签名流程:GitHub Actions 中使用 Minisign 私钥签名 release 产物

日志持久化

后端日志(Mihomo 内核输出 + 结构化事件)支持持久化存储到磁盘。

  • 每日轮转:按日期自动分割日志文件
  • 严重级别过滤:可配置最低记录级别(Info/Warn/Error)
  • 导出功能:UI 中一键导出日志文件
  • Mihomo 日志:内核 stdout/stderr 增量读取并写入持久化文件
  • 新增 6 个日志管理 IPC 命令

zephyr-core Crate

共享业务逻辑提取为独立的 core/ crate(rlib),支持可选的 UniFFI 绑定。

  • 目的:为未来移动端(iOS/Android)复用核心逻辑做准备
  • 包含:配置合并/清洗、订阅下载/SSRF 防护、RateLimiter、状态管理等
  • UniFFI:通过 feature flag 启用,生成 Swift/Kotlin 绑定
  • monorepo 结构标准化apps/desktop + packages/shared + packages/tokens + crates/core

DashMap 限流器

RateLimiter 从 Arc<Mutex<HashMap>> 重构为 DashMap<String, Arc<Mutex<Bucket>>>

  • 不同 key 的限流检查完全无锁竞争
  • 修复 Bucket::checkchecked_sub 溢出 bug
  • 修复 retry_after 计算中 checked_add + checked_duration_since 溢出

mihomo -t 配置预检

启动核心前运行 mihomo -t -f 验证配置有效性,防止无效配置导致崩溃重启循环。

孤儿进程防护

防止 mihomo 在 Zephyr 意外退出后成为孤儿进程。

  • Windows:使用 Job Object(JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE),Tauri 进程退出时(包括从任务管理器强制结束)自动终止 Job 内所有进程
  • LinuxPR_SET_PDEATHSIG 使 mihomo 在父进程退出时收到 SIGTERM;独立 process group 防止误杀 Tauri 主进程
  • macOS:独立 process group + 父进程 PID 监控

系统代理所有权守卫

防止外部程序篡改 Zephyr 设置的系统代理。

  • 启用系统代理时写入 .sys-proxy-ownership 标记文件(含代理地址)
  • 每 10 秒检测:若标记存在但系统代理被外部禁用,自动重新启用
  • 正常退出时删除标记;崩溃后标记持久化,下次启动自动恢复代理
  • 新增 restore_sys_proxy IPC 命令

代理环境变量复制

一键复制当前代理的环境变量到剪贴板,方便在终端中使用。

  • 支持 5 种 Shell 格式:Bash/Zsh、Fish、CMD、PowerShell、Nushell
  • 设置页面和托盘菜单均可触发
  • 自动读取当前 mixed-port / socks-port
  • Linux 托盘剪贴板:WebKit2GTK 要求窗口焦点才能使用 navigator.clipboard,托盘菜单点击时无法保证焦点。剪贴板写入通过 Rust arboard crate(Linux 启用 wayland-data-control feature 支持 Wayland),spawn_blocking 避免阻塞 UI 线程

每订阅 User-Agent 覆盖

每个订阅可单独设置 User-Agent,覆盖全局 subscription_user_agent

  • ConfigMetadata 新增 user_agent 字段
  • 新增 update_subscription_ua IPC 命令
  • 自动更新调度器(subscription_scheduler.rs)在后台更新时也使用 per-sub UA 优先于全局 UA
  • 订阅编辑面板新增 UA 下拉选择(Use Global / Clash Verge Rev / mihomo-party / Flclash / Shadowrocket)
  • UA 值长度限制 512 字符,仅允许 ASCII 可见字符

窗口隐藏时暂停渲染

窗口隐藏或最小化时暂停连接列表轮询和流量图表渲染,可见时自动恢复。

配置备份与恢复

事务性配置导出/导入系统,支持完整配置迁移。

  • 导出:将 settings.jsonrun_config.yaml、所有 profile YAML 打包为 ZIP,内含 manifest.json(SHA-256 校验 + 版本号 + 时间戳)
  • 导入:三阶段事务流程——验证(manifest + 校验和 + zip bomb 检测 + 路径遍历防护)→ 暂存 → 原子提交(失败自动回滚)
  • 安全:最大 200MB 解压、200:1 压缩比限制(与 updater 共用逻辑)、拒绝 .. / 绝对路径 / 符号链接
  • 新增 2 个 IPC 命令:export_backupimport_backup

WebView 崩溃恢复

Windows 平台 WebView2 进程崩溃时自动恢复。

  • 监听 ProcessFailed 事件(browser/render/GPU/utility 进程)
  • Render 进程崩溃:rate-limited reload(),5 分钟内 3 次则升级为完整窗口重建
  • Browser 进程退出:立即完整窗口重建
  • 崩溃恢复通过 AtomicI64 心跳检测实现(无锁、纳秒级开销)
  • 新增 webview_recovery.rs 模块(288 行)

系统休眠恢复

系统从睡眠/休眠唤醒后自动检查 mihomo 核心健康状态。

  • 监听 RunEvent::Resumed 事件
  • 3 次 TCP 探测核心 API(每次 2s 超时,间隔 1s)
  • 探测失败则使用 last-known config + custom args 重启核心
  • 通过 AtomicBool 防止并发 resume handler 竞争
  • 新增 3 个错误码:6019-6021

静默启动

启动时主窗口保持隐藏,仅显示托盘图标。

  • 设置入口:Settings → General → Silent Start
  • 点击托盘图标显示窗口
  • 与轻量模式互补(静默启动控制启动时可见性,轻量模式控制关闭时内存)

Settings Schema 迁移系统

settings.json 新增 schema_version 字段,支持自动迁移。

  • 版本化 schema:CURRENT_SCHEMA_VERSION = 1
  • v0→v1 迁移:last_proxy_selection 旧格式(纯字符串)自动包装为 v2 JSON 格式
  • 损坏文件处理:JSON 解析失败时备份为 settings.corrupt.<timestamp>.json,写入默认值
  • 迁移失败处理:备份原文件为 .pre-migration.<timestamp>,重置为默认值

ARM64 原生支持

全平台 ARM64 架构支持,覆盖构建、打包、发布全链路。

  • Linux ARM64 原生构建:新增 ubuntu-22.04-arm GitHub Actions 原生 runner,编译 deb/AppImage/portable
  • Windows ARM64 交叉编译windows-latest + --target aarch64-pc-windows-msvc,生成 NSIS/MSI 安装包
  • mihomo 二进制适配download-core.sh 重构为接收 arch 第 4 参数,三平台分别选择 mihomo-{os}-arm64 资产
  • CI 安全检查:security.yml 新增 Job 12「Windows Build Check」,在 windows-latest runner 上 cargo check --target aarch64-pc-windows-msvc,检查 #[cfg(target_os = "windows")] 代码
  • deny.toml 更新:新增 RUSTSEC 公告过滤

CPU v3 指令集检测

运行时检测 x86_64-v3 指令集支持(AVX2、BMI1/2、FMA),选择最优 mihomo 二进制。

  • 三分支 asset 选择:v3 优化版 → 兼容回退版 → 通用版
  • 检测失败时自动回退到兼容版
  • ARM64 架构直接使用通用版(无 v3 变体)

catch_unwind 守卫

关键函数(start_corewrite_config_fileupdate_core)包装 AssertUnwindSafe + catch_unwind

  • Cargo.toml panic = "abort"panic = "unwind"(仅在 catch_unwind 路径)
  • panic 转换为用户友好的错误字符串,防止整个应用崩溃

双源竞速更新检查

同时请求 GitHub REST API 和 Atom Feed,首个成功响应获胜。

  • tokio::select! 竞速,失败源被 abort
  • 版本一致性检查(两源返回不同版本时警告)
  • Atom Feed 解析作为 REST API 限流时的回退

窗口状态持久化

窗口位置、大小、最大化状态跨会话持久化。

  • 使用 tauri-plugin-window-state
  • 持久化 SIZE + POSITION + MAXIMIZED(不包括 FULLSCREEN 和 DECORATIONS)
  • 窗口创建时先 visible(false)restore_state()show(),避免位置闪烁

Design Token 4 级 Radius 系统

Radius 从 sm/md/lg 三级收敛为 4 级语义化系统。

级别 Token 用途
Control --radius-control 8px 按钮、输入框、标签
Surface --radius-surface 12px 卡片、面板
Overlay --radius-overlay 16px 模态框、下拉菜单
Full --radius-full 9999px 圆形元素

同时消除 15+ 文件中的硬编码颜色(bg-indigo-600text-emerald-400 等),统一使用 semantic token 类名(bg-dangertext-success 等)。新增 surface 三层定义(page / raised / elevated / input / overlay)。

UnoCSS presetWind4 迁移

前端 CSS 引擎从 Tailwind CSS v4 全面迁移到 UnoCSS presetWind4。

  • 替换 @tailwindcss/cli + tailwindcssunocss + @unocss/preset-wind4
  • 新增 uno.config.js(presetWind4,dark: class mode,preflights: reset)
  • @theme block → :root CSS 自定义属性
  • 新增 copy-tokens 脚本(Style Dictionary CSS → Tauri sandbox)
  • 删除 tailwind.csstailwind.config.js

导航栏微动效系统

侧边栏导航图标新增完整的微动效系统,每个页面有独特的动画语言。

  • Draw-On 引擎pathLength=1 归一化 + stroke-dashoffset 描绘,零漂移
  • 逐页动画:Home(屋顶错峰落笔)、Proxies(箭杆→箭头弹性释放)、Subscriptions(折角翻开→对勾描绘)、Connections(节点弹入+数据流)、Rule Library(一笔勾勒)、Logs(提示符+光标闪烁)、Settings(表冠上弦旋转)
  • 状态管理is-active(弹出+发光)/ is-leaving(呼气收缩),WeakMap timeout 防竞态
  • 所有动画在 prefers-reduced-motion: reduce 时禁用

3D 悬停效果优化

代理卡片 3D 透视悬停效果重构,消除布局抖动。

  • mouseenter 时缓存 getBoundingClientRect(),避免 RAF 内每帧强制 reflow
  • 使用 pageX / pageY 代替 clientX / clientY,免疫滚动偏移
  • WeakMap 管理 leave timeout

组件系统

  • Button State Matrix:disabled(opacity 0.4 + cursor not-allowed)、loading(aria-busy="true" + spinner 伪元素)
  • Form Control System:收敛 input-common / input-modal / input-mono / textarea-common 为统一的 .form-control,支持 sm / md / lg 尺寸变体 + mono 字体变体
  • Status Dot:在线 / 离线 / 错误 / 警告状态点
  • Latency Badge:快 / 中 / 慢延迟徽章(颜色编码)
  • Danger Zone:危险操作区域样式
  • Status Ring:圆形进度指示器(SVG stroke-dashoffset 动画),用于 Geo 数据库更新、核心更新等操作

无障碍增强

  • Disabled 状态统一:扩展到 :is(button, input, select, textarea, .btn, .form-control, .select-common, .dropdown-item, [role="button"]):is(:disabled, [disabled], [aria-disabled="true"])
  • Hover 禁用保护:所有 btn-*:hover 添加 :not(:disabled, [disabled], [aria-disabled="true"], .active)
  • Collapsible ARIArole="button" + tabindex="0" + aria-expanded + aria-controls + keyboard(Enter/Space)
  • Dropdown disabled 守卫:hover / click / option click 全部检查 disabled
  • Proxies reduced-motion:检测 prefers-reduced-motion,滚动文本添加 tabindex + role + aria-label
  • Focus ring adapter--zephyr-focus-ring 通过 color-mix 实现主题感知
  • Transition tokens--zephyr-time-standard / --zephyr-easing-ease-out 替代硬编码值

Override 脚本失败指示

Override 脚本执行失败时,状态指示器从绿色变为红色,显示"失败"状态文本。

开机自启

基于 Tauri autostart 插件实现开机自动启动,支持 Windows、macOS 和 Linux 三平台。

便携版模式

Zephyr 支持便携版(Portable)运行模式,无需安装,解压即用,适合 U 盘携带或多设备使用。

工作原理

  • 程序启动时检测是否存在 .portable 标记文件
  • 如果存在,所有数据(配置、订阅、核心、缓存)存储在程序所在目录
  • 如果不存在,使用系统标准目录(Windows AppData / Linux ~/.config / macOS ~/Library)

目录结构(便携模式):

Zephyr/
├── Zephyr.exe / Zephyr-*.AppImage
├── .portable          ← 便携模式标记文件
├── core/              ← mihomo 核心和 Geo 数据
├── profiles/          ← 订阅配置文件
├── prism/             ← Prism 规则数据
├── settings.json      ← 应用设置
└── .machine_key       ← 加密密钥

功能对比

特性 便携版 安装版
安装步骤 解压即用 需要安装程序
数据位置 程序目录 系统目录
开机自启 ❌ 不支持 ✅ 支持
客户端更新 ❌ 不支持(需手动下载) ✅ 支持
系统代理 ✅ 支持 ✅ 支持
TUN 模式 ✅ 支持 ✅ 支持
多实例 ✅ 支持 ❌ 不支持

CI 构建产物

  • Windows: Zephyr-windows-portable.zip
  • Linux: Zephyr-linux-portable.tar.gz(AppImage + 数据目录)

UI 缩放

Zephyr 提供用户可控的界面缩放功能,支持 0.5x - 2.0x 范围调节,适配不同分辨率和视力需求。

功能特性

  • 设置入口:Settings → Appearance → UI Scale
  • 调节范围:0.5x - 2.0x,步进 0.1x
  • 持久化:缩放偏好保存到 settings.json,下次启动自动应用
  • 实现方式:CSS transform: scale() 应用到应用根元素,确保所有 UI 组件统一缩放
  • i18n:所有支持语言的翻译已添加
  • Dropdown 修复:下拉菜单和右键菜单在缩放后正确计算位置(getBoundingClientRect 返回视觉坐标,需除以 scale)

技术实现

// 应用缩放
const scale = settings.ui_scale || 1.0;
document.documentElement.style.transform = `scale(${scale})`;
document.documentElement.style.transformOrigin = 'top left';
document.documentElement.style.width = `${100 / scale}%`;
document.documentElement.style.height = `${100 / scale}%`;

// Dropdown 定位修复(transform 容器内 fixed 定位需除以 scale)
const rect = trigger.getBoundingClientRect();
const uiScale = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--ui-scale')) || 1;
menu.style.left = `${rect.left / uiScale}px`;
menu.style.top = `${(rect.bottom + 6) / uiScale}px`;

隐藏超时节点

代理列表支持自动隐藏不可用的节点:

  • 设置入口:Settings → Proxy → Hide Timeout
  • 过滤逻辑:延迟 ≤ 0 或 ≥ 999999 视为超时,从列表中隐藏
  • 保留当前节点:当前选中的代理节点始终显示,即使超时
  • 延迟测试联动:单节点测试超时时立即移除;批量测试后重新渲染
  • 持久化:设置保存到 settings.jsonhide_timeout_nodes 字段

端口配置面板

设置页面新增端口配置模态框,支持可视化编辑 Mihomo 监听端口:

管理的端口

端口 说明
mixed-port 混合端口(HTTP + SOCKS5)
socks-port SOCKS5 代理端口
redir-port 透明代理端口
tproxy-port TProxy 代理端口

验证规则

  • 范围:0 - 65535(0 表示禁用)
  • 全禁用阻止mixed-portsocks-port 不能同时为 0(至少一个代理端口必须启用)
  • 重复检查:所有启用的端口(> 0)不能重复
  • 格式验证:仅接受纯数字(正则 /^\d+$/

实现细节

  • 模态框带焦点陷阱和 Escape 关闭
  • 异步保存期间禁用保存按钮
  • 保存时自动将 legacy port 设为 0(Mihomo 推荐使用 mixed-port

网络优化

Zephyr 实现了三层网络性能优化体系,遵循 Google Cloud TCP 最佳实践文档。

第一层:Mihomo 配置默认值(跨平台,无需特权)

参数 默认值 说明
tcp-concurrent true 并行 TCP 连接所有解析 IP,选最快
keep-alive-interval 30 防止中间设备断开空闲连接
keep-alive-idle 600 保持持久连接,减少 TLS 握手
find-process-mode always TUN 模式下精确进程路由
profile.store-fake-ip true 持久化 fake-ip 映射,重启免 DNS 重解析
default-nameserver 223.5.5.5, 119.29.29.29 避免 DoH 解析循环依赖

所有默认值尊重订阅/用户已有配置(contains_key 检查)。

第二层:OS TCP 调优(TUN 授权时应用)

平台 参数 说明
Linux tcp_slow_start_after_idle=0 禁用空闲后慢启动
Linux tcp_rto_min_us=5000 最小 RTO 5ms
Linux tcp_fastopen=3 TCP Fast Open(客户端+服务端)
Linux tcp_ecn=1 显式拥塞通知
Linux hystart_detect=2 禁用不可靠的 ACK train 检测
Linux rmem_max/wmem_max=4MB 提升高 RTT 路径吞吐
Linux tcp_rmem: 4K/256K/16MB 自动调优接收缓冲区
Linux tcp_wmem: 4K/256K/32MB 自动调优发送缓冲区
macOS tcp.fastopen=3 TCP Fast Open
macOS tcp.ecn.enable=1 显式拥塞通知
Windows autotuninglevel=normal TCP 窗口自动调优
Windows initialRto=300 初始 RTO 300ms
Windows fastopen=enabled TCP Fast Open

第三层:DNS 优化(跨平台)

  • fake-ip-filter 从 2 条扩展到 13 条(覆盖 captive portal、游戏主机、STUN、LAN 发现等)
  • 新增 default-nameserver 避免 DoH 解析循环依赖

独立网络优化 UI

设置页面新增 Network Optimization 行,提供手动 apply/revert 控制:

  • Apply:显示变更详情 + 警告,支持自动启动选项
  • Revert:从备份恢复优化前的系统值(非硬编码默认值)
  • Status:绿色圆点 = 已应用,灰色 = 未应用
  • 持久化:Linux 写入 /etc/sysctl.d/(重启保留);macOS 重启丢失;Windows 系统级持久

内核更新机制

Zephyr 内置了 Mihomo 内核版本检测与更新功能。当用户开启 auto_update 设置时,应用启动 5 秒后自动检查新版本;用户也可随时通过"检查更新"按钮手动触发。检测到新版本后,会弹出确认对话框,经用户确认后才执行下载与安装

注意:不存在自动下载行为,所有更新均需用户手动确认。

以下流程图展示了完整的更新链路(ui.js:performCoreUpdate + updater.rs:update_core):

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[触发更新检查<br/>自动 5s 后 / 手动点击] --> B[调用 get_latest_version]
    B --> C{网络请求成功?}
    C -->|否| D[提示检查失败]
    C -->|是| E[获取最新版本号]

    E --> F{版本一致?}
    F -->|是| G[提示已是最新版本]
    F -->|否| H[弹出确认对话框<br/>showConfirmModal]

    H --> I{用户确认?}
    I -->|否| J[取消更新]
    I -->|是| K[下载新版本二进制文件]

    K --> L{下载成功?}
    L -->|否| M[提示下载失败]
    L -->|是| N[计算 SHA256 校验值]

    N --> O{校验通过?}
    O -->|否| P[删除损坏文件<br/>提示校验失败]
    O -->|是| Q[解压二进制文件]

    Q --> R[停止当前 Mihomo 进程]
    R --> S[替换旧版二进制文件]
    S --> T{替换成功?}
    T -->|否| U[重试替换<br/>最多 5 次] --> V{重试成功?}
    V -->|否| W[提示更新失败]
    V -->|是| X
    T -->|是| X[启动新版 Mihomo 内核]

    X --> Y{内核健康检查通过?}
    Y -->|是| Z[更新 UI 显示新版本号<br/>提示更新成功]
    Y -->|否| W

    style A fill:#8B5CF6,stroke:#7C3AED,color:#fff
    style H fill:#F59E0B,stroke:#D97706,color:#fff
    style K fill:#3B82F6,stroke:#2563EB,color:#fff
    style N fill:#F59E0B,stroke:#D97706,color:#fff
    style R fill:#EF4444,stroke:#DC2626,color:#fff
    style X fill:#10B981,stroke:#059669,color:#fff
    style Z fill:#10B981,stroke:#059669,color:#fff
    style D fill:#6B7280,stroke:#4B5563,color:#fff
    style G fill:#6B7280,stroke:#4B5563,color:#fff
    style J fill:#6B7280,stroke:#4B5563,color:#fff
    style M fill:#6B7280,stroke:#4B5563,color:#fff
    style P fill:#6B7280,stroke:#4B5563,color:#fff
    style W fill:#6B7280,stroke:#4B5563,color:#fff
Loading

连接监控

Zephyr 内置了实时连接监控页面,提供对 Mihomo 内核所有活跃连接的细粒度可视化管理。

数据采集架构

连接数据通过轮询 Mihomo RESTful API 的 /connections 端点获取,采用 delta 累积追踪算法精确计算每个连接的实时速率和累计流量:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'sequence': {'messageAlign': 'center'}}}%%
sequenceDiagram
    participant Timer as 轮询定时器<br/>(2s 间隔)
    participant API as api.js<br/>getConnections()
    participant Mihomo as Mihomo Core<br/>/connections
    participant Acc as 累积器<br/>(connAccumulators)
    participant UI as connections.js<br/>渲染引擎

    Timer->>API: 页面可见时触发
    API->>Mihomo: GET /connections<br/>(Bearer secret)
    Mihomo-->>API: 连接列表 JSON<br/>(download/upload 为累计值)
    API-->>Acc: Phase 1: 检测死亡连接<br/>→ 归档到 closedConnections
    Acc-->>Acc: Phase 2: delta 计算<br/>dlDelta = max(0, curDl - prevDl)<br/>speed = delta / deltaTime
    Acc-->>UI: Phase 3: 更新统计栏<br/>+ 渲染连接列表
    Note over UI: 详情面板打开时<br/>仅更新数值 span<br/>不重建 DOM
Loading

Delta 累积追踪

Mihomo API 返回的 download / upload 字段是连接建立以来的累计字节数,而非瞬时速率。Zephyr 通过以下算法精确追踪:

// 每次轮询时更新累积器
const dt = (now - acc.prevTs) / 1000;  // 距上次轮询的秒数
const dlDelta = Math.max(0, curDl - acc.prevDl);  // 正向 delta 防护
acc.dl += dlDelta;          // 累积总下载量
acc.dlSpeed = dlDelta / dt; // 瞬时下载速率 (bytes/sec)
acc.prevDl = curDl;         // 记录本次基线
acc.prevTs = now;
  • 正向 delta 防护Math.max(0, ...) 防御 Mihomo 计数器重置或 API 异常导致的负值
  • 边界保护dt > 0.05 防止轮询过快导致速率计算失真
  • 死亡连接归档:当连接从 API 响应中消失时,自动归档到关闭历史(上限 200 条)

功能特性

功能 说明
实时轮询 2 秒间隔轮询,仅页面可见时触发,离开自动暂停
Active / Closed 标签 活跃连接与关闭历史分页展示,一键切换
搜索过滤 支持按主机名、目标 IP、端口、规则、链路、进程名搜索
多列排序 7 列可排序(Host / Rule / Chains / ↓Speed / ↓Total / ↑Speed / ↑Total),三态循环(降序 → 升序 → 关闭)
连接详情面板 点击连接行弹出详情浮层,显示完整元数据(进程、类型、来源、网络、持续时间)
详情面板实时刷新 活跃连接的详情面板在每次轮询时仅更新数值 span,不重建 DOM
批量关闭 一键关闭所有活跃连接
清除历史 一键清空关闭连接历史记录
统计栏 顶部 4 格统计:总连接数 / 总下载量 / 总上传量 / 活跃连接数

XSS 防护

连接监控页面中所有来自 Mihomo API 的动态数据均通过 escapeHtml() 函数转义后插入 DOM,包括连接行、详情面板、搜索过滤等场景。escapeHtml() 采用 textContent → innerHTML 模式,并内置 LRU 缓存(上限 500 条)优化高频轮询场景下的性能。


日志查看

Zephyr 内置了 Mihomo 内核日志实时查看页面,支持日志流式读取、搜索过滤和自动滚动。

数据流架构

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'sequence': {'messageAlign': 'center'}}}%%
sequenceDiagram
    participant Timer as 轮询定时器<br/>(1s 间隔)
    participant API as api.js<br/>readCoreLog()
    participant Rust as Rust 后端<br/>read_core_log
    participant File as 日志文件<br/>(mihomo-*.log)
    participant UI as logs.js<br/>渲染引擎

    Timer->>API: 页面可见时触发
    API->>Rust: invoke("read_core_log", offset, limit)
    Rust->>File: seek(offset) + read(500行)
    File-->>Rust: 日志文本行
    Rust-->>API: { lines, rotated, offset }
    API-->>UI: Phase 1: rotated? → 重置缓冲区
    UI-->>UI: Phase 2: 追加新行到环形缓冲区<br/>(上限 2000 行)
    UI-->>UI: Phase 3: 渲染到 DOM<br/>(仅增量更新)
Loading

功能特性

功能 说明
实时轮询 1 秒间隔轮询,仅页面可见时触发,离开自动暂停
增量读取 通过 offset 参数实现增量读取,避免重复传输已读日志
日志轮转检测 offset > file_size 时自动检测日志轮转,重置缓冲区
搜索过滤 支持实时搜索高亮,匹配文本用 <mark> 标签标记
自动滚动 默认自动滚动到底部,用户手动上滚时暂停自动滚动
环形缓冲区 内存中最多保留 2000 行,超限自动截断旧日志
懒加载 通过动态 import() 按需加载,离开页面时 destroyLogsPage() 清理资源
行数限制 后端硬性上限 2000 行/次请求,防止内存溢出

安全设计

  • 无路径遍历风险:日志路径由后端 MihomoState.last_log_path 自动生成(系统临时目录 + 固定前缀),不接受任何用户路径输入
  • XSS 防护:日志文本通过独立的轻量级 escapeHtml 函数转义(字符串映射表,零 DOM 分配),搜索高亮的 <mark> 标签基于已转义文本插入
  • 信息隔离:日志仅在本地 Tauri 窗口中显示,不写入外部存储、不发送到远程服务器

无障碍访问

Zephyr 实现了完整的键盘导航和屏幕阅读器支持,确保所有核心功能无需鼠标即可操作。

功能特性

功能 说明
Focus Trap 模态框内 Tab 循环,焦点不会泄漏到背景内容;Escape 键关闭模态框
Roving Tabindex 代理列表支持 Arrow keys 导航 + Enter/Space 激活,模拟原生 listbox 行为
ARIA 属性 role="listbox" / role="option" / role="dialog" / role="status" / aria-live="polite" / aria-modal / aria-selected
页面可见性守卫 页面不可见时自动暂停轮询(连接监控、日志)和动画(3D 效果),可见时恢复
排序标签同步 切换语言后排序标签自动更新为对应语言的文本

实现模块

模块 文件 行数 职责
focus-trap.js apps/desktop/src/utils/ ~115 行 模态框焦点陷阱,Tab 循环 + Escape 关闭
roving-tabindex.js apps/desktop/src/utils/ ~221 行 代理列表键盘导航,Arrow keys + Enter 激活
page-visibility.js apps/desktop/src/utils/ ~135 行 页面可见性检测,不可见时暂停轮询/动画

安全关联

无障碍功能与安全设计紧密关联:

  • NFKC 规范化escapeHtml()sanitizeHtml() 入口处执行 str.normalize('NFKC'),防止 Unicode 同形字符绕过 XSS 防护
  • 原型污染防护deepMerge() 使用 hasOwnProperty + Object.defineProperty 多层防护,修复了 CodeQL 安全告警
  • 循环替换防重组sanitizeHtml() 的 SSR fallback 循环执行标签剥离直到稳定,防止 <<script>><script> 重组攻击

Deep Link

Zephyr 注册了 clash:// 自定义 URL 协议,支持从浏览器或其他应用一键导入订阅配置。

使用方式

clash://install-config?url=https://example.com/subscribe&name=myconfig
参数 说明 限制
url 订阅 URL http:// / https://,最大 2048 字符
name 配置名称 拒绝 . / \ \0 等危险字符,最大 128 字符

安全设计

  • URL 解析:使用 url::Url::parse() 而非字符串匹配,防止 scheme 注入
  • 协议白名单:仅接受 clash:// scheme,订阅 URL 仅允许 http(s)://
  • 路径遍历防护:name 参数拒绝 . / \ \0 \n \r 等危险字符
  • 输入截断:URL 2048 字符、name 128 字符硬性上限
  • 单元测试:7 个测试覆盖正常/异常路径、路径遍历、截断

全局快捷键

Zephyr 支持自定义全局快捷键,即使应用在后台也能响应。

功能特性

功能 说明
自定义快捷键 用户可为不同操作绑定全局快捷键
平台感知显示 根据操作系统显示对应的修饰键(macOS: ⌘ / Windows: Ctrl)
快捷键录制 支持录制模式——用户按下组合键自动识别并填入
数量限制 最多 20 个快捷键
输入验证 action 最大 64 字符、accelerator 最大 128 字符
速率限制 注册/注销操作 500ms 冷却,防止滥用

系统通知

Zephyr 使用操作系统原生通知替代应用内弹窗,提供更自然的用户体验。

功能特性

功能 说明
原生通知 使用 tauri-plugin-notification 发送 OS 级通知
通知队列 最多 5 个同时通知,优先级排序(error > warning > info > success)
输入截断 title 128 字符、body 1024 字符
速率限制 1 秒冷却,防止通知轰炸
单例模式 使用 id(1) 确保同一时间只有一个通知

客户端自动更新

Zephyr 支持客户端自动更新检查,启动时静默检查并在有新版本时通知用户。

更新流程

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'sequence': {'messageAlign': 'center'}}}%%
sequenceDiagram
    participant App as Zephyr 启动
    participant API as GitHub API
    participant User as 用户

    App->>API: GET /repos/.../releases/latest
    API-->>App: { tag_name, assets, body }
    App->>App: 比较版本号
    alt 有新版本
        App->>User: 显示更新通知(含 Markdown Release Notes)
        User->>App: 点击"更新"
        App->>API: 下载安装包(SHA256 校验)
        API-->>App: 安装包文件
        App->>User: 打开安装包(系统默认程序)
    else 已是最新
        App-->>App: 静默跳过
    end
Loading

安全设计

  • 复用安全基础设施:复用 build_github_client()(User-Agent + DNS pinning)和 download_release_asset()(大小限制 + SHA256 校验 + Zip Slip 防护)
  • 临时目录下载:安装包下载到 zephyr_update_{uuid} 临时目录
  • 并发防护:前端 _updateCheckInProgress 标志防止重复检查
  • Markdown 安全:Release Notes 通过内置 Markdown 渲染器转换,所有内容经过 escapeHtml 转义

Prism Engine

Zephyr 集成了 Clash Prism Engine,一个基于 .prism.yaml 声明式配置的规则引擎/插件系统。通过 5 个独立的 clash-prism-* crate 实现模块化架构。

核心能力

功能 说明
声明式规则引擎 通过 .prism.yaml 文件声明规则,支持条件匹配、变量模板、规则分组
规则库 内置规则库管理(CRUD),支持从 URL 订阅导入,自动 YAML 安全清洗
插件系统 JavaScript 插件加载/卸载/调用,支持插件级沙箱权限控制
脚本沙箱 完整的 JavaScript 沙箱运行时,九维资源限制(执行时间/内存/输出/日志/脚本大小/字符串长度/循环/递归)
智能路由 基于节点测试历史的自动选择,支持延迟/速度/稳定性多维评估
自动故障转移 已集成到代理测试流水线,当前节点连续失败时自动切换至最优候选节点;支持并发锁防重入、嵌套组解析、延迟历史评估、中止信号感知
配置热重载 通过 Mihomo REST API 实现配置无缝热重载
速率限制 两套独立限流:原有命令(固定冷却时间)+ Prism 命令(滑动窗口:script_execute 10次/10s、rule_import_url 5次/10s)
Smart Score 限流 v2.2.3 新增:Smart Score IPC 并发限制为 2,避免测速期间密集 invoke 导致 UI 卡顿
配置验证 通过 mihomo -t -f 验证配置有效性,应用前预检

技术架构

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}}}%%
graph TB
    subgraph Crates["clash-prism-* Crates"]
        CORE["clash-prism-core 0.1.4<br/>配置解析 + 规则引擎"]
        EXT["clash-prism-extension 0.1.7<br/>扩展 + Watcher"]
        PLUGIN["clash-prism-plugin 0.1.1<br/>插件加载器"]
        SCRIPT["clash-prism-script 0.1.1<br/>脚本沙箱运行时"]
        SMART["clash-prism-smart 0.1.1<br/>智能节点选择"]
    end

    subgraph ZephyrPrism["Zephyr 集成层"]
        HOST["host.rs<br/>PrismHost trait 桥接"]
        CMDS["prism/ 11 个子模块<br/>67 个 Tauri Commands"]
        TESTS["prism_tests.rs<br/>119 个单元测试"]
    end

    subgraph Frontend["前端"]
        PRISM_UI["ui/prism.js<br/>Engine 控制层"]
        RULE_LIB["ui/rule-library.js<br/>规则库管理"]
        PLUGINS["ui/plugins.js<br/>插件管理"]
        EDITOR["ui/editor/<br/>CodeMirror 6 编辑器"]
    end

    CORE --> EXT
    EXT --> PLUGIN
    EXT --> SCRIPT
    EXT --> SMART
    HOST --> EXT
    CMDS --> HOST
    PRISM_UI -->|"invoke"| CMDS
    RULE_LIB -->|"invoke"| CMDS
    PLUGINS -->|"invoke"| CMDS
Loading

安全设计

安全措施 实现
文件名净化 sanitize_filename — URL 解码 + 拒绝 / \ .. \0 + sanitize_base_filename
输入大小限制 check_input_size — 10MB 硬上限
插件 ID 验证 validate_plugin_id — URL 解码 + 128 字符 + 路径分隔符拒绝
YAML 清洗 remove_dangerous_keys — 导入规则时剥离危险 key
沙箱默认安全 SandboxConfig::strict() — network/filesystem/child_process/workers 全部关闭
资源限制 ScriptLimits — 执行时间/内存/输出/日志/脚本大小/字符串/循环/递归 九维限制
配置验证 validate_config — 通过 mihomo -t -f 验证
URL 验证 validate_subscription_url_with_ip — DNS 解析 + IP pinning
文件大小限制 MAX_RESPONSE_SIZE — 读取文件大小检查

覆写系统 (v2.3.0)

v2.3.0 新增完整的配置覆写系统,支持两种格式:

Prism DSL(声明式)

YAML 格式,适合简单的规则注入、字段覆盖、节点过滤:

# 作用域:仅 SubA 和 SubB
__when__:
  profile: [SubA, SubB]

rules:
  $prepend:
    - "DOMAIN-SUFFIX,netflix.com,{{proxy}}"

dns:
  $default:
    enable: true
    enhanced-mode: fake-ip

支持的操作符:$prepend$append$override$default$filter$transform$remove

JavaScript 脚本(命令式)

QuickJS 沙箱执行,适合复杂逻辑(动态分组、条件判断、链式代理注入):

function main(config) {
    // 修改配置并返回
    config['mixed-port'] = 7890;
    return config;
}

执行管道

Prism Patches → JS 覆写(按 order 排序)→ 写回 run_config → 热重载

功能

  • CRUD:创建、读取、更新、删除覆写
  • 作用域:全局或指定订阅(__when__.profile
  • 远程覆写:从 URL 下载脚本,通过代理访问
  • 导入导出:JSON 格式批量导入导出
  • 持久化:{app_data}/prism/overrides/ 目录

全局用户偏好 (v2.3.0)

v2.3.0 将分散在 localStorage 和 YAML profile 中的全局偏好统一迁移到后端 settings.json,通过 patch_settings 命令原子更新,在运行时注入到配置中覆盖 YAML 默认值。

涵盖字段:modetun_enabledmixed_portsocks_portipv6allow_lanunified_delaydns_rewrite_enabledtheme_modeapp_opacity 等。

Smart State 异步持久化

v2.3.0 重写了 Smart State 持久化层,基于 WAL (Write-Ahead Log) 模式,使用 DashMap 无锁并发读取 + mpsc channel 异步处理持久化,IPC 非阻塞,崩溃可恢复。

单实例检测

Release 构建启用 tauri-plugin-single-instance,第二个实例启动时自动聚焦已有窗口并转发 clash:// 深度链接。


后端事件系统 (v2.3.1)

v2.3.1 新增统一的后端事件系统,替代散落在各 Rust 模块中的 eprintln!/println! 调用。

架构

组件 文件 说明
事件定义 backend_event.rs (577 行) BackendEvent 结构体 + 4 级日志 + 10 模块 + 83 错误码
前端监听 backend-events.js (198 行) 监听 backend-event + prism-event,缓冲 500 条,Fatal/Error 自动 Toast

事件模型

  • 4 个日志级别:Fatal / Error / Warn / Info
  • 10 个模块:Core、Subscription、Prism、Config、Plugin、System、Updater、Override、Rule、Smart
  • 83 个错误码:按模块分段(Core 1000-1999、Subscription 2000-2999、Prism 3000-3999、Config 4000-4999、Plugin 5000-5999、System 6000-6999、Updater 7000-7999、Override 8000-8999、Rule 9000-9999、Smart 10000-10999),含订阅下载细粒度诊断码(2007-2016)、配置加密/解密错误码(4006-4013)、WebView2 崩溃恢复码(6006-6021)、系统休眠恢复码(6019-6021)

路径脱敏

redact_error_message()core_dirprofiles_dir 替换为 [CORE_DIR] / [PROFILES_DIR] 占位符,支持正斜杠、反斜杠、转义反斜杠三种路径风格,大小写不敏感匹配。

前端事件分发

emit_to_main() 通过直接 eval() 注入 JS 绕过 Tauri 的 emit_js_filter 管道,解决 withGlobalTauri: false 配置下事件无法分发的问题。

便利宏

emit_error!(Core, CORE_START_FAILED, "mihomo failed to start: {e}");
emit_warn!(Subscription, SUB_UPDATE_TIMEOUT, "Timeout updating {name}");
emit_info!(Subscription, SUB_UPDATE_SUCCESS, "Updated {name}");

界面与体验

Zephyr 的前端界面完全基于原生 JavaScript 与 Tailwind CSS 构建,不依赖任何前端框架,实现了极致轻量与高度定制化的视觉体验。

玻璃拟态设计

全局采用 Glassmorphism(玻璃拟态)设计语言:

/* 玻璃拟态核心样式 */
.glass-card {
  background: rgba(255, 255, 255, 0.02);  /* 半透明背景 */
  backdrop-filter: blur(16px);               /* 背景模糊 */
  -webkit-backdrop-filter: blur(16px);
  border-radius: var(--radius-lg);           /* 大圆角 */
  border: 1px solid var(--border-primary);
  box-shadow: 0 4px 24px -8px rgba(0, 0, 0, 0.2);
}

主题系统

主题模式

模式 说明
深色模式 深色背景 + 浅色文字,适合夜间使用
浅色模式 浅色背景 + 深色文字,适合日间使用
Auto 跟随系统主题自动切换

主题色

提供 5 种预设主题色,同时支持通过取色器自定义任意颜色:

预设色 色值 视觉感受
薰衣草紫 #AF52DE 优雅、科技感
海洋蓝 #007AFF 沉稳、专业
翡翠绿 #34C759 自然、清新
玫瑰红 #FF2D55 活力、热情
琥珀橙 #FF9500 温暖、活力

主题色通过 Style Dictionary Design Token 管道(primitive → semantic → component 三层)生成 CSS 自定义属性,支持深色/浅色自动切换。强调色变量 --accent-primary 全局生效,所有强调色元素(按钮、链接、选中态等)自动跟随切换。

iOS 风格 Toggle 开关

内置三种尺寸的 iOS 风格 Toggle 开关组件:

尺寸 适用场景
小 (SM) 列表项内嵌开关
中 (MD) 设置面板选项
大 (LG) 独立功能开关

应用不透明度调节

支持在 10% - 100% 范围内调节应用窗口不透明度,用户可根据个人偏好或桌面壁纸搭配调整透明度。

节点名称滚动动画

当节点名称过长超出容器宽度时,自动启用水平滚动动画:

/* 节点名称滚动动画 */
@keyframes text-scroll {
  0% { transform: translateX(0); }
  15% { transform: translateX(0); }
  85% { transform: translateX(calc(-100% + 120px)); }
  100% { transform: translateX(calc(-100% + 120px)); }
}

.group:hover .scrolling-text {
  animation: text-scroll 4s linear infinite alternate;
}

.scrolling-text-container {
  mask-image: linear-gradient(to right, black 80%, transparent 100%);
}

通过 mask-image 渐变遮罩实现文字两端的淡出效果,滚动动画自然流畅。

通知系统

内置轻量级通知系统,支持四种通知级别。通知超时按是否有标题区分:有标题的通知 4 秒自动消失,无标题的通知 3 秒自动消失。

级别 用途
info 一般信息提示
success 操作成功反馈
warning 警告信息
error 错误信息

自定义下拉菜单

所有下拉菜单组件通过预定义的 DOM 容器元素挂载,避免被父容器的 overflow: hidden 裁切,确保在复杂布局中始终正确显示。

无边框窗口

平台 窗口样式 说明
Windows 无边框 自定义标题栏,完全掌控窗口外观
macOS 无边框 自定义标题栏,原生拖拽区域
Linux 无边框 自定义标题栏,与 Windows/macOS 统一体验(v2.3.4 起)

返回 Home

Clone this wiki locally