<?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>DEV Community: Pranav Kishan</title>
    <description>The latest articles on DEV Community by Pranav Kishan (@pranav_kishan_).</description>
    <link>https://dev.to/pranav_kishan_</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3669047%2Fe14eda30-1493-460f-bccf-2742af9d8310.jpg</url>
      <title>DEV Community: Pranav Kishan</title>
      <link>https://dev.to/pranav_kishan_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pranav_kishan_"/>
    <language>en</language>
    <item>
      <title>Designing a Zero-Knowledge Persistent Personal Information Manager with Client-Side Encryption</title>
      <dc:creator>Pranav Kishan</dc:creator>
      <pubDate>Sat, 20 Dec 2025 16:25:10 +0000</pubDate>
      <link>https://dev.to/pranav_kishan_/designing-a-zero-trust-personal-information-manager-with-client-side-encryption-4pb5</link>
      <guid>https://dev.to/pranav_kishan_/designing-a-zero-trust-personal-information-manager-with-client-side-encryption-4pb5</guid>
      <description>&lt;p&gt;I am a B.Tech Computer Science undergraduate at &lt;strong&gt;Amrita Vishwa Vidyapeetham&lt;/strong&gt; who enjoys building privacy-focused systems and learning by deploying real software end-to-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Motivation
&lt;/h2&gt;

&lt;p&gt;The inception of &lt;strong&gt;InfoStuffs&lt;/strong&gt; was not driven by a desire to build just another productivity application. It started with a very specific user requirement from my sister. She needed a digital space to organize personal documents and sensitive notes but refused to use standard cloud services such as Google Keep or Notion.&lt;/p&gt;

&lt;p&gt;Her constraint was simple but technically demanding: &lt;strong&gt;she wanted the convenience of the cloud without trusting the cloud provider with her plaintext data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This challenge became the foundation of InfoStuffs. My goal shifted from building a simple web application to architecting a &lt;strong&gt;Zero-Knowledge Information Management System&lt;/strong&gt; that prioritizes privacy by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Statement
&lt;/h2&gt;

&lt;p&gt;Modern productivity tools generally fall into two categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Convenient SaaS:&lt;/strong&gt; (e.g., Notion, Keep) store user data in plaintext or use server-managed keys, leaving data vulnerable to internal leaks or database breaches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-Hosted:&lt;/strong&gt; (e.g., Obsidian, Nextcloud) offer strong privacy but are difficult to access and maintain across multiple devices.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;InfoStuffs bridges this gap. The system had to be secure enough that a &lt;strong&gt;complete server-side compromise would yield nothing but garbage data&lt;/strong&gt;, yet accessible via a standard web browser on any device.&lt;/p&gt;

&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;

&lt;p&gt;To satisfy these constraints, InfoStuffs uses a decoupled, cloud-native architecture with clearly separated responsibilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8eu2329chvc21mlh7o61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8eu2329chvc21mlh7o61.png" alt="Architecture diagram for infostuffs" width="800" height="871"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React (Vite) with Material UI. Responsibilities include UI rendering and &lt;strong&gt;client-side cryptographic operations&lt;/strong&gt; (encryption/decryption).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js and Express, utilizing a &lt;strong&gt;Serverless Architecture&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Development:&lt;/strong&gt; Runs as a &lt;strong&gt;fully dockerized monolithic container&lt;/strong&gt;, ensuring a consistent development environment that mirrors production dependencies.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Production Deployment:&lt;/strong&gt; Deployed to &lt;strong&gt;Vercel&lt;/strong&gt; as &lt;strong&gt;stateless Serverless Functions&lt;/strong&gt;, allowing the API to scale to zero when idle (cost-efficient) while maintaining a single Express codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Database:&lt;/strong&gt; MongoDB Atlas for storing encrypted metadata and ciphertext.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; Clerk. Delegating identity management reduced the attack surface for auth flows (MFA, session management).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; Supabase Storage, used strictly for isolating binary objects (images and PDFs) via obfuscated paths and client-side encrypted blobs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security by Design: The Zero-Knowledge Vault
&lt;/h2&gt;

&lt;p&gt;Security was not an optional feature; it was the primary architectural constraint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwcpmbhd9mxxeeiyn5n7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwcpmbhd9mxxeeiyn5n7r.png" alt="Sequence diagram flow" width="800" height="771"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Problem with Static Keys
&lt;/h3&gt;

&lt;p&gt;In my initial design, I used a static encryption key stored in the server's environment variables (&lt;code&gt;VITE_SECRET_KEY&lt;/code&gt;). I quickly realized this was a critical flaw. If an attacker or a compromised hosting environment were to expose environment variables, they could decrypt everyone's data. The key was &lt;strong&gt;visible&lt;/strong&gt;, which violated the core concept of Zero-Knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Solution: User-Derived Cryptography
&lt;/h3&gt;

&lt;p&gt;To fix this, I removed the static key entirely. I implemented &lt;strong&gt;PBKDF2 (Password-Based Key Derivation Function 2)&lt;/strong&gt; on the client side.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a user logs in, they enter a &lt;strong&gt;Vault Password&lt;/strong&gt;. This password is never transmitted or stored and exists only transiently in the client’s memory.&lt;/li&gt;
&lt;li&gt;The browser runs PBKDF2 to derive a temporary 256-bit AES-GCM key in memory.&lt;/li&gt;
&lt;li&gt;This key is used to encrypt notes, titles, and file paths &lt;em&gt;before&lt;/em&gt; the network request is even formed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The server only ever sees (and stores) ciphertext. If the database administrator (me) were to look at the data, I would see nothing but unreadable strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Ephemeral Access to Media
&lt;/h3&gt;

&lt;p&gt;For file storage, I avoided public buckets entirely.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted Paths:&lt;/strong&gt; The database stores an &lt;em&gt;encrypted&lt;/em&gt; string pointing to the file path (e.g., "user/123/image.jpg" is encrypted).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Obfuscated Storage:&lt;/strong&gt; Uploaded files generate randomized UUID storage paths, so the storage provider never sees the original filenames.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-Demand Access:&lt;/strong&gt; When a user unlocks their vault, the client decrypts the path and requests a &lt;strong&gt;Signed URL&lt;/strong&gt; from Supabase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-Limited:&lt;/strong&gt; Signed URLs are valid for one hour. This prevents "link sharing" leaks and ensures that even if a URL is intercepted, it becomes useless quickly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Zero-Knowledge Offline Vault (PWA)
&lt;/h3&gt;

&lt;p&gt;To make the app truly resilient, I engineered a complete offline mode without sacrificing the encryption model.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Offline Caching:&lt;/strong&gt; During an online session, raw encrypted ciphertexts of the user's notes are saved into IndexedDB; plaintext is never stored on disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Offline Detection:&lt;/strong&gt; A module-level event interceptor and a fast connectivity probe immediately detect offline states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth Bypass &amp;amp; Key Derivation:&lt;/strong&gt; If offline, Clerk initialization is bypassed entirely. The app regenerates the AES-GCM key dynamically using the Vault Password and the cached, plaintext Clerk User ID as the PBKDF2 salt.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Infrastructure Evolution: Solving the Cost Problem
&lt;/h2&gt;

&lt;p&gt;One of the most valuable learning experiences came from adapting the infrastructure to real-world cost constraints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: The "Enterprise" Trap (GCP)
&lt;/h3&gt;

&lt;p&gt;My initial deployment used Google Cloud Platform with &lt;strong&gt;Cloud Run&lt;/strong&gt; and &lt;strong&gt;Cloud Build&lt;/strong&gt;. While this was an industry-standard "Enterprise" setup, it introduced significant problems for a personal project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High Costs:&lt;/strong&gt; Paying for load balancers, container registry storage, and compute time quickly added up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; Managing IAM roles and build triggers for a simple app was overkill.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 2: The "Serverless Monolith" (Vercel)
&lt;/h3&gt;

&lt;p&gt;To solve the deployment cost problem, I re-architected the stack to run for &lt;strong&gt;$0/month&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local Development (Dockerized):&lt;/strong&gt; I kept the convenience of a containerized environment. A single &lt;code&gt;docker-compose up&lt;/code&gt; spins up the Frontend, Backend, and Database services. This ensures that the development environment is isolated and reproducible on any machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Deployment (Serverless):&lt;/strong&gt; Instead of paying for a permanently running container (which costs money even when idle), I refactored the Express application to run on &lt;strong&gt;Vercel Serverless Functions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Result:&lt;/strong&gt; I effectively have a "Serverless Monolith." I develop it like a standard monolithic app (easy to debug, easy to run locally in Docker) but deploy it as distributed functions. This gives me the best of both worlds: &lt;strong&gt;Zero infrastructure management&lt;/strong&gt; and &lt;strong&gt;Zero cost&lt;/strong&gt; for personal use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I intentionally avoided microservices, as the domain does not yet justify multiple bounded contexts, and premature service decomposition would increase complexity and attack surface without tangible benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Challenges &amp;amp; Performance Optimizations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Environment Variable Visibility&lt;/strong&gt;&lt;br&gt;
Relying on &lt;code&gt;.env&lt;/code&gt; files for security was a mistake. The migration to user-derived keys solved this, but it required handling edge cases like "Lost Passwords." Since I no longer had the key, I had to implement a &lt;strong&gt;"Nuclear Reset"&lt;/strong&gt; feature. This allows users to wipe their unrecoverable data and start fresh, prioritizing security over recovery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Client-Side Decryption Bottlenecks&lt;/strong&gt;&lt;br&gt;
Rendering a vault full of encrypted images and text placed heavy load on the UI thread. I implemented two major optimizations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Intersection Observer Deferral:&lt;/strong&gt; Encrypted image attachments are only requested, downloaded, and decrypted when they scroll into the viewport.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Performance Base64 Parser:&lt;/strong&gt; I replaced slow native &lt;code&gt;Uint8Array.from()&lt;/code&gt; callbacks with custom loops, significantly reducing CPU overhead during decryption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. The Docker vs. Serverless Routing Mismatch&lt;/strong&gt;&lt;br&gt;
One of the most complex challenges was reconciling the difference between a running Docker container and Vercel's file-system routing. In my local Docker container, Express handled all routing internally. However, when deployed to Vercel, requests to sub-paths (like &lt;code&gt;/api/info/nuke&lt;/code&gt;) were hitting Vercel's 404 handler before reaching my Express app. I resolved this by creating a Vercel-compatible entry point (&lt;code&gt;api/info.js&lt;/code&gt;) and a rewrite rule that pipes all sub-route traffic directly into the Express instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Production OAuth Strictness&lt;/strong&gt;&lt;br&gt;
A critical UI bug emerged in production: users completing Google Sign-In were dumped onto a blank white screen at &lt;code&gt;/sso-callback&lt;/code&gt;. My SPA lacked a dedicated route to handle strict server-side redirects enforced in production. I architected a dedicated &lt;strong&gt;"OAuth Landing Pad"&lt;/strong&gt; component that intercepts the OAuth token, displays a branded loading state, and seamlessly completes the handshake before forwarding the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Roadmap
&lt;/h2&gt;

&lt;p&gt;While InfoStuffs is fully functional, I plan to explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redis Caching:&lt;/strong&gt; To reduce database reads for frequently accessed (encrypted) metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React Native Mobile App:&lt;/strong&gt; Wrapping the existing logic to allow biometric vault unlocking (FaceID) instead of typing the password.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;InfoStuffs is more than a note-taking application. It is a practical exploration of &lt;strong&gt;Zero-Knowledge Engineering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By addressing the real-world problems of &lt;strong&gt;data visibility&lt;/strong&gt; and &lt;strong&gt;cloud costs&lt;/strong&gt;, I built a system where privacy is enforced by mathematics, not by policy. It satisfies a real user need while serving as a valuable learning experience in full-stack security and DevOps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/Destroyer795/InfoStuffs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Live Deployment:&lt;/strong&gt; &lt;a href="https://info-stuffs.vercel.app" rel="noopener noreferrer"&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The live deployment requires authentication and a vault password. The core security properties are enforced client-side and are best understood via the architecture discussion above.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>cloud</category>
      <category>security</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
