本文档以中文撰写。

功能特性

Zephyr 提供了一套完整的代理管理功能集,涵盖实时流量监控、智能节点选择、高级安全防护与跨平台适配。本文档描述 v2.5.0 的功能实现,并通过 Mermaid 图表直观展示数据流与处理逻辑。标注 (v2.5.0) 的章节为该版本新增功能。


目录


实时流量监控

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', '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

    MC->>HS: GET /traffic (chunked JSON)
    loop 持续推送 (约每秒 1 次)
        HS-->>WS: {"up": 1024, "down": 51200}
        WS->>WS: TextDecoder 解码 + 按换行分割
        WS->>SW: JSON.parse(line)
        SW->>SW: 超过 60 点时移除最旧数据
        SW->>TC: 通知数据更新
        TC->>TC: requestAnimationFrame 调度
        TC->>CV: 绘制下载面积图 (蓝 #0A84FF)
        TC->>CV: 绘制上传面积图 (珊瑚红 #FF6B6B)
    end
    Note over HS,WS: 断开时指数退避重连
    WS->>HS: 重连 (delay = min(1s × 2^attempt, 30s))

数据采集

流量数据通过 HTTP Streaming(非 WebSocket)从 Mihomo 内核的 /traffic 端点获取,前端使用 fetch + ReadableStream 逐块读取,TextDecoder 解码后按换行拆分为 JSON 行,每行包含 up / down(bytes/s)。

可视化渲染

特性 说明
双通道面积图 下载为蓝色 #0A84FF,上传为珊瑚红 #FF6B6B,半透明填充叠加
曲线平滑 相邻点之间以中点为终点的二次贝塞尔曲线(quadraticCurveTo)连接
60 点滑动窗口 MAX_DATA_POINTS = 60,旧数据自动移出视口
requestAnimationFrame 节流 渲染频率与显示器刷新率同步,避免无效重绘
DPR 适配 Canvas 物理像素按 devicePixelRatio 缩放,高分屏下保持锐利
窗口隐藏暂停 窗口隐藏或最小化时暂停渲染,可见时恢复

曲线绘制核心(modules/traffic-chart.js):

javascript
target.quadraticCurveTo(prev.x, prev.y, (prev.x + cur.x) / 2, (prev.y + cur.y) / 2);

速度格式化

formatBytes / formatSpeed 采用**十进制(base-1000)**单位,从 B 到 TB 自动切换,保留 2 位小数:

速度范围 显示格式 示例
< 1000 B/s XXX B/s 512 B/s
< 1000 KB/s X.XX KB/s 86.42 KB/s
< 1000 MB/s X.XX MB/s 3.14 MB/s
< 1000 GB/s X.XX GB/s 1.20 GB/s
更大 X.XX TB/s —
%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[输入:bytes/s] --> B{值 < 1000?}
    B -->|是| C[显示 B/s]
    B -->|否| D[除以 1000,单位升一级<br/>KB → MB → GB → TB]
    D --> E{值 < 1000 或已到 TB?}
    E -->|否| D
    E -->|是| F[保留 2 位小数显示]

    style A fill:#0A84FF,stroke:#0060DF,color:#fff
    style C fill:#10B981,stroke:#059669,color:#fff
    style F fill:#10B981,stroke:#059669,color:#fff

断线重连

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

参数 值 说明
初始延迟 1 秒 BASE_DELAY = 1000
最大延迟 30 秒 MAX_DELAY = 30000
最大重试次数 15 次 超过后停止重连
内核未运行时 3 次 连接被拒绝(core 未启动)时 MAX_RETRIES_REFUSED = 3
心跳超时 30 秒 30 秒无数据视为断流,主动重连
解析错误阈值 连续超过 10 次 触发强制重连(应对流格式异常)
%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[fetch /traffic] --> B[读取 chunk]
    B --> C{stream done 或<br/>30s 无数据?}
    C -->|是| H[指数退避等待]
    H --> I{超过重试上限?<br/>15 次 / 内核未运行 3 次}
    I -->|是| J[停止重连]
    I -->|否| A
    C -->|否| K[解码 + 按行 JSON.parse]
    K --> N{解析成功?}
    N -->|否| P{连续错误 > 10?}
    P -->|是| A
    P -->|否| B
    N -->|是| R[推入 60 点窗口并通知渲染]
    R --> B

    style A fill:#3B82F6,stroke:#2563EB,color:#fff
    style H fill:#F59E0B,stroke:#D97706,color:#fff
    style J fill:#EF4444,stroke:#DC2626,color:#fff
    style R fill:#10B981,stroke:#059669,color:#fff

Core 生命周期

Zephyr 对 Mihomo Core 的启动、健康检查和终止流程做了针对性设计,兼顾启动速度与优雅退出。

认证健康检查

启动内核后,后端以带 Authorization: Bearer <secret> 的 GET /version 进行健康检查(core/core_process.rs):

参数 值 说明
退避间隔 50ms 起步,每次翻倍,上限 1s HEALTH_CHECK_INITIAL_INTERVAL_MS / HEALTH_CHECK_MAX_INTERVAL_MS
最大重试 20 次 HEALTH_CHECK_MAX_RETRIES = 20
单次超时 500ms 异步 I/O,不阻塞运行时
密钥校验 正确 token 须返回 200,错误 token 须返回 401 区分自家内核与同端口上未设密钥的其他内核

配置预检

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

连接排空 (Connection Draining)

终止 Core 前先调用 DELETE /connections 关闭所有活跃连接(2 秒超时),帮助 Mihomo 在繁忙时更快、更干净地退出。

优雅终止

Unix 平台对内核所在进程组发送 SIGTERM,最多等待约 500ms(5 × 100ms 轮询),仍未退出再发送 SIGKILL:

SIGTERM(进程组)→ 最多等待 500ms → SIGKILL

孤儿进程防护

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

系统休眠恢复

系统从睡眠唤醒后(RunEvent::Resumed),后端以 3 次 TCP 探测(每次 2s 超时、间隔 1s)检查内核 API;探测失败则以最近一次配置与自定义参数重启内核,并发出 core-restarted-after-resume 事件通知前端刷新。


首页 (v2.5.0)

首页有两种视图,由设置项 home_page_mode 控制:minimal(默认)与 console。

精简首页统计条

精简首页顶部为一条极简统计条(ui/home-stats.js,每 2 秒轮询):

指标 说明
总流量 本次运行累计的下载/上传总量
活跃连接 当前连接数,附 TCP / UDP 拆分
节点延迟 当前所选节点的延迟

Console 控制台首页

信息密集的控制台风格仪表盘(ui/console-home.js):

  • KPI 卡片:下载/上传速度(含峰值)、总流量(↓↑ 明细)、活跃连接数(TCP/UDP 拆分)、节点延迟(快/中/慢徽章)
  • 实时流量趋势图与实时连接列表(可排序,显示进程图标、进程名与目标)
  • 节点卡片 + 快捷控制:模式切换(规则/全局/直连)、节点选择器、点击复制主机
  • 订阅用量面板 + 最近日志面板
  • 头部:显示内核真实运行时长与版本号
  • 缓存延迟优先:进入页面时先显示已缓存的延迟,再异步刷新,避免空白闪烁

进程图标

连接行显示发起连接的进程图标(后端 process_icon.rs,前端 ui/process-icon.js):

平台 图标来源
Windows 从 exe 中提取图标(SHDefExtractIconW,64px)
macOS 定位所属 .app,读取 Info.plist 指定的 .icns
Linux 通过 .desktop 文件(含 Flatpak)在 hicolor 主题与 pixmaps 中查找
  • 安全与性能:跳过网络挂载路径(读取挂载表判断);符号链接逐跳解析;单个图标上限 512 KB
  • 缓存:最多 256 条;查找失败的条目 5 分钟后才会重试

无订阅空状态

尚未添加任何订阅时,首页显示引导式空状态(ui/no-subscription.js),直接提供添加订阅入口,而不是空白仪表盘。

内核错误卡片

内核启动失败时,首页内嵌错误卡片(ui/core-error.js)展示错误原因与重试入口,而不仅仅依赖一次性提示。


智能节点选择

Zephyr 提供直观、高效的节点选择体验,结合自适应网格、延迟可视化与 3D 交互效果,让用户在大量节点中快速定位最优选择。

节点选择流程

%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[加载节点列表] --> B[GET /proxies]
    B --> C[解析代理组与节点]
    C --> D[渲染节点卡片网格]
    D --> E{用户操作}
    E -->|点击节点卡片| F[切换节点]
    E -->|悬停 300ms / 点击 / Enter| G[打开节点轮盘]
    G --> F
    F --> I[PUT /proxies/group_name]
    I --> J[关闭该组现有连接]
    J --> L[更新 UI 状态与延迟标签]
    L --> N[同步托盘菜单]

    style A fill:#0A84FF,stroke:#0060DF,color:#fff
    style F fill:#3B82F6,stroke:#2563EB,color:#fff
    style J fill:#F59E0B,stroke:#D97706,color:#fff
    style N fill:#10B981,stroke:#059669,color:#fff

自适应网格布局

节点列表使用 CSS Grid 自动填充,列数随可用宽度自然变化,无固定断点:

css
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));

延迟颜色编码

延迟范围 等级 含义
< 200ms good(绿色) 体验优良
200ms - 500ms ok(黄色) 可用,可能有轻微延迟
≥ 500ms 慢(红色) 延迟较高
测试中 加载动画 正在测量延迟
不可达 超时标记 延迟 ≤ 0 或 ≥ 999999

传输层协议指示器

节点卡片显示由代理类型映射而来的协议与传输标签(TRANSPORT_MAP),例如 SS · TCP,帮助快速识别节点类型。

排序模式

节点面板顶部的下拉菜单提供以下排序方式:

  1. 默认 -- 按 Mihomo 返回的原始顺序
  2. 名称 -- 按名称排序,同一地区的节点归为一组
  3. 延迟 -- 从低到高,不可达节点排末尾
  4. 智能 -- 基于节点评分排序,仅在开启「节点评分」时可选

3D 悬浮效果

节点卡片悬停时呈现 3D 透视倾斜(ui/3d-effect.js):mouseenter 时缓存 getBoundingClientRect(),之后在 requestAnimationFrame 中按指针相对卡片中心的偏移计算变换,避免每帧强制 reflow:

javascript
`perspective(1000px) rotateX(${-y / 40}deg) rotateY(${x / 40}deg) translateY(-3px)`

所有动效在 prefers-reduced-motion: reduce 时禁用。

节点名称滚动

名称超出卡片宽度时,.node-name 添加 is-marquee 类,播放 node-marquee 关键帧水平滚动,两端以渐变遮罩淡出。

节点轮盘 (v2.5.0)

首页顶部的节点胶囊显示当前实际使用的出口节点名,点击、按 Enter 或悬停 300ms 即可展开轮盘(ui/node-wheel.js):

操作 说明
打开 悬停 300ms、点击或 Enter
键盘导航 ↑ / ↓ 移动,Home / End 跳到首尾,Enter 确认,Escape 关闭
组行信息 代理组行显示该组当前所选节点的延迟
切换行为 切换节点后关闭现有连接,使新节点立即生效
加载中 Provider 尚在加载时显示加载提示,而非空白列表
  • 竞态防护:session 计数器防止快速连续操作导致的状态错乱
  • 自动同步:切换成功后同步到托盘菜单和代理列表页
%%{init: {'themeVariables': {'fontSize': '10px', 'actorTextColor': '#ccc', 'signalColor': '#666', 'lineColor': '#666', 'actorBkg': '#333', 'actorBorder': '#555', 'signalTextColor': '#ccc', 'noteBkgColor': '#333', 'noteTextColor': '#ccc', 'noteBorderColor': '#555'}}}%%
sequenceDiagram
    participant U as 用户
    participant RO as 节点轮盘
    participant API as Mihomo API
    participant TM as 托盘菜单

    U->>RO: 悬停 300ms / 点击 / Enter
    RO->>API: GET /proxies
    API-->>RO: 组与节点列表
    U->>RO: ↑↓ 选择 + Enter
    RO->>API: PUT /proxies/{group}
    API-->>RO: 切换成功
    RO->>API: 关闭现有连接
    RO-->>U: 更新胶囊并关闭轮盘
    RO->>TM: 同步托盘节点状态

隐藏超时节点

  • 过滤逻辑:延迟 ≤ 0 或 ≥ 999999 视为超时并隐藏
  • 保留当前节点:当前选中节点始终显示
  • 安全阀:若某组过滤后为空,则保留原始列表
  • GLOBAL 组:作为 include-all 处理,不参与过滤
  • 持久化:settings.json 的 hide_timeout_nodes 字段

Provider 加载轮询

代理组从 Provider 异步加载时,前端以 1.5 秒间隔轮询;每轮携带代际 token 防止陈旧请求,同一时间仅一个请求在途;连续 20 次(约 30 秒)仍无节点则显示重试按钮;离开代理页时自动清理。

代理节点记忆(v2)

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

  • v2 格式:同时保存 group + node;parseSelection 兼容旧版纯字符串格式
  • 主组偏好:savePrimaryGroupPreference / getPrimaryGroupPreference 独立保存用户选择的主组
  • 恢复优先链:主组偏好 > 已保存组 > 当前 UI 组
  • 重命名迁移:重命名配置时自动迁移相关记录

主组 Resolver

确定性的 7 级优先链解析主代理组:

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

辅助模块 run-config-cache.js 提供 5 秒 TTL 缓存、请求合并与代际计数器。

observedGroup 观察器

后台根据连接数据检测用户实际使用的代理组和节点(ui/observed-group.js):

  • 采样:取最近 30 条连接,遍历 chains 找第一个可写组
  • 阈值:频率 ≥ 3 且比率 ≥ 0.3 才视为有效
  • 连续确认:连续 3 次一致结果后才更新
  • 轮询间隔:2 秒
  • 特殊组排除:DIRECT、REJECT、PASS 等不参与检测

订阅管理

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

下载三级回退

订阅下载从不使用系统代理或环境变量代理,而是依次尝试以下三级:

%%{init: {'themeVariables': {'fontSize': '9px'}, '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[第二级:经内核 mixed 端口下载]
    F --> G{成功?}
    G -->|是| K
    G -->|否| H[第三级:写入恢复标记<br/>临时切换 GLOBAL 模式下载]
    H --> I{成功?}
    I -->|是| J[恢复原模式] --> K
    I -->|否| J2[恢复原模式] --> ERR2[返回下载失败]
    K[Base64 检测与解码] --> O[YAML 解析]
    O --> P{解析成功?}
    P -->|否| ERR3[返回解析错误]
    P -->|是| Q[安全清洗<br/>危险键 + 控制面键]
    Q --> T[AES-256-GCM 加密元数据]
    T --> W[write_file_secure 写入<br/>0600 权限]

    style B fill:#EF4444,stroke:#DC2626,color:#fff
    style D fill:#3B82F6,stroke:#2563EB,color:#fff
    style F fill:#0A84FF,stroke:#0060DF,color:#fff
    style H fill:#F59E0B,stroke:#D97706,color:#fff
    style Q fill:#EF4444,stroke:#DC2626,color:#fff
    style W fill:#10B981,stroke:#059669,color:#fff
    style ERR1 fill:#991B1B,color:#fff
    style ERR2 fill:#991B1B,color:#fff
    style ERR3 fill:#991B1B,color:#fff

第三级在切换前写入恢复标记,即使下载过程中应用异常退出,下次启动也能把模式恢复为原值。

多格式兼容

格式 处理方式 说明
Clash YAML 直接解析 标准 Mihomo / Clash 配置格式
Base64 编码 自动检测并解码 兼容分享链接格式;解码后须为包含 proxies: 的有效 YAML

客户端伪装

支持自定义 User-Agent,内置 Clash Verge Rev、mihomo-party、FlClash、Shadowrocket 等预设,也可输入任意 UA。每个订阅可单独设置 UA(覆盖全局 subscription_user_agent),长度上限 512 字符,仅允许 ASCII 可见字符;自动更新调度器同样优先使用订阅级 UA。

流量信息解析

自动解析 subscription-userinfo 响应头,展示流量使用进度与到期时间:

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

订阅链接对话框 (v2.5.0)

添加订阅使用独立的链接对话框(ui/link-dialog.js),输入时实时校验 URL(仅 http/https、长度与格式),不合法时即时提示并禁用确认按钮。导入远程覆写也复用同一对话框。

列表操作 (v2.5.0)

功能 说明
拖拽排序 通过拖拽手柄调整顺序,结果自动持久化
键盘排序 焦点在订阅行时按 Alt + ↑ / ↓ 移动
下次更新时间 开启自动更新的订阅显示下次计划更新时间
更新失败徽章 最近一次更新失败的订阅显示失败徽章,便于排查
下载排队 后端 download_sub 有 5 秒限流;手动下载超出时排队等待共享下载槽位,而不是直接报错
重命名 自动同步 last_config 与代理节点记忆
编辑面板 内联编辑 URL、名称、UA 与更新间隔,保留用户自定义名称
批量更新 一键更新所有订阅

自动更新调度

每个订阅可独立设置自动更新间隔:关闭 / 12 小时 / 1 天 / 3 天。若订阅已有不在预设中的旧值,则保留该值。后台 tokio 定时任务(subscription_scheduler.rs)负责执行。

其他功能

  • 智能命名:优先使用 Content-Disposition 文件名,其次提取策略组名称
  • 重复名称自动后缀:文件名已存在时追加递增数字
  • 拖拽导入本地配置:把 .yaml / .yml 文件拖入窗口即导入 profiles/,文件名经净化与目录边界校验,内容经过与订阅相同的危险字段清洗

TUN 虚拟网卡

Zephyr 支持通过 TUN 虚拟网卡实现系统级透明代理,接管所有网络流量。TUN 需要提升权限,各平台的提权方式不同。TUN 开关位于首页 / 模式控制区域。

跨平台权限管理

平台 权限获取方式 说明
macOS osascript 提权 弹出系统授权对话框,确认后以 root 身份启动内核
Windows UAC 以管理员身份重启 (v2.5.0) 需要管理员权限,见下文
Linux pkexec 授权 为 mihomo 授予 CAP_NET_ADMIN 并安装 polkit 规则,后续无需重复授权

macOS TUN 提权流程

%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[用户启用 TUN] --> B{内核正在启动?}
    B -->|是| ERR1[返回:内核启动中]
    B -->|否| G[停止当前 Mihomo]
    G --> K[构造 osascript 提权命令]
    K --> L[系统授权对话框]
    L --> M{用户确认?}
    M -->|取消| N[返回:用户取消]
    M -->|确认| O[以 root 启动 Mihomo]
    O --> S[认证健康检查]
    S --> T{通过?}
    T -->|否| R[清理并返回错误]
    T -->|是| V[标记 TUN 激活]
    V --> W[托盘切换为 TUN 图标]
    W --> X[通知前端]

    style A fill:#0A84FF,stroke:#0060DF,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 ERR1 fill:#991B1B,color:#fff
    style R fill:#991B1B,color:#fff

Windows UAC 提权 (v2.5.0)

Windows 上 TUN 需要管理员权限。Zephyr 通过 elevation.rs 以 ShellExecuteExW(runas 动词)请求 UAC,以管理员身份重新启动自身:

  • 按需提供:仅当当前用户属于拆分令牌的管理员(可以提升为自身)时才提供「以管理员身份重启」;相关命令为 is_process_elevated、can_elevate_as_self、restart_as_admin_cmd
  • 交接:新实例等待旧实例完全退出后再接管
  • 后续启动转发:提权实例运行期间,普通方式再次启动 Zephyr 会被转发给已提权实例
  • 安全默认:未提权的普通启动一律以 TUN 关闭状态启动,避免无权限时启动失败
  • 错误码:提权重启相关错误为 6025–6026
%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[用户开启 TUN] --> B{进程已提权?}
    B -->|是| C[直接启用 TUN]
    B -->|否| D{可提升为自身?<br/>拆分令牌管理员}
    D -->|否| E[提示需要管理员权限]
    D -->|是| F[确认后 ShellExecuteExW runas]
    F --> G{UAC 同意?}
    G -->|否| H[保持当前实例,TUN 关闭]
    G -->|是| I[新实例等待旧实例退出]
    I --> J[以管理员身份启动并启用 TUN]

    style C fill:#10B981,stroke:#059669,color:#fff
    style J fill:#10B981,stroke:#059669,color:#fff
    style F fill:#F59E0B,stroke:#D97706,color:#fff
    style E fill:#6B7280,stroke:#4B5563,color:#fff
    style H fill:#6B7280,stroke:#4B5563,color:#fff

托盘图标状态同步

状态 图标 说明
TUN 激活 红橙色(tray-tun.png) 系统级透明代理已启用
系统代理 绿色(tray-proxy.png) 系统代理模式运行
空闲 平台默认 macOS 为模板图像;Windows 按任务栏主题(注册表)显示浅色/深色字形;Linux 使用应用图标

托盘菜单还提供「复制环境变量」,以及代理子菜单(最多 15 个节点,取自主组;全局模式下取自 GLOBAL)。

DNS 防泄漏

启用 TUN 时,Zephyr 确保 dns-hijack 同时包含 UDP 与 TCP 两项,劫持所有 53 端口的 DNS 流量到 Mihomo:

yaml
tun:
  enable: true
  dns-hijack:
    - any:53        # UDP DNS 劫持
    - tcp://any:53  # TCP DNS 劫持
  • 已存在 dns-hijack 时补充缺失项;类型不正确时替换为正确格式
  • DNS 防泄漏设置仅通过内核 API 应用,不会写回用户的 profile 文件
  • 与 Fake-IP 模式配合,防止应用使用硬编码 DNS 绕过代理

安全设计

Zephyr 实现了多层纵深防御体系,覆盖网络请求、供应链、数据加密存储、文件系统与前端渲染等维度。

SSRF 防护链

每次网络请求(包括重定向的每一跳)都经过严格校验,最多跟踪 5 次重定向:

%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[解析目标 URL] --> B{HTTP/HTTPS?}
    B -->|否| ERR1[拒绝:非法协议]
    B -->|是| D{主机名为私有地址?<br/>localhost / .local 等}
    D -->|是| ERR2[拒绝]
    D -->|否| E[DNS 预解析]
    E --> F{解析结果为私有 IP?}
    F -->|是| ERR3[拒绝]
    F -->|否| G[DNS Pinning 固定解析结果]
    G --> H[发起请求]
    H --> I{收到重定向?}
    I -->|否| J[返回响应]
    I -->|是| K{次数 ≤ 5?}
    K -->|否| ERR4[拒绝:重定向过多]
    K -->|是| A

    style E fill:#F59E0B,stroke:#D97706,color:#fff
    style G fill:#3B82F6,stroke:#2563EB,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
防护层 机制 说明
DNS 解析验证 预解析 + IP 校验 请求前解析域名并校验目标 IP
私有 IP 拦截 CIDR 匹配 拦截 RFC 1918 / 环回 / 链路本地等地址
重定向链校验 最多 5 跳 每跳重新执行全部校验
协议白名单 HTTP/HTTPS only 仅允许 http / https
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

IPv6:

范围 CIDR 说明
环回 / 未指定 ::1, :: —
唯一本地地址 fc00::/7 ULA
链路本地 fe80::/10 —

供应链安全

校验项 策略 说明
SHA-256 校验 哈希比对 内核与客户端更新包完整性
Minisign 签名 Ed25519 客户端更新包必须同时通过 SHA-256 与 Minisign 签名验证
可信域名白名单 域名匹配 内核更新仅从 updater.rs 的 TRUSTED_HOSTS 下载;订阅下载使用 IP 级 SSRF 防护
大小限制 10MB 超限订阅直接丢弃
路径遍历防护 多轮 URL 解码 + 文件名净化 防止目录穿越

数据加密存储

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

参数 值 说明
加密算法 AES-256-GCM 认证加密
密钥派生 PBKDF2-HMAC-SHA256 从机器标识派生
迭代次数 100,000 次 抵御暴力破解
密钥绑定 机器标识 元数据文件迁移到其他设备后无法解密

⚠️ 注意:代理配置文件(.yaml)默认明文存储,支持可选的机器绑定加密;启用后使用相同的 AES-256-GCM 方案,密文以 djI6(v2: 的 base64)前缀标识。run_config.yaml 始终明文。

文件权限控制

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

命令限流

命令 限流间隔 说明
start_core 3 秒 防止内核频繁启停
download_sub 5 秒 手动下载超限时在前端排队
exempt_uwp_apps 5 分钟 UWP 环回免除操作较重
send_notification 1 秒 防止系统通知轰炸

限流器基于 DashMap<String, Arc<Mutex<Bucket>>>,不同 key 之间无锁竞争。Prism 命令另有一套滑动窗口限流(见 Prism Engine)。

配置清洗 (v2.5.0)

加载外部配置(订阅、规则库导入、Prism YAML 覆写)时,crates/core 的 sanitizer 会移除可能导致代码执行、路径遍历或暴露控制面的字段:

类别 被移除的键
代码执行 script、script-path
路径遍历 provider 中的 path
客户端私有字段 cfw-*
控制面(CONTROL_PLANE_KEYS) external-controller-unix、external-controller-pipe、external-controller-tls、external-controller-cors、external-doh-server、external-ui、external-ui-url、external-ui-name

控制面键的移除会沿 YAML << 合并键继续追踪:mihomo 会把合并源展平到同一映射,因此藏在合并源里的键同样会被清除。

前端渲染安全

  • escapeHtml:对 & < > " ' 做纯字符串映射转义,无需转义时走快速路径直接返回
  • sanitizeHtml:入口执行 NFKC 规范化,并循环剥离标签直到结果稳定,防止 <<script>> 重组
  • 原型污染防护:deepMerge() 使用 hasOwnProperty + Object.defineProperty
  • 外链限制:openUrl 只接受 http(s) 链接
  • 长度上限:Prism 中的名称最长 1,024 字节

配置热重载

配置修改无需重启 Mihomo 即可生效。前端通过 Tauri IPC 传递变更,后端完成安全键保护与 YAML 递归合并后写入 run_config.yaml,再通知内核从该文件重新加载。

热重载流程

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

    UI->>CM: invoke("update_config", {patch})
    Note over CM: JSON 转 YAML
    CM->>CM: 备份 external-controller / secret
    CM->>CM: merge_yaml(existing, patch, 0)
    Note over CM: 深度上限 50<br/>null 值表示删除键
    CM->>CM: 恢复安全键
    CM->>FS: 写入 run_config.yaml
    CM->>API: PUT /configs?force=true<br/>{"path": run_config.yaml}
    API-->>CM: 204 / 200
    CM-->>UI: Ok(())

递归合并算法

crates/core/src/config/merge.rs 中的 merge_yaml:

rust
pub fn merge_yaml(base: &mut YamlValue, patch: &YamlValue, depth: usize) -> Result<(), String> {
    if depth > 50 {
        return Err("YAML nesting depth exceeded limit".to_owned());
    }
    match (base, patch) {
        (YamlValue::Mapping(a), YamlValue::Mapping(b)) => {
            for (k, v) in b {
                if v.is_null() {
                    a.remove(k);
                } else if let Some(a_v) = a.get_mut(k) {
                    merge_yaml(a_v, v, depth + 1)?;
                } else {
                    a.insert(k.clone(), v.clone());
                }
            }
        }
        (a, b) => {
            *a = b.clone();
        }
    }
    Ok(())
}

安全键保护机制

安全键 保护原因 恢复值
external-controller 防止 API 绑定到非 loopback 地址 127.0.0.1:{port}
secret 防止移除 API 认证 保留原值;原配置无 secret 时不会自动添加

重载 API 调用

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

{"path": "<run_config.yaml 绝对路径>"}

内核从指定文件完整重载配置,force=true 确保强制生效。


快速延迟测试

测试机制

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

GET /proxies/{proxy_name}/delay?timeout=5000&url=https://www.gstatic.com/generate_204
参数 值 说明
测试 URL https://www.gstatic.com/generate_204 轻量 204 端点
超时时间 默认 5000ms 可自定义;前端额外留 1s 余量后中止请求
并发策略 并行请求 多节点同时测试
统一中止 共享 AbortController 重置测试时一次性取消所有在途请求
%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[触发延迟测试] --> B[乐观 UI:标签进入测试中]
    B --> E[并行发起请求]
    E --> F1[GET /proxies/node_1/delay]
    E --> F2[GET /proxies/node_2/delay]
    E --> F3[GET /proxies/node_N/delay]
    F1 --> H[逐个更新延迟标签]
    F2 --> H
    F3 --> H
    H --> J[颜色编码刷新]
    J --> K{按延迟排序中?}
    K -->|是| L[重新排序]
    K -->|否| N[完成]
    L --> N

    style A fill:#0A84FF,stroke:#0060DF,color:#fff
    style E fill:#3B82F6,stroke:#2563EB,color:#fff
    style N fill:#10B981,stroke:#059669,color:#fff

乐观 UI

触发测试后界面立即进入「测试中」状态,结果逐个到达逐个更新,无需等待全部完成。开启「隐藏超时节点」时,单节点测试超时会立即移除该节点。


自动切换与定时测速 (v2.5.0)

设置中的自动切换区域(ui/settings/auto-switch.js)提供三个相互独立的开关:

开关 作用 依赖
故障转移 当前节点连续测试失败时自动切换到最优候选节点 无
节点评分 基于测试历史(延迟、稳定性)为节点打分,启用「智能」排序 无
定时测速 后台按计划测试节点并更新评分 需开启节点评分
%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart LR
    S[节点评分] -->|前置条件| T[定时测速]
    T -->|更新| S
    S -->|提供| R[智能排序]
    F[故障转移] -->|独立工作| X[切换最优节点]

故障转移集成在代理测试流水线中,具备并发锁防重入、嵌套组解析与中止信号感知。


多种运行模式

模式 说明 适用场景
Rule(规则分流) 按配置中的规则分流 日常使用,推荐
Global(全局代理) 所有流量经 GLOBAL 组转发 需要全部走代理
Direct(直连) 所有流量直连 排查问题、访问本地服务

切换方式

前端调用 switch_mode 命令,由后端向内核发送:

PATCH /configs
Content-Type: application/json

{"mode": "rule"}

内核接受后才记录并持久化该模式;内核未运行时只记录设置,下次启动生效。全局模式下切换节点使用 switch_global_node 命令。

UI 交互

三段式分段控件带平滑滑块动画,当前模式高亮,切换即时生效。


跨平台支持

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

平台与安装包

平台 架构 格式
Windows x64 / ARM64 NSIS (.exe) + MSI (.msi),另有便携版 zip
macOS Intel / Apple Silicon DMG
Linux x64 / ARM64 DEB + AppImage + RPM,另有便携版 tar.gz

每个平台提供 Full 与 Lite 两种构建:Full 内置 mihomo 内核与 Geo 数据,Lite 不内置,需在应用内下载内核。

Windows 特性

  • 系统代理:通过注册表写入系统代理
  • TUN 提权:UAC 以管理员身份重启(见 Windows UAC 提权)
  • WebView2 崩溃恢复:监听 ProcessFailed;Render 进程崩溃时限流 reload(),5 分钟内 3 次则升级为完整窗口重建;Browser 进程退出时立即重建窗口
  • UWP 环回免除:为非框架 UWP 应用添加环回免除,使其可以走代理;操作前确认,5 分钟限流
%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[用户点击 UWP 环回豁免] --> B{距上次操作 > 5 分钟?}
    B -->|否| C[提示操作过于频繁]
    B -->|是| D[确认对话框]
    D --> E{用户确认?}
    E -->|取消| F[操作取消]
    E -->|确认| G[枚举非框架 UWP 应用]
    G --> K[CheckNetIsolation LoopbackExempt -a]
    K --> L{全部成功?}
    L -->|否| M[报告失败的应用]
    L -->|是| N[完成]

    style G fill:#3B82F6,stroke:#2563EB,color:#fff
    style N fill:#10B981,stroke:#059669,color:#fff
    style C fill:#6B7280,stroke:#4B5563,color:#fff
    style F fill:#6B7280,stroke:#4B5563,color:#fff

macOS 特性

  • TUN 提权:通过 osascript 弹出系统授权对话框
  • 托盘模板图像:空闲状态使用模板图像,随菜单栏明暗自动适配

Linux 特性

  • 多桌面环境系统代理:同时应用到所有检测到的桌面环境:
桌面环境 配置工具
GNOME gsettings
KDE Plasma kwriteconfig6
XFCE xfconf-query
  • 托盘剪贴板:WebKit2GTK 下托盘点击无法保证窗口焦点,剪贴板写入改由 Rust arboard(启用 wayland-data-control)在 spawn_blocking 中完成
  • AppImage 打包:使用 linuxdeploy-plugin-gtk 提升 GTK/WebKitGTK 兼容性
  • AUR:Release 发布时自动更新 zephyr-clash-bin

窗口

所有平台均为无边框透明窗口 + 自定义标题栏。窗口位置、大小与最大化状态通过 tauri-plugin-window-state 跨会话持久化;窗口创建时先隐藏、恢复状态后再显示,避免位置闪烁。


高级功能

端口转发

支持创建 TCP/UDP 端口转发隧道,将本地端口流量经指定代理节点转发到目标地址(协议、监听地址/端口、目标地址/端口、代理节点)。

DNS 覆写

独立的 DNS 配置面板,可覆盖内核默认 DNS:Fake-IP 模式、自定义 nameserver、按域名指定解析服务器。

动态配置引擎

高级配置页(ui/advanced.js)根据配置的嵌套结构递归生成编辑控件,按值类型(字符串、数字、布尔、数组、对象)自动选择输入控件,修改经热重载流程生效。

端口配置面板

可视化编辑 mixed-port、socks-port、redir-port、tproxy-port:

  • 范围 0–65535(0 表示禁用),仅接受纯数字
  • mixed-port 与 socks-port 不能同时为 0
  • 所有启用的端口不得重复
  • 保存时将旧版 port 置 0

轻量模式

关闭窗口时销毁 WebView 以释放内存,仅保留托盘图标;点击托盘恢复窗口。关闭「最小化到托盘」时自动禁用轻量模式。更新进行中会持有页面(update hold),避免安装过程中页面被销毁。

静默启动

启动时主窗口保持隐藏,仅显示托盘图标。与轻量模式互补:前者控制启动时的可见性,后者控制关闭后的内存占用。

开机自启

基于 Tauri autostart 插件,支持三平台(便携版不支持)。

系统代理所有权守卫

  • 启用系统代理时写入所有权标记(含代理地址)
  • 每 10 秒检测:若标记存在但系统代理被外部关闭,自动重新启用
  • 正常退出时删除标记;崩溃后标记保留,下次启动自动恢复(restore_sys_proxy)

代理环境变量复制

一键复制代理环境变量到剪贴板,设置页与托盘菜单均可触发:

  • 支持 Bash/Zsh、Fish、CMD、PowerShell、Nushell
  • 同时输出大小写变量(http_proxy + HTTP_PROXY 等)
  • 平台默认格式:Windows 为 PowerShell,其他为 Bash
  • 自动读取当前 mixed-port / socks-port

日志持久化

后端日志(内核输出 + 结构化事件)可持久化到磁盘:按日轮转、可配置最低级别、UI 中一键导出。

配置备份与恢复

  • 导出:将 settings.json、run_config.yaml 与全部 profile 打包为 ZIP,内含 manifest.json(SHA-256 校验 + 版本 + 时间戳)
  • 导入:验证(manifest + 校验和 + zip bomb 检测 + 路径遍历防护)→ 暂存 → 原子提交,失败自动回滚
  • 限制:最大 200MB 解压、200:1 压缩比;拒绝 ..、绝对路径与符号链接

Settings Schema 迁移

settings.json 带 schema_version,启动时自动迁移;JSON 损坏时备份为 settings.corrupt.<timestamp>.json 并写入默认值;迁移失败时备份原文件并重置。

便携版模式

程序目录存在 .portable 标记文件时,所有数据(配置、订阅、内核、缓存)存放在程序目录,否则使用系统标准目录。

Zephyr/
├── Zephyr.exe / Zephyr.AppImage
├── .portable          ← 便携模式标记
├── core/              ← mihomo 内核与 Geo 数据
├── profiles/          ← 订阅配置
├── prism/             ← Prism 数据
├── settings.json      ← 应用设置
└── .machine_key       ← 加密密钥
特性 便携版 安装版
安装步骤 解压即用 需要安装
数据位置 程序目录 系统目录
开机自启 ❌ ✅
客户端更新 跳转 Releases 页面手动下载 ✅ 应用内安装
内核更新 ✅ ✅
系统代理 / TUN ✅ ✅
多实例 ❌(单实例插件同样生效) ❌

UI 缩放

外观设置中的四档分段控件:100% / 110% / 125% / 150%。缩放值通过 --ui-scale 变量应用在 body 上,下拉菜单与右键菜单定位会按缩放比例换算;后端接受 0.5–2.0 范围内的值并持久化到 settings.json。

网络优化

三层网络性能优化,遵循 Google Cloud TCP 最佳实践:

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

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

所有默认值都尊重订阅/用户已有配置(仅在键不存在时写入)。

第二层:OS TCP 调优(手动应用,需授权)

平台 参数
Linux tcp_slow_start_after_idle=0、tcp_rto_min_us=5000、tcp_fastopen=3、tcp_ecn=1、hystart_detect=2、rmem_max/wmem_max=4MB、tcp_rmem 4K/256K/16MB、tcp_wmem 4K/256K/32MB
macOS tcp.fastopen=3、tcp.ecn.enable=1
Windows autotuninglevel=normal、initialRto=300、fastopen=enabled

第三层:DNS 优化

扩展 fake-ip-filter(覆盖 captive portal、游戏主机、STUN、LAN 发现等),并添加 default-nameserver。

网络优化 UI

设置中的网络优化行提供 Apply(显示变更详情与警告)/ Revert(从备份恢复原值)/ 状态指示。Linux 写入 /etc/sysctl.d/(重启保留);macOS 重启后失效;Windows 为系统级持久设置。

CPU v3 指令集检测

运行时检测 x86_64-v3(AVX2、BMI1/2、FMA),按「v3 优化版 → 兼容版 → 通用版」选择 mihomo 资产;检测失败回退兼容版;ARM64 直接使用通用版。

catch_unwind 守卫

start_core、write_config_file、update_core 等关键函数以 AssertUnwindSafe + catch_unwind 包裹,panic 转换为友好的错误字符串,避免整个应用崩溃。

zephyr-core Crate

共享业务逻辑位于独立的 crates/core(配置合并/清洗、订阅下载/SSRF 防护、限流器、状态管理等),可选启用 UniFFI 生成 Swift/Kotlin 绑定,为移动端复用做准备。


规则库 (v2.5.0)

规则库(ui/rule-library.js + ui/rule-library/)集中管理 Prism 规则文件,也是 Zephyr 的自定义规则编辑入口。

功能 说明
插入规则表单 通过表单添加规则,支持 12 种规则类型
内联编辑器 基于 CodeMirror 6 直接编辑规则文件
启用 / 禁用 停用的文件以 .disabled 后缀保存,不参与编译,可随时恢复
从订阅提取 从某个 profile 中提取其规则,生成规则库文件
导入 支持从文本、本地文件或 URL 导入
安全保存 保存前经过清洗:无法解析的 YAML 直接拒绝;若没有任何内容被移除,则保留用户原始文本(含注释与格式)
%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[文本 / 文件 / URL / 从订阅提取] --> B[YAML 解析]
    B --> C{可解析?}
    C -->|否| ERR[拒绝保存]
    C -->|是| D[清洗危险键与控制面键]
    D --> E{有内容被移除?}
    E -->|否| F[保存原始文本]
    E -->|是| G[保存清洗后的 YAML]
    F --> H[规则库文件]
    G --> H
    H --> I{启用?}
    I -->|是| J[参与 Prism 编译并热重载]
    I -->|否| K[以 .disabled 保存]

    style D fill:#EF4444,stroke:#DC2626,color:#fff
    style J fill:#10B981,stroke:#059669,color:#fff
    style ERR fill:#991B1B,color:#fff

URL 导入经过与订阅相同的 SSRF 防护,并受 Prism 限流约束(rule_import_url 5 次 / 10 秒)。


连接监控

Zephyr 内置实时连接监控页面(modules/connections.js),对 Mihomo 的所有活跃连接进行细粒度管理。

数据采集架构

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

    Timer->>API: 页面可见且未暂停时触发
    API->>Mihomo: GET /connections (Bearer secret)
    Mihomo-->>API: 连接列表 (累计字节数)
    API-->>Acc: 检测消失的连接 → 归档到关闭历史
    Acc-->>Acc: delta 计算与速率
    Acc-->>UI: 更新统计栏与列表

Delta 累积追踪

Mihomo 返回的 download / upload 为累计字节数。Zephyr 每次轮询计算增量:

javascript
const dlDelta = Math.max(0, curDl - acc.prevDl);
acc.dl += dlDelta;
if (dt > 0.05) {
    acc.dlSpeed = dlDelta / dt;
}
acc.prevDl = curDl;
  • 正向 delta 防护:Math.max(0, …) 防御计数器重置导致的负值
  • 边界保护:dt > 0.05 防止轮询过快导致速率失真
  • 关闭历史:消失的连接归档,上限 200 条(MAX_CLOSED_CONNS)

功能特性

功能 说明
实时轮询 2 秒间隔,仅页面可见时运行
暂停 一键暂停刷新,便于查看
Active / Closed 标签 活跃连接与关闭历史分页
进程图标 连接行显示进程图标(见 进程图标)
搜索过滤 按主机名、目标 IP、端口、规则、链路、进程名搜索
多列排序 三态循环(降序 → 升序 → 关闭)
连接详情面板 显示完整元数据;活跃连接只更新数值,不重建 DOM
关闭匹配连接 关闭当前过滤结果中的连接,或关闭全部
清除历史 清空关闭历史
统计栏 3 项汇总:连接总数、↓ 总量、↑ 总量

所有来自 API 的动态数据均经 escapeHtml() 转义后插入 DOM。


日志查看 (v2.5.0)

日志页(ui/logs.js)统一展示应用日志与内核日志,二者共用同一过滤栏。

数据流架构

%%{init: {'themeVariables': {'fontSize': '9px'}, 'sequence': {'messageAlign': 'center'}}}%%
sequenceDiagram
    participant Timer as 轮询定时器<br/>(1s)
    participant Rust as Rust 后端<br/>read_core_log
    participant File as 内核日志文件
    participant UI as logs.js

    Timer->>Rust: invoke("read_core_log", offset, limit)
    Rust->>File: 从 offset 增量读取
    File-->>Rust: 新日志行
    Rust-->>UI: { lines, rotated, offset }
    UI->>UI: rotated 时重置缓冲区
    UI->>UI: 追加到环形缓冲区 (2,000 行)
    UI->>UI: 按过滤条件增量渲染

功能特性

功能 说明
统一过滤栏 应用日志与内核日志共用
级别过滤 全部 / Info 及以上 / Warn 及以上 / 仅错误
行数显示 显示当前匹配行数
暂停 暂停追加新行
跳到最新 向上滚动后出现「跳到最新」胶囊,并显示新增行数
结构化行 每条日志按字段结构化渲染,而非原始文本
搜索高亮 ui/log-highlight.js 在已转义文本上标记匹配项
增量读取 offset 增量读取,自动检测日志轮转
环形缓冲区 内存中最多 2,000 行;轮询间隔 1 秒

安全设计

  • 无路径遍历风险:日志路径由后端生成,不接受用户路径输入
  • XSS 防护:日志文本先经 escapeHtml 转义,再在转义结果上插入高亮标记
  • 本地显示:日志仅在本地窗口展示,不发送到远程服务器

键盘操作与无障碍 (v2.5.0)

Zephyr 的核心功能均可仅用键盘完成,并为屏幕阅读器提供完整语义。

实现模块

模块 职责
utils/focus-trap.js 模态框内 Tab 循环,Escape 关闭
utils/roving-tabindex.js 代理列表方向键导航 + Enter/Space 激活
ui/tablist-keys.js 标签页列表的方向键 / Home / End 切换
ui/input-modality.js 仅在键盘操作时显示焦点环,鼠标点击不显示
ui/nav-tooltip.js 侧边导航图标的悬停 / 聚焦提示
ui/header-fit.js 页面头部在窄窗口下自适应,避免内容被截断
ui/seg-control.js 分段控件的键盘与 ARIA 支持
utils/page-visibility.js 页面不可见时暂停轮询与动画

功能特性

功能 说明
ARIA 语义 listbox / option / dialog / tablist / status、aria-live="polite"、aria-modal、aria-selected
键盘排序 订阅列表 Alt + ↑ / ↓
节点轮盘 ↑↓ / Home / End / Enter / Escape
折叠面板 role="button" + aria-expanded + aria-controls,Enter/Space 切换
统一禁用态 按钮、表单控件与下拉项的禁用态及 hover 保护统一处理
减少动效 遵循 prefers-reduced-motion,禁用 3D、导航与滚动动画
主题感知焦点环 --zephyr-focus-ring 通过 color-mix 跟随主题色

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

使用方式

clash://install-config?url=https://example.com/subscribe&name=myconfig
参数 说明 限制
url 订阅 URL 仅 http:// / https://,最大 2048 字符
name 配置名称 拒绝路径分隔符与危险字符;Windows 保留设备名加 _ 前缀;清理尾部空格;最大 128 字符

待处理队列

%%{init: {'themeVariables': {'fontSize': '10px', 'actorTextColor': '#ccc', 'signalColor': '#666', 'lineColor': '#666', 'actorBkg': '#333', 'actorBorder': '#555', 'signalTextColor': '#ccc', 'noteBkgColor': '#333', 'noteTextColor': '#ccc', 'noteBorderColor': '#555'}}}%%
sequenceDiagram
    participant OS as 操作系统
    participant BE as Rust 后端
    participant FE as 前端页面
    participant U as 用户

    OS->>BE: clash://install-config?...
    BE->>BE: 解析校验后入队 (最多 8 条)
    BE-->>FE: deep-link 事件 (无载荷)
    FE->>BE: take_pending_deep_links
    BE-->>FE: 待处理链接
    FE->>U: 确认对话框
    U->>FE: 确认导入
    FE->>BE: 下载订阅
  • 冷启动或页面尚未就绪时到达的链接不会丢失,页面就绪后主动拉取
  • 每条链接导入前都需用户确认
  • 第二个实例启动时,链接经单实例插件转发给已运行的实例

安全设计

  • URL 解析:使用 url::Url::parse(),防止 scheme 注入
  • 协议白名单:仅 clash://;订阅 URL 仅 http(s)://
  • 路径遍历防护:name 拒绝 . / \ \0 \n \r 及 < > : " | ? *
  • 输入截断:URL 2048、name 128 字符硬上限
  • 快照测试:insta 快照覆盖正常/异常路径、路径遍历、保留设备名、危险字符、长名称与尾部空格

全局快捷键

基于 tauri-plugin-global-shortcut,应用在后台时也能响应。

功能 说明
自定义快捷键 为不同操作绑定全局快捷键
平台感知显示 macOS 显示 ⌘,Windows/Linux 显示 Ctrl
快捷键录制 按下组合键自动识别并填入
数量限制 最多 20 个
输入验证 action ≤ 64 字符,accelerator ≤ 128 字符
速率限制 注册/注销 500ms 冷却

快捷键触发的结果在窗口不可见时以系统通知告知,否则使用应用内提示。


通知与提示

应用内提示 (Toast)

特性 说明
级别 info / success / warning / error
显示时长 有标题 3.5 秒,无标题 2.5 秒,常驻型 7 秒
悬停暂停 鼠标悬停时暂停倒计时
队列 最多 5 条排队,按优先级排序

确认对话框同样排队显示,多个确认请求依次弹出而不会互相覆盖。

系统通知

send_notification 命令通过 tauri-plugin-notification 发送 OS 原生通知:标题截断 128 字符、正文 1024 字符,使用固定 id(1) 保持单条,1 秒限流。主要用于窗口不可见时的场景,例如内核启动失败、更新检查结果、全局快捷键反馈;发送失败时回退为应用内提示。


更新 (v2.5.0)

内核与客户端更新统一在更新对话框(ui/update-dialog.js)中完成。

更新对话框状态

%%{init: {'themeVariables': {'fontSize': '9px'}}}%%
stateDiagram-v2
    state "检查中" as Checking
    state "已是最新" as UpToDate
    state "有可用更新" as Available
    state "安装中" as Installing
    state "完成" as Done
    state "失败" as Failed
    [*] --> Checking
    Checking --> UpToDate
    Checking --> Available
    Checking --> Failed
    Available --> Installing: 用户确认
    Installing --> Done
    Installing --> Failed
    Failed --> Checking: 重试
    UpToDate --> [*]
    Done --> [*]
  • 发行说明:「有可用更新」状态显示 Release Notes,经安全的 Markdown 渲染(内容先转义)
  • 安装顺序:内核与客户端同时有更新时,先安装内核
  • 便携版:客户端更新改为跳转 Releases 页面;内核更新照常在应用内完成
  • 更新保持:安装期间持有页面(end_update_hold 结束),轻量模式下页面也不会被销毁
  • 下载路径:先直连,失败时回退为经由 Zephyr 管理的内核代理下载
  • 自动检查:auto_update(内核)/ auto_update_client(客户端)开启后,启动 5 秒后检查并通知,不会自动安装
  • 双源检查:同时请求 GitHub REST API 与 Atom Feed,tokio::select! 取先成功者

内核更新

%%{init: {'themeVariables': {'fontSize': '9px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[用户确认安装] --> B[按平台与 CPU 选择资产<br/>Windows .zip / 其他 .gz]
    B --> C[从可信域名下载]
    C --> D{SHA-256 校验通过?}
    D -->|否| E[删除文件并报错]
    D -->|是| F[解压新二进制]
    F --> G[停止当前内核]
    G --> H[备份旧二进制]
    H --> I[原子重命名替换]
    I --> J[启动新内核]
    J --> K{健康检查通过?}
    K -->|是| L[完成,显示新版本]
    K -->|否| M[用备份回滚并报错]

    style C fill:#3B82F6,stroke:#2563EB,color:#fff
    style I fill:#F59E0B,stroke:#D97706,color:#fff
    style L fill:#10B981,stroke:#059669,color:#fff
    style E fill:#991B1B,color:#fff
    style M fill:#991B1B,color:#fff

客户端更新

  • 安装包下载到 zephyr_update_{uuid} 临时目录
  • 必须同时通过 SHA-256 与 Minisign(Ed25519)签名验证;公钥内置于 minisign_verify.rs,.minisig 文件限 4KB;任一失败即删除安装包并报错
  • 复用 build_github_client()(User-Agent + DNS pinning)与 download_release_asset()(大小限制 + 校验 + Zip Slip 防护)

Prism Engine

Zephyr 集成了 Clash Prism Engine,一个基于 .prism.yaml 声明式配置的规则引擎/插件系统,由 5 个独立的 clash-prism-* crate 组成。

核心能力

功能 说明
声明式规则引擎 .prism.yaml 声明规则,支持条件匹配、变量模板、规则分组
规则库 规则文件管理,见 规则库
插件系统 JavaScript 插件加载/卸载/调用,插件级沙箱权限控制
脚本沙箱 完整的 JavaScript 沙箱运行时,多维资源限制(执行时间/内存/输出/日志/脚本大小/字符串长度/循环/递归)
智能路由 基于节点测试历史的评分,见 自动切换与定时测速
配置热重载 PUT /configs?force=true 携带 run_config.yaml 路径
速率限制 滑动窗口:script_execute 10 次/10s、rule_import_url 5 次/10s;Smart Score IPC 并发上限 2
配置验证 应用前通过 mihomo -t -f 预检
YAML 覆写清洗 Prism YAML 覆写同样经过危险键与控制面键清洗
WiFi SSID 条件 __when__.ssid 按当前 WiFi 应用不同规则
网络切换协调器 自动检测网络切换并重新编译应用配置
沙盒边界验证 script_set_limits 强制边界(执行时间 100ms–300s、内存 1MB–512MB、循环 1K–10M、递归 8–512),权限提升时发出 PRISM_SANDBOX_CHANGED(3007)警告

WiFi SSID 条件

  • 平台检测:netsh / networksetup / iwconfig 获取当前 SSID
  • 缓存:4 秒 TTL(含 None 结果),断网时不会反复 spawn 子进程
  • 解析:跳过 BSSID 行,仅匹配 SSID 前缀
  • 联动:集成到 NetworkChangeCoordinator,网络切换时自动重新编译

网络切换协调器

NetworkChangeCoordinator:Windows 使用 NotifyAddrChange,全平台另有回退轮询;2 秒去抖动;单飞行状态机;fail-closed 验证(HTTP 2xx + ACK 双确认)。

技术架构

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

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

    subgraph Frontend["前端"]
        PRISM_UI["ui/prism.js"]
        RULE_LIB["ui/rule-library.js"]
        PLUGINS["ui/plugins.js"]
        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

安全设计

安全措施 实现
文件名净化 sanitize_filename — URL 解码 + 拒绝 / \ .. \0
输入大小限制 check_input_size — 10MB 上限
名称长度 名称最长 1,024 字节
插件 ID 验证 validate_plugin_id — URL 解码 + 128 字符 + 拒绝路径分隔符
YAML 清洗 导入规则与 YAML 覆写时剥离危险键与控制面键
沙箱默认安全 SandboxConfig::strict() — network/filesystem/child_process/workers 全部关闭
URL 验证 validate_subscription_url_with_ip — DNS 解析 + IP pinning

覆写系统 (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 沙箱执行,适合动态分组、条件判断、链式代理注入等复杂逻辑:

javascript
function main(config) {
    config['mixed-port'] = 7890;
    return config;
}

执行管道

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

功能

  • CRUD 与作用域(全局或 __when__.profile 指定订阅)
  • 远程覆写:通过链接对话框输入 URL 导入
  • 导入导出:JSON 批量导入导出
  • 失败指示:每个覆写记录 lastError,执行失败时状态显示为失败并给出错误信息
  • 仅保存:可只保存不立即应用
  • 面包屑:编辑器顶部显示路径,存在未保存修改时带标记
  • 持久化:{app_data}/prism/overrides/

全局用户偏好 (v2.3.0)

全局偏好统一保存在后端 settings.json,通过 patch_settings 原子更新,并在运行时注入配置、覆盖 YAML 默认值。涵盖 mode、tun_enabled、mixed_port、socks_port、ipv6、allow_lan、unified_delay、dns_rewrite_enabled、theme_mode、app_opacity、home_page_mode 等。

Smart State 异步持久化

基于 WAL 模式:DashMap 无锁并发读取 + mpsc channel 异步持久化,IPC 非阻塞,崩溃可恢复。

单实例

tauri-plugin-single-instance 确保同时只运行一个实例:第二次启动会聚焦已有窗口并转发 clash:// 链接;Windows 提权实例运行时,普通启动会被转发给提权实例。


后端事件系统 (v2.3.1)

统一的后端事件系统替代散落的 eprintln!/println!。

架构

组件 文件 说明
事件定义 backend_event.rs BackendEvent 结构体 + 4 级日志 + 11 模块 + 97 错误码
前端监听 backend-events.js 监听 backend-event + prism-event,缓冲事件,Fatal/Error 自动提示

后端共向前端发出 15 种事件,包括 core-restarted-after-resume(休眠恢复后重启内核)与无载荷的 deep-link 通知(页面随后从队列拉取)。

事件模型

  • 4 个级别:Fatal / Error / Warn / Info
  • 11 个模块:Core、Subscription、Prism、Config、Plugin、System、Updater、Override、Rule、Smart、Frontend
  • 97 个错误码:按模块分段(Core 1000+、Subscription 2000+、Prism 3000+、Config 4000+、Plugin 5000+、System 6000+、Updater 7000+、Override 8000+、Rule 9000+、Smart 10000+、Frontend 11000+)
错误码 含义
1012–1014 GLOBAL 组切换与恢复
2007–2016 订阅下载细粒度诊断
4006–4013 配置加密/解密
4015 CONFIG_WRITE_FAILED 配置写入失败
6006–6021 WebView2 崩溃恢复与系统休眠恢复
6025–6026 Windows 提权重启
11001–11004 前端日志转发

前端日志转发

前端 console 错误、Toast、未捕获异常与未处理的 Promise rejection 上报到后端应用日志:

  • frontend-log.js fire-and-forget,不阻塞 UI;限流(每 5 秒 50 条非错误 + 100 条错误)+ 内容哈希去重
  • Frontend 模块事件不再触发 Toast,避免反馈循环
  • 前端截断 16 KiB,后端 UTF-8 安全截断

路径脱敏

redact_error_message() 将 core_dir 和 profiles_dir 替换为 [CORE_DIR] / [PROFILES_DIR],支持正斜杠、反斜杠与转义反斜杠,大小写不敏感。

便利宏

rust
emit_error!(Core, CORE_START_FAILED, "mihomo failed to start: {e}");

设置页 (v2.5.0)

分区

设置页分为 7 个分区,另有独立的「高级配置」页面:

分区 主要内容
外观 语言、主题模式、主题色、不透明度、UI 缩放、首页视图(精简 / 控制台)
通用 最小化到托盘、开机自启、静默启动、轻量模式、全局快捷键
代理 故障转移、节点评分与参数、定时测速、隐藏超时节点、统一延迟、高级配置入口
网络 端口设置、局域网连接、IPv6、DNS 覆写、复制代理环境变量、端口转发、网络优化、UWP 环回免除(Windows)
订阅与规则 订阅客户端伪装(UA)、规则库自动应用与文件管理、Geo 数据更新
数据与安全 备份与恢复、配置加密、配置文件夹、日志设置与导出
关于 版本信息与检查更新、自动检查更新、第三方许可、GitHub 主页、恢复默认设置

TUN 开关不在设置页,统一在首页/模式控制区域切换。

分区导航与搜索

ui/settings-nav.js 提供分区索引栏(点击跳转到对应分区)与设置搜索(按名称查找并定位设置项)。

恢复默认设置

位于「关于」分区。确认对话框会分别列出将被重置的内容与会被保留的内容,由用户确认后执行。

第三方许可

「关于」分区内置第三方许可查看器(get_third_party_licenses),无需离开应用即可浏览依赖的许可证文本。


国际化与 RTL (v2.4.3)

支持 14 种语言,每种约 1,020 个翻译键。

RTL 基础设施

  • CSS 逻辑属性:margin-inline、padding-inline、inset-inline、text-align: end 替代物理属性
  • RTL 变量:--rtl-sign / --scroll-fade-dir,:root[dir=rtl] 级联
  • 动画适配:开关、模式滑块、shimmer、文本滚动均用 var(--rtl-sign) 反转方向
  • LTR 锁定:代码编辑器与日志面板始终 LTR
  • 上下文菜单:RTL 下坐标镜像定位
  • 工具函数:getDirection()、getRTLSign()

语言列表

代码 语言 代码 语言
zh 中文 tk 土库曼语
en 英语 my 缅甸语
ja 日语 ar 阿拉伯语(RTL)
ko 韩语 ur 乌尔都语(RTL)
ru 俄语 sw 斯瓦希里语
es 西班牙语 tr 土耳其语
fa 波斯语(RTL) vi 越南语

界面与体验

前端基于原生 JavaScript + UnoCSS(presetWind4)构建,不依赖任何前端框架。

玻璃拟态设计

全局采用 Glassmorphism:半透明背景、backdrop-filter 模糊、细边框与柔和阴影,配合透明无边框窗口。

主题系统

主题模式

模式 说明
深色 适合夜间使用
浅色 适合日间使用
自动 跟随系统主题

主题色

4 种预设主题色,另支持自定义十六进制颜色:

预设 色值
蓝(默认) #007AFF
绿 #34C759
橙 #FF9500
粉 #FF2D55

主题色经 Style Dictionary Design Token 管道(primitive → semantic → component)生成 CSS 自定义属性,--accent-primary 全局生效。

应用不透明度

滑块范围 10%–100%。

Design Token

圆角(packages/tokens/src/primitive.json):

Token 值 典型用途
radius.control 8px 按钮、输入框
radius.surface 12px 卡片
radius.md 16px 中型容器
radius.dropdown-option 18px 下拉选项
radius.overlay 24px 模态框、下拉菜单、浮层
radius.full 9999px 圆形元素

动效时长:

Token 值 用途
time.micro 0.12s 按下 / 悬停
time.standard 0.2s 展开 / 切换
time.slow 0.32s 大型面板

导航栏微动效

侧边栏图标采用 Draw-On 描绘动画(pathLength=1 + stroke-dashoffset),每个页面有独立的动画;is-active / is-leaving 状态切换,prefers-reduced-motion 时禁用。

组件系统

  • 按钮状态矩阵:disabled(降低不透明度 + not-allowed)、loading(aria-busy="true" + spinner)
  • 统一表单控件 .form-control:sm / md / lg 尺寸 + mono 变体
  • 分段控件:模式切换、UI 缩放等
  • Status Dot / Latency Badge / Danger Zone
  • Status Ring:SVG 圆形进度,用于 Geo 数据库与内核更新
  • 自定义下拉菜单:挂载到独立容器,避免被 overflow: hidden 裁切

国旗字体

内置 Catrinity 子集国旗字体,节点名称中的国旗 emoji 在所有平台(包括默认不显示国旗 emoji 的 Windows)上一致渲染。

品牌标识

v2.5.0 启用新 Logo 与横排组合标(icons/lockup.svg、icons/lockup-white.svg)。


返回 Home

↑↓ 选择↵ 打开