Back to Blog

Migrating VST Manager to Rust

I'm excited to share a major shift in how VST Manager is built. At its heart, the app lives on the filesystem. It walks plugin folders on Windows, macOS, and Linux, reads binaries, and keeps a local SQLite catalog. It has to stay reliable when paths are long, permissions are odd, or a scan runs across thousands of files. That kind of work is what pushed me toward Rust for the whole application: not only a scanner binary, but the core that talks directly to the OS.

In this post I'll cover why I'm migrating, what changes technically, and how Rust fits plugin management better than the Node.js and Electron stack I used earlier.

The Decision

After spending a lot of time thinking about how the project is put together, I made the call to migrate to Rust. I looked at what actually runs on disk, what the scanner and desktop shell each own, and where bugs kept showing up. The reasons below are what convinced me. This isn't a quick pivot. The product goals stay the same; the foundation changes. Node.js and Electron got VST Manager to alpha. Rust is how I get to one cross-platform core that handles filesystem and OS work the way this app needs.

Why Move Away from the Current Stack

My earlier posts described a working core: Vue and Nuxt for the UI, Node for the backend, SQLite for storage, and Electron for desktop packaging. That stack shipped the alpha and proved the idea. It also kept surfacing limits in real-world use:

  • Filesystem access: Different APIs, path rules, and failure modes on each OS, often showing up late as runtime errors
  • The VST scanner: Started as a separate Windows executable, which made true cross-platform scanning from one codebase difficult
  • Electron overhead: Extra memory and slower startup for an app that is mostly I/O and metadata, not a heavy web runtime
  • Error handling: Partial scans, locked files, and symlink loops were harder to handle exhaustively in JavaScript

Rust goes after these problems in a direct way: one native binary per platform, explicit error paths, and solid support for system calls and file operations without a large runtime in the middle.

What "Whole Project" Means Here

The migration is layered. I'm not planning one big-bang rewrite.

Core Engine (Rust)

  • Directory traversal and plugin discovery (VST, VST3, and planned AU, AAX, and DLL paths)
  • Metadata extraction and validation against plugin binaries
  • SQLite access for the plugin database (likely via rusqlite or similar)
  • Configuration for scan paths, exclusions, and integrity rules on rescan

Desktop Shell

  • Replace Electron with a lighter native shell (for example Tauri) or a Rust UI layer, while keeping a familiar web-style UI where it still makes sense
  • Native menus, file associations, and auto-update hooks that call into the same Rust core, so scan logic does not live twice in JavaScript

Website

  • This marketing site stays on Nuxt. Only the downloadable app moves to Rust. The blog and docs stay as they are.

The goal is one language for everything that touches the OS, even if UI and scanner are not a single crate.

Rust and the Filesystem: Why It Fits VST Manager

Plugin management is mostly correct, fast filesystem work plus safe handling of untrusted binaries (plugins). Rust helps on both fronts.

Explicit Errors Instead of Silent Failure

Filesystem APIs fail often: permission denied, path not found, file in use. Rust's Result<T, E> means every read_dir, metadata, and canonicalize must be handled or passed on on purpose. A scan job can record which path failed and keep going, instead of stopping the whole batch because of one bad entry.

Paths That Match Reality on Each OS

std::path::Path and ecosystem crates (for example camino and dunce on Windows) respect platform separators, prefixes, and case rules. VST paths on Windows (C:\Program Files\...), macOS (/Library/Audio/Plug-Ins/...), and Linux (~/.vst, ~/.vst3) can share one conceptual model in code, with platform-specific branches only where the OS actually differs.

Traversal Performance and Control

Walking large trees is common for producers with huge libraries. Rust helps here with:

  • Zero-cost abstractions and no GC pauses during deep scans
  • Parallel walkers (rayon, walkdir) with clear ownership of per-directory state
  • Streaming results to the database and UI instead of loading entire directory lists into memory

For collections with tens of thousands of plugins, steady performance matters as much as raw speed.

Audio setups often use symlinks and redirected folders. In Rust I can define policy clearly: whether to follow links, how to detect cycles, and how to dedupe by canonical path. That lines up with database integrity goals from earlier development: don't overwrite good records when the same plugin shows up under two paths.

Reading Plugin Binaries Safely

Scanning means opening .dll, .vst, .vst3, bundles, and similar files. Rust's ownership model reduces use-after-free and buffer issues when parsing headers or handling format-specific logic. Together with crates for PE, Mach-O, or VST3 bundle layout, parsing stays memory-safe even when files are malformed or hostile.

Rust and Other OS Functionality

Beyond raw file I/O, VST Manager needs OS-level behavior that the alpha and roadmap already pointed at:

ConcernHow Rust Helps
Cross-platform buildsOne codebase with cfg(target_os = "...") for Windows, macOS, and Linux
Process and permissionsClear options for elevated scans or sandboxed access only where needed
Watching foldersCrates like notify to trigger rescans when install paths change
Native dialogsOpen-folder and pick-directory flows through the desktop shell
Code signing and distributionStandard native artifacts per OS, typically smaller than Electron bundles
Concurrencystd::thread or async runtimes for scan workers, with data races caught at compile time

Electron got distribution moving quickly, but it duplicated filesystem logic at the JavaScript and native boundary. Rust gives me one implementation of how we scan and persist, shared by CLI tooling and the GUI.

Technical Direction

I'm not rewriting features for the sake of it. Priorities follow the same product goals as before:

  1. Unified scanner: One Rust implementation for all platforms, replacing the Windows-only external executable I mentioned in the alpha post
  2. SQLite layer in Rust: Same schema, with stricter transactions on rescan and metadata updates
  3. Thinner desktop shell: The UI talks to Rust via IPC or FFI, without a second copy of scan rules in JavaScript
  4. Incremental rollout: Ship scanner and database first, then the UI shell, then retire the Node and Electron paths

After migration, the app side of the stack looks like this:

  • Language: Rust (2021 edition, stable toolchain)
  • Database: SQLite, embedded and local-only, with the same privacy model as today
  • UI: Web tech inside a native host, or Rust-native UI where that simplifies things (I'll decide per milestone)
  • Build: cargo plus CI for Windows, macOS, and Linux

What This Means for You

  • Alpha testers on Windows: Expect a move from the standalone scanner plus Electron setup toward a single Rust-built app in upcoming releases
  • Mac and Linux: The same scanner codebase as Windows, in line with 1.0 cross-platform goals
  • Privacy: Unchanged. Everything stays local. Rust changes how we read disks, not what leaves your machine

Looking Ahead

Migrating to Rust is an investment in robustness where VST Manager actually lives: directories, binaries, and OS quirks. Faster scans and smaller binaries are a nice bonus. The main win is fewer kinds of bugs in the code path you trust with your entire plugin library.

I'll post follow-ups on scanner layout, cross-platform path tests, and beta builds as milestones land. Thank you for following along. The alpha proved the workflow, and Rust is how I plan to harden the foundation for 1.0 and beyond.