Building LumaTorrent: a desktop app where I had to learn Rust and Tauri at the same time
LumaTorrent is a desktop torrent client I started building for legal file distribution — open-source ISOs, Internet Archive mirrors, that kind of thing. I picked the project because every torrent client I'd ever seen looked like it was from 2008 and made me nervous to click anything.
The goal: make it feel calm. Safe defaults. No mystery buttons. Diagnostics that explain themselves.
Why Tauri instead of Electron
I'll be honest, I tried Electron first. It worked. It also weighed 200 MB and used a gigabyte of RAM doing nothing. Tauri uses the OS webview instead of bundling Chromium, and the binary came out at ~12 MB. For a tool people install on their actual computer, that mattered to me.
The downside: Tauri's backend is Rust. I did not know Rust.
How I learned Rust without dying
I'm not going to pretend I'm good at Rust now. I'm not. But the borrow checker stopped being scary once I learned to listen to it. When it yelled at me, I'd paste the error into Claude and ask "explain what ownership thinks is happening here." Nine times out of ten, the AI explanation matched what the compiler was already telling me — I just couldn't parse the compiler's voice yet.
Two things that helped a lot:
- Writing the engine contracts first as TypeScript types, then porting them to Rust structs. Having the shape locked down meant the borrow checker fights were always about how, never what.
- Mock sidecars before real ones. The real torrent engine is libtorrent (C++). I built a fake Rust engine that returned canned data first, wired the whole UI to that, then swapped it for the real thing. Future me will be grateful that the UI tests don't need a real network.
The architecture I landed on
Three layers:
- UI (React + TypeScript) — knows nothing about networking, only calls Tauri commands.
- Rust local engine — orchestrates downloads, manages state, owns the database.
- C++ native sidecar — wraps libtorrent. Runs as a separate process so a libtorrent crash can't take the whole app down.
The C++ boundary is the part I'm most proud of. It took three rewrites. The first version had the Rust code calling libtorrent directly via FFI and it was a nightmare of unsafe blocks. Splitting it into a child process you talk to over a pipe is way more code, but it's honest code.
Safety features that I think matter
- Manifest-gated deletes. The app refuses to delete a file unless its path is in a manifest the engine built. So a bug in the UI can't
rm -rfyour downloads folder. - Trash, not delete. Anything removed goes to the OS trash. Recovery is one click away.
- Visible status. The diagnostics panel shows what the engine is doing, not just what it ended up with. If a peer connection failed, you see the failure, the reason, and the retry attempt.
What AI helped with, specifically
- Rust lifetime puzzles. AI is really good at "why does this need a
'ahere." A lot of my early code had'staticeverywhere because I was scared. Refactoring that with AI as a rubber-duck-that-talks-back was the fastest way I learned. - Tauri command signatures. The Tauri docs are decent but I'm a student and "decent" still means I get stuck. Pasting the doc plus my error and asking for the smallest possible working example unstuck me a lot.
- Designing the test strategy. I had a vague idea I wanted Playwright + Vitest + Cargo tests. Claude helped me draw the boundaries: Playwright for user flows, Vitest for the UI in isolation, Cargo for the Rust engine. Once it was written down, I built it.
Honest readiness status
The repo has a production gap register — a file that lists every feature that looks done but isn't. I think every portfolio project should have one of these. It keeps me honest with myself and it keeps anyone who looks at the repo honest about what they're seeing. Right now: the UI is good, the mock engine works, the real engine integration is partial. That's the truth.
What's next
Finish the native lifecycle. Replace the last mock diagnostics with real ones. Ship a v0.1 that I'd feel okay handing to a non-technical friend.
If you're a student afraid to start a Rust project: start one. The borrow checker is a really patient teacher.