Skip to content

Features

Juwan-Hwang edited this page Apr 18, 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

智能节点选择

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 红色 延迟较高,不适合实时应用
测试中 灰色 + 加载动画 正在测量延迟
不可达 红色 + 超时标记 节点无法连接

排序模式

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

  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)`;
  });
});

节点轮盘选择

以下时序图展示了节点轮盘(Roulette)选择的完整交互流程,从用户点击代理胶囊到最终同步托盘菜单的全链路:

%%{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[第三优先级:系统代理下载]
    H --> I{下载成功?}
    I -->|是| K
    I -->|否| 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 时间戳)

其他功能

  • 批量更新:一键更新所有已添加的订阅
  • 拖拽导入:支持将 .yaml / .yml 文件直接拖拽到订阅页面导入

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 标准权限 通过 CAP_NET_ADMIN 或 root 权限创建 TUN 接口

托盘图标状态同步

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

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

安全设计

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 解码 + 文件名净化 防止目录穿越攻击

数据加密存储

敏感数据(如订阅链接、认证信息)使用 AES-256-GCM 加密存储:

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

文件权限控制

平台 权限设置 说明
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
  • 窗口装饰:Linux 使用原生窗口装饰(有边框),Windows 和 macOS 使用无边框窗口

高级功能

自定义规则编辑器

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"]
  }
}

开机自启

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

内核更新机制

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 src/utils/ ~115 行 模态框焦点陷阱,Tab 循环 + Escape 关闭
roving-tabindex.js src/utils/ ~221 行 代理列表键盘导航,Arrow keys + Enter 激活
page-visibility.js 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 转义

界面与体验

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 种预设主题色,同时支持通过取色器自定义任意颜色:

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

主题色通过 CSS 变量 --color-accent 全局生效,所有强调色元素(按钮、链接、选中态等)自动跟随切换。

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 原生窗口装饰 使用系统提供的窗口边框和标题栏,保证兼容性

返回 Home

Clone this wiki locally