TieZ:Tauri 2 剪贴板管线的分层、主题系统与 XChaCha20 加密中继

约 9 分钟阅读桌面应用

文章目录7 节

TieZ 是一个 Tauri 2 + React + Rust 的跨平台剪贴板管理器,3.5 万行 Rust 里包含采集管线、SQLite 仓储、主题系统、WebDAV/MQTT 同步,以及一条刻意独立于云同步的加密中继通道。本文讲它的分层边界、主题令牌体系和中继的密码学设计。

TieZ 的代码量在个人项目里算偏大的:src-tauri/src 下约 35,800 行 Rust,前端 114 个 TS/TSX 文件。大不是重点,重点在于它把"剪贴板采集""本地仓储""主题渲染""跨设备传输"这几件事分得比较清楚,尤其是把剪贴板中继(Clipboard Relay)和云历史同步做成了两条互不干扰的通道。这在实际使用里很关键:临时把一段文本丢到另一台机器,不应该意味着把整份历史记录上传。

一、分层与模块地图

1
2
3
4
5
6
7
8
9
10
11
12
13
src-tauri/src/
app/ # 生命周期:setup、hooks、commands(settings_cmd / system_cmd / ui_cmd)
services/
clipboard/ # mod.rs(1731) 采集与写回、pipeline.rs(616) 处理阶段
clipboard_relay.rs # 1368 行:加密中继
cloud_sync.rs # 5268 行:WebDAV 历史同步
encryption_queue.rs # 加密任务队列
mqtt_sub.rs # MQTT 连接
file_transfer/ # 局域网文件/文本传输(含 web_ui)
sensitive_align.rs # 敏感内容对齐/掩码
infrastructure/
repository/ # clipboard_repo.rs(2271)、tag_repo、migrations.rs(855) —— SQLite
windows_api/ # win_clipboard.rs(1061)、drag_drop、apps

技术栈上几个刻意的选择:

关注点 选型
剪贴板读写 arboard(启用 wayland-data-control)+ clipboard-rs,兼顾 X11/XWayland 与实现 data-control 的 Wayland 合成器
本地存储 rusqlite(bundled)+ 自管迁移,不依赖外部服务
凭据存储 keyring 4.1.5(Windows Credential Manager / macOS Keychain / Linux Secret Service)
加密 chacha20poly1305(XChaCha20-Poly1305)
局域网传输 axum 0.8.8(multipart/ws)
MQTT rumqttc + rustls
二维码识别 rqrr;图片处理 image

发布构型也做了取舍:lto = true、codegen-units = 1、opt-level = "s"、strip、panic = "abort" —— 牺牲编译时间换包体与启动表现,同时用 portable / devtools 两个 feature 区分便携模式与调试模式。

窗口是"贴边小面板"而不是普通窗口:主窗口 352×380(最小 250×300),transparent、decorations: false、skipTaskbar: true、dragDropEnabled: false,配合 window-vibrancy 做 mica/acrylic 背景。CSP 写得比较收敛:default-src 'self',脚本/样式只额外放开 'unsafe-inline',connect-src 限定 'self' ws: http: https:;资源协议只允许 $HOME/**、$APPDATA/**、$APPLOCALDATA/**、$PROFILE/**;opener:allow-open-url 只允许打开 https://github.com/**。

二、主题系统:令牌 + 单一注册表

docs/THEME-SYSTEM.md 把主题拆成两层,规则是"共享 CSS 只负责结构与中性默认值,每个主题文件拥有自己的视觉语言":

  • 注册表唯一:src/shared/config/themes.ts 是 id、多语言标签(zh/en/tw)和功能开关的唯一来源;
  • 自动装载:src/styles/themes/load.ts 自动 import themes/*.css,所以"纯 CSS 主题"新增时不需要改任何加载器代码;
  • 运行时归一:未知主题 id 回落到 DEFAULT_THEME,并在 html 与 body 上同时挂 theme-<id> 类,主窗口与 compact-preview 窗口用同一个 id;
  • 令牌规模:约 100+ 个语义变量,按 --shell-*、--toolbar-*、--title-*、--input-*、--button-*、--card-*、--panel-*、--switch-*、--segmented-*、--modal-*、--tags-*、--menu-*、--queue-* 分组;
  • 原生层单独出口:mica/acrylic 这类需要调系统 API 的效果只走 app/commands/ui_cmd.rs 的 set_theme,不把平台分支渗透进前端。

内置主题是 retro / mica / acrylic / sticky-note。文档里也诚实列了"主题债":Announcement.css、compact-mode.css、emoji.css、file-transfer.css 还没完全令牌化。

三、中继(Relay):把"发一段文本"和"同步全量历史"分开

docs/CLIPBOARD-RELAY.md 定义得很清楚:中继是显式的一次性传输,与云历史同步完全独立。

数据路径与语义:

  • 收发双方配置同一 WebDAV 账号 + 同一个 relay 共享密钥,各自绑定 Send clipboard relay / Fetch clipboard relay 两个全局快捷键(默认为空,不会偷偷占用按键);
  • Send 只上传当前 text/plain 剪贴板值:不上传历史、标签、置顶状态、使用元数据、设置或本地路径;
  • Fetch 只把最新一条合格消息写回系统剪贴板,并标记这次写入是"应用发起",因此不会进入本地历史;
  • 消息默认 10 分钟过期,协议校验收到的 TTL 不允许超过 24 小时,也会拒绝过大的时钟偏移;
  • 远端队列上限 2000 个对象,过期与超量对象在正常收发流程中做尽力而为的清理。

密码学与凭据:

  • 流量走独立的 <base-path>/relay/v1 WebDAV 命名空间;
  • 32 字节共享密钥 + XChaCha20-Poly1305 加密消息正文,附加认证数据绑定消息 ID、发送者、目标列表、媒体类型、时间戳、算法标识和 WebDAV 文件名——也就是说,元数据不能在不被发现的情况下被改名或重放;
  • ACK 文件同样加密并认证,未认证的 ACK 直接拒绝;
  • 信封里没有明文的正文哈希或其他指纹,避免"看不到内容但能比对内容"的泄漏;
  • 共享密钥存进操作系统凭据库,不通过通用设置 API 返回,也永不作为普通设置项落盘;生成密钥时只在原生写入成功后显示一次;便携模式下直接禁用中继,而不是退化成明文存 key;
  • 因为应用的端到端加密保护不了 WebDAV 凭据、对象路径和流量特征,所以中继强制要求 https:// 的 WebDAV 地址。

投递语义写得很克制:从本地视角是 at-most-once。如果剪贴板写入成功但 ACK 上传失败,收据停在 copied_pending_ack,之后的 fetch 只重试 ACK、不会再次覆盖剪贴板;损坏、被篡改、密钥不符或未认证的对象会报错,而不是静默显示成"空收件箱"。Linux 上需要可用的用户态 D-Bus Secret Service(GNOME Keyring / KWallet),否则中继 fail closed,其他功能不受影响。

四、数据与迁移

  • SQLite 仓储由 database.rs 与 infrastructure/repository/migrations.rs(855 行)共同管理,单是剪贴板仓储 clipboard_repo.rs 就有 2271 行,标签等实体拆成独立 repo,避免堆成一个巨型 DAO;
  • 迁移是版本化的(含 schema 版本与一次性迁移逻辑),采集侧有 pipeline.rs 明确划分"读取 → 规范化 → 去重/掩码 → 入库 → 事件分发"的阶段;
  • sensitive_align.rs + 设置项负责敏感预览的自动掩码——在本地就掩掉,而不是依赖 UI 层不显示。

五、工程化程度

  • 主校验命令:npm test(前端)、npm run build、npm run test:rust;
  • 发布构型 + CI 组合(Rust 静态检查、clippy、测试、前端构建)意味着"能跑"不是验收标准,"能复现地跑"才是;
  • 上游为社区 fork,README 明确要求发布二进制前先审查旧更新器端点、签名凭据与分发基础设施——这类清单化的提醒比"自行注意安全"有用得多。

六、限制

  • 自动粘贴在 Linux 依赖 xdotool(X11) / wtype(Wayland),两者都不可用时只把内容留在剪贴板(不假装粘贴成功);
  • macOS 需要辅助功能与剪贴板权限;当前构建未做 Apple 签名/公证,首次启动需手动放行;
  • 系统 OCR 依赖 Windows Runtime,仅在 Windows 可用(二维码识别是跨平台的);
  • 中继仅支持 UTF-8 文本,单条上限 64 KiB。

小结

TieZ 最值得借鉴的两点:主题是带注册表的数据层,而不是散落的样式覆盖;临时传输与历史同步必须是两条通道,并且这条临时通道从密钥存储、加密模式到失败策略都按"最坏情况可见"来设计——包括明文指纹都不留。

仓库:sakuradairong/tiez-clipboard(GPL-3.0,社区维护 fork)

搜索文章

按 / 打开搜索,Esc 关闭;多个关键词用空格分隔,搜索在本地完成。