NoamVC v0.3 — 我们删除了 3,500 行,应用变得更好
Source: Dev.to
🎙️ 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/CD | GitHub Actions → auto‑draft release |
| 错误报告 | Firestore REST API |
🔜 接下来
- Push‑to‑talk – 噪声环境下的替代模式。
- Per‑peer connection quality indicators – 每位参与者的 RTT、丢包、抖动可视化。
- BLE data transmission – 探索蓝牙低功耗作为近距离、无服务器房间设置的替代信令通道。
- Linux support – AppImage / .deb 包。
🔗 Links
🌐 Landing:
NoamVC 正在积极开发中。如果你在使用 WebRTC、Tauri 或应用密码学——或者只是想要一个不通过他人服务器转发音频的语音聊天——请看看吧。欢迎反馈和贡献。
