NoamVC v0.3 — We deleted 3,500 lines and the app got better
Source: Dev.to
🎙️ What is NoamVC?
NoamVC is a peer‑to‑peer encrypted voice‑chat desktop app — think Discord, but your voice never touches a server. Audio travels directly between participants via WebRTC with end‑to‑end encryption.
Built with Tauri 2 (Rust) + React 19 + TypeScript. Available for macOS (ARM + Intel) and Windows.
TL;DR of the security model
- P2P audio via
RTCPeerConnection— zero server‑side processing - E2E encryption with Insertable Streams + PBKDF2 (256‑bit key, 100 K SHA‑256 iterations)
- HMAC‑SHA256 signed signaling — secret embedded in Rust binary, never touches JS
- Secure storage with IOTA Stronghold — nothing in
localStorage
First time here? Check out the previous posts in the series for the full architecture breakdown.
🆕 What’s new in v0.2.1 → v0.3.3
1. 🚪 Room Admission System (v0.3.0)
The biggest feature in this cycle. Previously, anyone with the room code could join silently. Now the room creator acts as host and must approve every join request.
New user → knock → Host sees dialog → Admit / Deny
How it works
- The first user to enter a room automatically becomes the host.
- Subsequent participants enter a “waiting” state until explicitly admitted.
- The host sees a dialog with the requester’s name and avatar, plus Admit/Deny buttons.
- Denied users get a clear message and return to the lobby.
- Fully managed at the signaling level — zero changes to the WebRTC connection flow.
This is a significant UX and security improvement. In the previous model, knowing the room code was equivalent to being inside. Now there’s a human gatekeeper.
2. 🐛 In‑App Bug Reports → Firestore (v0.2.1)
Users can report bugs without leaving the app:
type BugCategory = "crash" | "audio" | "connection" | "ui" | "other";
- Form with title, description, and category selector.
- Real‑time validation (title ≥ 3 chars, description ≥ 10 chars).
- Sent to Firestore via REST API — no heavy Firebase SDK bundled.
Lazy‑loaded: Firebase code doesn’t load until the user opens the dialog.
Security note – the Firebase API key is embedded in the Rust binary at compile time and never appears in the JavaScript bundle:
const FIREBASE_API_KEY: &str = env!("FIREBASE_API_KEY");
#[tauri::command]
fn get_firebase_api_key() -> String {
FIREBASE_API_KEY.to_string()
}
The JS side calls this Tauri command on demand, so the key only exists in the Rust binary and only flows to the frontend when needed.
3. 🗑️ Embedded Server Removal — The –3,546 LOC Delete (v0.3.1)
Previous versions bundled an embedded signaling server (Axum + Socketioxide) for local/LAN development. We nuked it entirely.
What was removed
- Axum, Socketioxide, tower, and all related Rust dependencies from the desktop app.
- ‑3,546 lines of code.
What replaced it
- The signaling server is now a standalone Rust service (Axum + Socketioxide) deployed on Railway. Same tech, separate repo, clean boundary.
Why
- The embedded server added ~30 s of extra compile time.
- It confused developers about which server was active.
- Nobody used it for LAN in practice.
- Removing it shrinks the binary and reduces the attack surface.
“The best code is the code you don’t have.” — Someone wise
4. 🔧 Duplicate Peers Fix (v0.3.1)
A subtle race condition could cause the same peer to appear twice in the participant list during rapid disconnect/reconnect cycles. The fix adds proper peer‑identity tracking in the WebRTC hook.
5. 🔒 CSP Update for Firestore (v0.3.2)
After adding bug reports, Tauri’s Content Security Policy silently blocked requests to Firestore. The fix adds the required source:
"connect-src": "... https://firestore.googleapis.com"
Small change, but a reminder: Tauri’s CSP is strict by default (which is good), and every new external endpoint requires explicit allow‑listing.
6. 🏗️ The CI/CD Bug That Took 3 Releases to Fix (v0.3.2 → v0.3.3)
The build.rs script loaded env vars from .env for local builds, but in GitHub Actions the variables were only in the OS environment and weren’t forwarded to rustc.
The error
error: environment variable `FIREBASE_API_KEY` not defined at compile time
--> src\desktop.rs:11:32
The fix – explicitly forward OS env vars to the Rust compiler in build.rs:
for key in keys {
if let Ok(val) = std::env::var(key) {
println!("cargo:rustc-env={}={}", key, val);
}
}
And add the secret to the workflow:
env:
FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }}
Key takeaway for Tauri/Rust devs
env!() reads variables that Cargo knows about, not the shell environment directly. If your CI sets env vars in the usual way, you need cargo:rustc-env in build.rs to bridge that gap. This isn’t documented prominently and bit us hard.
📊 Numbers for this cycle
| Metric | Value |
|---|---|
| Commits | 112 |
| Pull Requests | 27 |
| Issues Closed | 45 |
| Downloads (macOS) | 3,842 |
| Downloads (Windows) | 2,917 |
| Active Users | 1,274 |
| Bug Reports Submitted | 38 |
| Average Session Length | 42 min |
All data collected from the app’s telemetry (opt‑in) and GitHub statistics.
📊 Summary
Files changed: 51
Lines added: ~1,222
Lines deleted: ~4,768
Net: ‑3,546 lines ✂️
We deleted 3.9× more code than we wrote – the best kind of release.
🛠️ Stack
| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript 5.9, Vite 7 |
| Styles | Tailwind CSS 4, shadcn/ui |
| Desktop | Tauri 2 (Rust backend) |
| Signaling | Rust (Axum + Socketioxide) → Railway |
| Encryption | Insertable Streams + PBKDF2, HMAC‑SHA256 |
| Storage | IOTA Stronghold |
| CI/CD | GitHub Actions → auto‑draft release |
| Bug Reports | Firestore REST API |
🔜 What’s Next
- Push‑to‑talk – alternative mode for noisy environments.
- Per‑peer connection quality indicators – RTT, packet loss, jitter visualized per participant.
- BLE data transmission – exploring Bluetooth Low Energy as an alternative signaling channel for ultra‑local, serverless room setup between nearby devices.
- Linux support – AppImage / .deb packages.
🔗 Links
🌐 Landing:
NoamVC is under active development. If you’re working with WebRTC, Tauri, or applied cryptography — or just want a voice chat that doesn’t route your audio through someone else’s server — give it a look. Feedback and contributions are welcome.
