<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Varinder Singh</title>
    <description>The latest articles on Forem by Varinder Singh (@varinder_singh_c541dcb059).</description>
    <link>https://forem.com/varinder_singh_c541dcb059</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3857382%2F45ac6bee-e7a4-434e-9aef-1d6744cb6893.jpg</url>
      <title>Forem: Varinder Singh</title>
      <link>https://forem.com/varinder_singh_c541dcb059</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/varinder_singh_c541dcb059"/>
    <language>en</language>
    <item>
      <title>Building an encrypted vault with Tauri + Rust — architecture decisions and trade-offs</title>
      <dc:creator>Varinder Singh</dc:creator>
      <pubDate>Thu, 02 Apr 2026 12:43:52 +0000</pubDate>
      <link>https://forem.com/varinder_singh_c541dcb059/building-an-encrypted-vault-with-tauri-rust-architecture-decisions-and-trade-offs-519p</link>
      <guid>https://forem.com/varinder_singh_c541dcb059/building-an-encrypted-vault-with-tauri-rust-architecture-decisions-and-trade-offs-519p</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Tauri over Electron
&lt;/h2&gt;

&lt;p&gt;The numbers tell the story:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle size: ~5MB vs ~200MB&lt;/li&gt;
&lt;li&gt;Memory at idle: ~40MB vs ~150MB&lt;/li&gt;
&lt;li&gt;Startup time: &amp;lt;1s vs 2-3s&lt;/li&gt;
&lt;li&gt;Backend: Rust (native speed) vs Node.js&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Worth it? Absolutely. Users notice the speed difference immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encryption Architecture
&lt;/h2&gt;

&lt;p&gt;The key hierarchy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Master password&lt;/strong&gt; → Argon2id (64MB memory, 3 iterations, 32-byte salt) → &lt;strong&gt;Master key&lt;/strong&gt; (256-bit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Master key&lt;/strong&gt; encrypts/decrypts individual secret blocks&lt;/li&gt;
&lt;li&gt;Each secret block: AES-256-GCM with unique 96-bit nonce&lt;/li&gt;
&lt;li&gt;Master key stored in &lt;code&gt;vault.key&lt;/code&gt; (encrypted with itself — bootstrapped from password)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why per-block encryption instead of encrypting the whole file?&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search Engine: tantivy
&lt;/h2&gt;

&lt;p&gt;I considered three options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SQLite FTS5&lt;/strong&gt;: Simple, embedded, works everywhere. Used this on mobile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tantivy&lt;/strong&gt;: Rust-native, Lucene-like, field-level boosting. Used this on desktop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom inverted index&lt;/strong&gt;: Maximum control, maximum effort. Rejected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;tantivy won for desktop because:&lt;/p&gt;

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

&lt;p&gt;Critical design decision: &lt;strong&gt;secret values are never indexed&lt;/strong&gt;. 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MCP Server
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;How it works:&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  Markdown-First: Why .md Files, Not a Database
&lt;/h2&gt;

&lt;p&gt;The vault is literally a folder:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Claspt/
  general/
    my-note.md
  credentials/
    aws-setup.md
  .securenotes/
    config.json
    vault.key
    index/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Each page is a standalone .md file with YAML frontmatter. This means:&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Free forever on desktop. No account required.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://claspt.app" rel="noopener noreferrer"&gt;https://claspt.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Security model: &lt;a href="https://claspt.app/security/" rel="noopener noreferrer"&gt;https://claspt.app/security/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://docs.claspt.app" rel="noopener noreferrer"&gt;https://docs.claspt.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code is Rust + TypeScript. If you're building with Tauri, happy to discuss architecture decisions in the comments.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>tauri</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I built an encrypted vault where markdown notes and passwords live together</title>
      <dc:creator>Varinder Singh</dc:creator>
      <pubDate>Thu, 02 Apr 2026 10:21:55 +0000</pubDate>
      <link>https://forem.com/varinder_singh_c541dcb059/i-built-an-encrypted-vault-where-markdown-notes-and-passwords-live-together-32lo</link>
      <guid>https://forem.com/varinder_singh_c541dcb059/i-built-an-encrypted-vault-where-markdown-notes-and-passwords-live-together-32lo</guid>
      <description>&lt;h2&gt;
  
  
  Use for a longer-form technical post covering:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why Tauri over Electron (bundle size, memory, Rust backend)&lt;/li&gt;
&lt;li&gt;The encryption architecture (key hierarchy, per-block encryption, Argon2id parameters)&lt;/li&gt;
&lt;li&gt;How the MCP server works for AI tool integration&lt;/li&gt;
&lt;li&gt;The markdown-first approach&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Link back to &lt;a href="https://claspt.app" rel="noopener noreferrer"&gt;https://claspt.app&lt;/a&gt; and &lt;a href="https://claspt.app/security/" rel="noopener noreferrer"&gt;https://claspt.app/security/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
