<?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: Pradeep Singh</title>
    <description>The latest articles on DEV Community by Pradeep Singh (@pradeepsng30).</description>
    <link>https://dev.to/pradeepsng30</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%2F4210036%2Fd563dc29-79dc-437d-a60b-a3f3b9097f1c.png</url>
      <title>DEV Community: Pradeep Singh</title>
      <link>https://dev.to/pradeepsng30</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pradeepsng30"/>
    <language>en</language>
    <item>
      <title>How I built a zero-knowledge, self-destructing secret sharing tool</title>
      <dc:creator>Pradeep Singh</dc:creator>
      <pubDate>Tue, 27 Oct 2026 06:15:03 +0000</pubDate>
      <link>https://dev.to/pradeepsng30/how-i-built-a-zero-knowledge-self-destructing-secret-sharing-tool-2kj8</link>
      <guid>https://dev.to/pradeepsng30/how-i-built-a-zero-knowledge-self-destructing-secret-sharing-tool-2kj8</guid>
      <description>&lt;p&gt;Most of us have pasted a password or API key into Slack or email because it was the quickest option. That message then sits in search indexes, backups, and inboxes, often long after anyone needs it.&lt;/p&gt;

&lt;p&gt;Privnote and OneTimeSecret exist for this. I wanted one where the server can't read the secret at all, so I built &lt;a href="https://vanishshare.app" rel="noopener noreferrer"&gt;VanishShare&lt;/a&gt;: encrypted, self-destructing notes and small files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The key never leaves the browser
&lt;/h2&gt;

&lt;p&gt;When you create a secret, the browser does the work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a random 256-bit AES key with the Web Crypto API&lt;/li&gt;
&lt;li&gt;Encrypt the note or file with AES-256-GCM&lt;/li&gt;
&lt;li&gt;Send only the ciphertext to the server&lt;/li&gt;
&lt;li&gt;Put the key in the link's hash fragment (&lt;code&gt;#...&lt;/code&gt;)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Generate a key and encrypt in the browser&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AES-GCM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encrypt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decrypt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRandomValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AES-GCM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why the hash fragment matters
&lt;/h2&gt;

&lt;p&gt;Browsers leave everything after &lt;code&gt;#&lt;/code&gt; out of HTTP requests. A link like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://vanishshare.app/s/abc123#&amp;lt;decryption-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;sends only &lt;code&gt;/s/abc123&lt;/code&gt; to the server. The key stays on the sender's and recipient's devices, and the server stores ciphertext it can't decrypt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-destruction
&lt;/h2&gt;

&lt;p&gt;Each secret expires after 10 minutes, 1 day, 1 week or 1 month, and you can cap it at 1, 5, 10 or 20 views or leave views unlimited. Once a secret expires or hits its limit, the server deletes the encrypted payload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional protections
&lt;/h2&gt;

&lt;p&gt;You can add a passphrase and share it through a different channel, so a leaked link alone isn't enough. You can also require recipients to verify their email with a one-time code. Files up to 200KB work too (certificates, private keys, small documents) and get the same browser-side encryption. There's also an API and CLI for scripting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;The server still sees metadata, such as when a secret was created, its size and when it was opened. Like any web app, VanishShare relies on you trusting the JavaScript the server delivers. It's meant for temporary secrets. Long-term storage belongs in a password manager.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live: &lt;a href="https://vanishshare.app" rel="noopener noreferrer"&gt;vanishshare.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Algorithm Review Code: &lt;a href="https://github.com/pradeepsng30/vanishshare-sec" rel="noopener noreferrer"&gt;github.com/pradeepsng30/vanishshare-sec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;API docs: &lt;a href="https://vanishshare.app/docs/api" rel="noopener noreferrer"&gt;vanishshare.app/docs/api&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback on the threat model and the crypto design is especially welcome. If you find a flaw, please open an issue.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
