NoamVC v0.3 — 我们删除了 3,500 行,应用变得更好

发布: (2026年2月18日 GMT+8 04:34)
8 分钟阅读
原文: Dev.to

Source: Dev.to

NoamVC 截图

🎙️ NoamVC 是什么?

NoamVC 是一款 点对点 加密语音聊天桌面应用——可以把它想象成 Discord,但你的声音永不经过服务器。音频通过 WebRTC 直接在参与者之间传输,并使用端到端加密。

使用 Tauri 2(Rust) + React 19 + TypeScript 构建。支持 macOS(ARM + Intel)和 Windows。

安全模型 TL;DR

  • 通过 RTCPeerConnection 实现 P2P 音频 —— 零服务器端处理
  • 使用 Insertable Streams + PBKDF2 的端到端加密(256 位密钥,100 K 次 SHA‑256 迭代)
  • HMAC‑SHA256 签名的信令 —— 密钥嵌入 Rust 二进制文件,永不触及 JS
  • 使用 IOTA Stronghold 的安全存储 —— localStorage 中没有任何数据

第一次来到这里?请查看系列中的前几篇文章,了解完整的架构解析。

🆕 v0.2.1 → v0.3.3 新增内容

1. 🚪 房间准入系统 (v0.3.0)

本轮最大的功能。之前,只要知道房间码就可以悄悄加入。现在 房间创建者 充当主持人,必须批准每一次加入请求。

New user → knock → Host sees dialog → Admit / Deny

工作原理

  • 第一个进入房间的用户会自动成为 主持人
  • 后续参与者会进入“等待”状态,直到被明确批准。
  • 主持人会看到一个对话框,显示请求者的姓名和头像,以及 批准/拒绝 按钮。
  • 被拒绝的用户会收到明确提示并返回大厅。
  • 完全在信令层管理 —— 对 WebRTC 连接流程零改动

这是一次重要的用户体验和安全性提升。之前的模型中,知道房间码等同于已经在内部。现在多了一个人工门卫。

2. 🐛 应用内 Bug 报告 → Firestore (v0.2.1)

用户可以在不离开应用的情况下报告 Bug:

type BugCategory = "crash" | "audio" | "connection" | "ui" | "other";
  • 表单包含标题、描述和分类选择器。
  • 实时校验(标题 ≥ 3 字符,描述 ≥ 10 字符)。
  • 通过 REST API 发送至 Firestore —— 不需要捆绑庞大的 Firebase SDK。

懒加载:Firebase 代码只有在用户打开对话框时才会加载。

安全说明 – Firebase API 密钥在编译时嵌入 Rust 二进制,永远不会出现在 JavaScript 包中:

const FIREBASE_API_KEY: &str = env!("FIREBASE_API_KEY");

#[tauri::command]
fn get_firebase_api_key() -> String {
    FIREBASE_API_KEY.to_string()
}

JS 端按需调用此 Tauri 命令,因此密钥仅存在于 Rust 二进制中,并且只在需要时流向前端。

3. 🗑️ 移除嵌入式服务器 — 删除 -3,546 行代码 (v0.3.1)

之前的版本捆绑了一个用于本地/LAN 开发的嵌入式信令服务器(Axum + Socketioxide)。我们 彻底砍掉了它

移除内容

  • 从桌面应用中删除 Axum、Socketioxide、tower 以及所有相关的 Rust 依赖。
  • ‑3,546 行代码。

替代方案

  • 信令服务器现在是一个 独立的 Rust 服务(Axum + Socketioxide),部署在 Railway。技术相同,仓库分离,边界清晰。

原因

  • 嵌入式服务器额外增加约 30 秒的编译时间。
  • 开发者常常混淆到底使用的是哪个服务器。
  • 实际上几乎没有人在 LAN 环境下使用它。
  • 移除后二进制体积更小,攻击面更低。

“最好的代码是你根本没有的代码。” — 某位智者

4. 🔧 重复 Peer 修复 (v0.3.1)

一次细微的竞争条件可能导致同一 Peer 在快速断连/重连周期中出现两次。修复在 WebRTC Hook 中加入了正确的 Peer 身份追踪。

5. 🔒 Firestore 的 CSP 更新 (v0.3.2)

在加入 Bug 报告功能后,Tauri 的内容安全策略(CSP)默认阻止了对 Firestore 的请求。修复在 CSP 中加入了所需的来源:

"connect-src": "... https://firestore.googleapis.com"

小改动,却提醒我们:Tauri 的 CSP 默认非常严格(这很好),每新增的外部端点都必须显式列入白名单。

6. 🏗️ 经过 3 次发布才修复的 CI/CD Bug (v0.3.2 → v0.3.3)

build.rs 脚本从 .env 加载环境变量用于本地构建,但在 GitHub Actions 中,这些变量仅存在于 OS 环境中,未被转发给 rustc

错误信息

error: environment variable `FIREBASE_API_KEY` not defined at compile time
 --> src\desktop.rs:11:32

修复方法 – 在 build.rs 中显式将 OS 环境变量转发给 Rust 编译器:

for key in keys {
    if let Ok(val) = std::env::var(key) {
        println!("cargo:rustc-env={}={}", key, val);
    }
}

并在工作流中添加对应的 secret:

env:
  FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }}

对 Tauri/Rust 开发者的关键提示

env!() 读取的是 Cargo 已知的变量,而不是 shell 环境变量。

直接访问环境变量。如果你的 CI 以常规方式设置环境变量,你需要在 build.rs 中使用 cargo:rustc-env 来弥合这一差距。这一点并没有被显著记录,结果让我们吃了大亏。

📊 本周期数据

指标数值
提交次数112
拉取请求27
已关闭问题45
下载量(macOS)3,842
下载量(Windows)2,917
活跃用户1,274
提交的错误报告38
平均会话时长42 min

所有数据均来自应用的遥测(自愿加入)和 GitHub 统计。

📊 摘要

更改的文件: 51
新增行数: ~1,222
删除行数: ~4,768

净变化: ‑3,546 行 ✂️

我们删除的代码量是编写的 3.9×,这是最好的发布方式。

🛠️ 技术栈

层级技术
前端React 19, TypeScript 5.9, Vite 7
样式Tailwind CSS 4, shadcn/ui
桌面Tauri 2 (Rust backend)
信令Rust (Axum + Socketioxide) → Railway
加密Insertable Streams + PBKDF2, HMAC‑SHA256
存储IOTA Stronghold
CI/CDGitHub Actions → auto‑draft release
错误报告Firestore REST API

🔜 接下来

  • Push‑to‑talk – 噪声环境下的替代模式。
  • Per‑peer connection quality indicators – 每位参与者的 RTT、丢包、抖动可视化。
  • BLE data transmission – 探索蓝牙低功耗作为近距离、无服务器房间设置的替代信令通道。
  • Linux support – AppImage / .deb 包。

🌐 Landing:

NoamVC 正在积极开发中。如果你在使用 WebRTC、Tauri 或应用密码学——或者只是想要一个不通过他人服务器转发音频的语音聊天——请看看吧。欢迎反馈和贡献。

0 浏览
Back to Blog

相关文章

阅读更多 »