DEV Community

Varinder Singh
Varinder Singh

Posted on

Building an encrypted vault with Tauri + Rust — architecture decisions and trade-offs

I recently launched Claspt, a desktop app that combines markdown note-taking with AES-256 encrypted secret storage. This post covers the technical decisions behind it — what worked, what didn't, and what I'd do differently.

Why Tauri over Electron

The numbers tell the story:

  • Bundle size: ~5MB vs ~200MB
  • Memory at idle: ~40MB vs ~150MB
  • Startup time: <1s vs 2-3s
  • Backend: Rust (native speed) vs Node.js

Tauri 2.x gives you a Rust backend with a WebView frontend. The frontend is React + TypeScript + Tailwind. The backend handles everything performance-sensitive: encryption, file I/O, full-text search, Git operations.

The trade-off: WebView rendering varies slightly across platforms (especially font rendering and scrollbar behavior). And the ecosystem is younger — fewer plugins, smaller community, more DIY.

Worth it? Absolutely. Users notice the speed difference immediately.

Encryption Architecture

The key hierarchy:

  1. Master password → Argon2id (64MB memory, 3 iterations, 32-byte salt) → Master key (256-bit)
  2. Master key encrypts/decrypts individual secret blocks
  3. Each secret block: AES-256-GCM with unique 96-bit nonce
  4. Master key stored in vault.key (encrypted with itself — bootstrapped from password)

Why per-block encryption instead of encrypting the whole file?

  • Markdown content stays plaintext → readable in any editor, searchable, git-diffable
  • Only the sensitive parts are encrypted
  • Each block has its own nonce → compromising one block doesn't help with others
  • Labels stay visible for organization → :::secret[AWS Access Key] is plaintext, the value inside is ciphertext

The Argon2id parameters (64MB, 3 iterations) are based on OWASP recommendations. On a modern machine, key derivation takes ~200ms — imperceptible on unlock, painful for brute force.

Search Engine: tantivy

I considered three options:

  1. SQLite FTS5: Simple, embedded, works everywhere. Used this on mobile.
  2. tantivy: Rust-native, Lucene-like, field-level boosting. Used this on desktop.
  3. Custom inverted index: Maximum control, maximum effort. Rejected.

tantivy won for desktop because:

  • Field-level boosting: title (3x), tags (2x), secret labels (2x), content (1x)
  • Sub-100ms search across thousands of documents
  • Rust-native = no FFI overhead, compiles with the rest of the backend
  • Incremental indexing on save

Critical design decision: secret values are never indexed. The search index only contains titles, tags, labels, and non-secret content. Even if someone extracts the search index files, no secret values are exposed.

The MCP Server

This is the feature I'm most excited about. Claspt exposes a Model Context Protocol (MCP) server on localhost. AI tools like Claude Code, Cursor, and Windsurf can connect to it and use your vault as persistent memory.

How it works:

  • MCP server runs on a configurable local port
  • Two-tier token auth: notes-only token (read/write notes) and secrets token (can also read secrets)
  • AI tools connect via the MCP protocol and get tools like read_page, search, create_page
  • Your AI assistant can store and retrieve information across sessions

Use case: "Remember this deployment procedure for next time" → stored as a note in your vault, available in every future conversation.

Markdown-First: Why .md Files, Not a Database

The vault is literally a folder:

~/Claspt/
  general/
    my-note.md
  credentials/
    aws-setup.md
  .securenotes/
    config.json
    vault.key
    index/
Enter fullscreen mode Exit fullscreen mode

Each page is a standalone .md file with YAML frontmatter. This means:

  • No vendor lock-in: Your notes are readable without Claspt
  • Git-friendly: Every save auto-commits. Full version history for free.
  • Backup-friendly: It's a folder. rsync, Time Machine, whatever you use.
  • Sync-friendly: Git remotes, Google Drive, Dropbox — anything that syncs folders works.

The trade-off: no relational queries, no structured data beyond frontmatter. But for a personal vault, folder + search is enough.

What I'd Do Differently

  1. Start with @dnd-kit from day one. HTML5 drag-and-drop doesn't work in Tauri's WebView. Wasted time debugging before switching to pointer events.
  2. Design the config system earlier. Settings grew organically and the config struct is now 30+ fields. Should have used a more modular approach.
  3. Write the browser extension in parallel. It's the most-requested feature and designing it after the desktop app means some architectural retrofitting.

Try It

Free forever on desktop. No account required.

The code is Rust + TypeScript. If you're building with Tauri, happy to discuss architecture decisions in the comments.

Top comments (0)