<?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: Mahdyar</title>
    <description>The latest articles on DEV Community by Mahdyar (@mahdyarmonfared).</description>
    <link>https://dev.to/mahdyarmonfared</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%2F4136512%2Ff165d391-1c5a-4c1e-9963-c523ae778e86.jpg</url>
      <title>DEV Community: Mahdyar</title>
      <link>https://dev.to/mahdyarmonfared</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mahdyarmonfared"/>
    <language>en</language>
    <item>
      <title>How I Built Secret-Scrub: A Zero-Dependency Pre-Commit Secret Scanner with Shannon Entropy Analysis</title>
      <dc:creator>Mahdyar</dc:creator>
      <pubDate>Mon, 21 Sep 2026 23:20:53 +0000</pubDate>
      <link>https://dev.to/mahdyarmonfared/how-i-built-secret-scrub-a-zero-dependency-pre-commit-secret-scanner-with-shannon-entropy-analysis-5ea2</link>
      <guid>https://dev.to/mahdyarmonfared/how-i-built-secret-scrub-a-zero-dependency-pre-commit-secret-scanner-with-shannon-entropy-analysis-5ea2</guid>
      <description>&lt;p&gt;Every developer has had that split-second wave of cold panic:&lt;/p&gt;

&lt;p&gt;You type &lt;code&gt;git commit -m "update config" &amp;amp;&amp;amp; git push&lt;/code&gt;, and a second later you realize:&lt;br&gt;
&lt;em&gt;"Wait... did I just push my &lt;code&gt;.env&lt;/code&gt; file or hardcoded AWS secret key to a public GitHub repo?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Even if you delete the commit or force-push, git history scanners and malicious scrapers detect leaked credentials within &lt;strong&gt;seconds&lt;/strong&gt; of hitting GitHub. Once an API key is leaked, it must be rotated immediately.&lt;/p&gt;

&lt;p&gt;To protect codebases before secrets ever leave your local machine, I built &lt;strong&gt;Secret-Scrub&lt;/strong&gt; — a blazing-fast, zero-dependency Node.js CLI scanner with a 1-click Git pre-commit hook installer and Shannon entropy analysis.&lt;/p&gt;


&lt;h2&gt;
  
  
  Architecture: Dual-Layer Detection Engine
&lt;/h2&gt;

&lt;p&gt;Regex alone is never enough for secret scanning. Vendor formats change, and raw credentials don't always carry known prefixes. Secret-Scrub combines two complementary inspection layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Target File / Staged Diff]
            │
            ▼
┌──────────────────────────────────────────────┐
│ Layer 1: High-Fidelity Signature Patterns     │
│ └── AWS (AKIA...), GitHub (ghp_...), Stripe,  │
│     OpenAI (sk-...), Private Keys (RSA/SSH)  │
└──────────────────────┬───────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────┐
│ Layer 2: Shannon Entropy Analysis             │
│ └── H(X) = -Σ P(x) * log2(P(x))               │
│     Calculates character randomness to catch │
│     unstructured API keys &amp;amp; high-entropy pwds│
└──────────────────────┬───────────────────────┘
                       │
                       ▼
        [Exit Code 1: Block Git Commit]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Vendor Signature Matching (18+ Cloud Providers)
&lt;/h3&gt;

&lt;p&gt;Scans for well-known token formats including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Access Keys (&lt;code&gt;AKIA...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;GitHub Personal Access Tokens (&lt;code&gt;ghp_...&lt;/code&gt;, &lt;code&gt;gho_...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Stripe Secret &amp;amp; Restricted Keys (&lt;code&gt;sk_live_...&lt;/code&gt;, &lt;code&gt;rk_live_...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;OpenAI API Keys (&lt;code&gt;sk-...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Slack Webhooks, Google API Keys, JWT tokens, and PEM Private Keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Shannon Entropy Math for Unknown Secrets
&lt;/h3&gt;

&lt;p&gt;Attackers and developers frequently use unstructured high-randomness tokens that have no vendor prefix. Secret-Scrub measures information entropy:&lt;/p&gt;

&lt;p&gt;$$H(X) = -\sum_{i=1}^n P(x_i) \log_2 P(x_i)$$&lt;/p&gt;

&lt;p&gt;High entropy strings (e.g., &lt;code&gt;dGhpcy1pcy1hLXJhbmRvbS1zZWNyZXQ=&lt;/code&gt;) score high randomness and are flagged even without a pattern match, while normal camelCase code variables score low and are ignored.&lt;/p&gt;




&lt;h2&gt;
  
  
  Git Workflow Integration (&lt;code&gt;--staged&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Scanning thousands of files on every commit slows down developers. Secret-Scrub solves this with &lt;code&gt;--staged&lt;/code&gt; mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx secret-scrub &lt;span class="nt"&gt;--staged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of scanning entire directories, it queries &lt;code&gt;git diff --cached&lt;/code&gt; and only inspects files currently staged for commit. This takes less than &lt;strong&gt;40 milliseconds&lt;/strong&gt;, making it imperceptible in day-to-day coding.&lt;/p&gt;

&lt;h3&gt;
  
  
  1-Click Pre-Commit Hook Installer
&lt;/h3&gt;

&lt;p&gt;You don't need complex external hook runners. You can install native Git protection directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx secret-scrub install-hook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically configures &lt;code&gt;.git/hooks/pre-commit&lt;/code&gt;. If anyone on your team attempts to commit a secret, the commit is automatically aborted locally before it can be pushed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;Test your current directory or staged commits immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan whole directory&lt;/span&gt;
npx secret-scrub &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Scan only staged git files&lt;/span&gt;
npx secret-scrub &lt;span class="nt"&gt;--staged&lt;/span&gt;

&lt;span class="c"&gt;# Machine-readable JSON output for CI pipelines&lt;/span&gt;
npx secret-scrub &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Source Code &amp;amp; Community
&lt;/h2&gt;

&lt;p&gt;Secret-Scrub is 100% open-source under the MIT license.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/mahdyarmonfared/secret-scrub" rel="noopener noreferrer"&gt;github.com/mahdyarmonfared/secret-scrub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What provider signatures or secret formats would you like added?&lt;/li&gt;
&lt;li&gt;How does your team enforce secret prevention before CI/CD?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find this tool helpful for securing your repositories, feel free to drop a ⭐ on GitHub!&lt;/p&gt;

</description>
      <category>security</category>
      <category>node</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Built QuickShare-QR: Share Files from Terminal to Phone via Instant ASCII QR Codes</title>
      <dc:creator>Mahdyar</dc:creator>
      <pubDate>Mon, 21 Sep 2026 23:17:28 +0000</pubDate>
      <link>https://dev.to/mahdyarmonfared/how-i-built-quickshare-qr-share-files-from-terminal-to-phone-via-instant-ascii-qr-codes-1llb</link>
      <guid>https://dev.to/mahdyarmonfared/how-i-built-quickshare-qr-share-files-from-terminal-to-phone-via-instant-ascii-qr-codes-1llb</guid>
      <description>&lt;p&gt;Every developer encounters this awkward friction almost every week:&lt;/p&gt;

&lt;p&gt;You have an APK file, a configuration snippet, an invoice PDF, or a test video on your development machine, and you need to send it to your phone right now.&lt;/p&gt;

&lt;p&gt;What do most of us do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email the file to ourselves.&lt;/li&gt;
&lt;li&gt;Send it over a Telegram "Saved Messages" chat.&lt;/li&gt;
&lt;li&gt;Upload it to Google Drive or Dropbox and wait for cloud syncing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these methods upload your local files to external third-party cloud servers just to travel 30 centimeters across your desk over the same local Wi-Fi router.&lt;/p&gt;

&lt;p&gt;To eliminate this friction, I built &lt;strong&gt;QuickShare-QR&lt;/strong&gt; — a zero-setup Node.js CLI tool that spins up a secure, ephemeral local HTTP server and prints a scannable ASCII QR code directly into your terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Developer Machine]                                   [Smartphone / Tablet]
       │                                                       │
$ npx quickshare-qr test.pdf                                   │
       │                                                       │
       ├── 1. Discovers Local LAN IP (192.168.1.X)             │
       ├── 2. Spins up Ephemeral HTTP Server on random port    │
       └── 3. Renders ANSI QR Code directly in Terminal        │
                       │                                       │
                       └────────── [Scan with Camera] ─────────┤
                                                               │
                                         Direct LAN Streaming Download
                                        (No app installation needed!)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Zero Cloud, Zero External Servers
&lt;/h3&gt;

&lt;p&gt;The transfer happens 100% peer-to-peer over your local Wi-Fi / LAN network. Files never leave your local router. Speed is limited only by your local Wi-Fi bandwidth (often 30–80 MB/s).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Native Camera Scanning (No Client App Required)
&lt;/h3&gt;

&lt;p&gt;Because QuickShare-QR serves standard HTTP with proper &lt;code&gt;Content-Disposition&lt;/code&gt; headers, the receiver does not need to install any app. &lt;br&gt;
Just point the phone's default camera at the terminal QR code, tap the notification, and the browser immediately begins downloading.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Direct Streaming with Ephemeral Auto-Shutdown
&lt;/h3&gt;

&lt;p&gt;The CLI streams files chunk-by-chunk using Node.js read streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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="s1"&gt;Content-Type&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="s1"&gt;application/octet-stream&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="s1"&gt;Content-Disposition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`attachment; filename="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Length&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fileStat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&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;fileStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;fileStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;fileStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transfer complete! Shutting down server...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&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;p&gt;As soon as the file finishes downloading, the server gracefully shuts down and releases the port.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security &amp;amp; Path Traversal Protection
&lt;/h2&gt;

&lt;p&gt;Allowing devices on the local network to fetch files requires strict security boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Path Confinement:&lt;/strong&gt; Prevents directory traversal attacks (&lt;code&gt;../&lt;/code&gt;) by strictly binding the HTTP route to the single selected target file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Ephemeral Ports:&lt;/strong&gt; Binds to arbitrary available ports to prevent conflict and sniffing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional Single-Use Tokens:&lt;/strong&gt; Guarantees that only the device that scanned the terminal QR code can initiate the download.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Start in 5 Seconds
&lt;/h2&gt;

&lt;p&gt;You don't need to install anything permanently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Share any file instantly over LAN&lt;/span&gt;
npx quickshare-qr ./my-presentation.pdf

&lt;span class="c"&gt;# Share an archive or photo&lt;/span&gt;
npx quickshare-qr ./assets.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your phone's camera at your terminal screen, and the transfer starts immediately!&lt;/p&gt;




&lt;h2&gt;
  
  
  Source Code &amp;amp; Community
&lt;/h2&gt;

&lt;p&gt;QuickShare-QR is open source under the MIT license.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/mahdyarmonfared/quickshare-qr" rel="noopener noreferrer"&gt;github.com/mahdyarmonfared/quickshare-qr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd love your feedback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Would you like support for receiving files (uploading from phone back to terminal)?&lt;/li&gt;
&lt;li&gt;What other local sharing protocols or encryption layers would make this even better?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to open an issue or drop a star ⭐ on GitHub!&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Built HashDup: A Fast, Memory-Safe Duplicate File Finder CLI in Node.js</title>
      <dc:creator>Mahdyar</dc:creator>
      <pubDate>Mon, 21 Sep 2026 23:14:15 +0000</pubDate>
      <link>https://dev.to/mahdyarmonfared/how-i-built-hashdup-a-fast-memory-safe-duplicate-file-finder-cli-in-nodejs-3oa3</link>
      <guid>https://dev.to/mahdyarmonfared/how-i-built-hashdup-a-fast-memory-safe-duplicate-file-finder-cli-in-nodejs-3oa3</guid>
      <description>&lt;p&gt;Over time, every workstation accumulates duplicate files: duplicated photo backups, repeated downloads of large zip archives, cloned repositories, and redundant ISOs. They quietly eat gigabytes of expensive SSD storage.&lt;/p&gt;

&lt;p&gt;Most people write a simple script that reads all files and compares their hashes. But on large drives, that naive approach has two fatal problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Performance bottleneck:&lt;/strong&gt; Hashing thousands of files wastes CPU and disk I/O when 90% of files have unique sizes and can never be duplicates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory crashes (OOM):&lt;/strong&gt; Reading multi-gigabyte files into memory (&lt;code&gt;fs.readFileSync&lt;/code&gt;) crashes the Node.js event loop with out-of-memory errors.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this efficiently, I built &lt;strong&gt;HashDup&lt;/strong&gt; — a fast, memory-safe CLI duplicate finder built with native Node.js streams and a smart 2-phase scanning engine.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 2-Phase Scanning Architecture
&lt;/h2&gt;

&lt;p&gt;HashDup uses an algorithmic filtering pipeline that avoids unnecessary disk reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[All Target Files]
       │
       ▼
Phase 1: Size-First Grouping (O(1) filter)
  └── Discard all files with unique byte sizes without reading contents!
       │
       ▼ (Only files with identical sizes remain)
Phase 2: Chunked Streaming SHA-256 Hashing
  └── Streams files in 64KB buffers (Flat O(1) memory consumption)
       │
       ▼
[Identified Duplicate Sets &amp;amp; Reclaimable Space]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Phase 1: Size-First Filtering
&lt;/h3&gt;

&lt;p&gt;Before reading any file content, HashDup runs a lightweight metadata scan using &lt;code&gt;fs.promises.stat()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If a file has a unique size in bytes, it is physically impossible for it to be a duplicate of any other file. We discard these immediately. On a drive with 10,000 files, this eliminates 90%–95% of candidates in milliseconds!&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Phase 2: Memory-Safe Streaming SHA-256
&lt;/h3&gt;

&lt;p&gt;For candidate files that share the exact same byte size, we compute cryptographic SHA-256 digests.&lt;/p&gt;

&lt;p&gt;Instead of buffering whole files into RAM, HashDup uses Node.js streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hashFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;hash&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;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&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;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&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;p&gt;This ensures that whether a file is 2 KB or 10 GB, Node.js memory usage stays constant at around 30MB of RAM.&lt;/p&gt;




&lt;h2&gt;
  
  
  Safety &amp;amp; Reporting Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Waste Analytics:&lt;/strong&gt; Displays exact duplicate groupings, file paths, and calculates total reclaimable space (MB/GB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dry-Run by Default:&lt;/strong&gt; Inspect duplicates in a clean CLI table before touching or deleting anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Heavy Dependencies:&lt;/strong&gt; Built entirely with Node.js built-ins (&lt;code&gt;crypto&lt;/code&gt;, &lt;code&gt;stream&lt;/code&gt;, &lt;code&gt;fs/promises&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;You can test it right now on any directory using &lt;code&gt;npx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan current directory and preview duplicates&lt;/span&gt;
npx hashdup-cli scan &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Scan with custom output format or dry-run&lt;/span&gt;
npx hashdup-cli scan ~/Downloads &lt;span class="nt"&gt;--min-size&lt;/span&gt; 1MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Source Code &amp;amp; Community Feedback
&lt;/h2&gt;

&lt;p&gt;HashDup is open source under the MIT license.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/mahdyarmonfared/hashdup-cli" rel="noopener noreferrer"&gt;github.com/mahdyarmonfared/hashdup-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear from other CLI developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do you handle file comparisons on massive datasets (e.g., partial hashing vs full SHA-256)?&lt;/li&gt;
&lt;li&gt;Would you find an interactive terminal cleanup wizard (TUI) useful for safe one-click deletion?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback, PRs, and ⭐ stars on GitHub are always appreciated!&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>performance</category>
    </item>
    <item>
      <title>How I Built CleanDrop: A Safe, Zero-Dependency CLI to Tame Your Downloads Folder</title>
      <dc:creator>Mahdyar</dc:creator>
      <pubDate>Mon, 21 Sep 2026 23:09:54 +0000</pubDate>
      <link>https://dev.to/mahdyarmonfared/how-i-built-cleandrop-a-safe-zero-dependency-cli-to-tame-your-downloads-folder-4b6j</link>
      <guid>https://dev.to/mahdyarmonfared/how-i-built-cleandrop-a-safe-zero-dependency-cli-to-tame-your-downloads-folder-4b6j</guid>
      <description>&lt;p&gt;Every developer’s &lt;code&gt;Downloads&lt;/code&gt; folder eventually becomes a chaotic digital landfill. Screenshots, random PDF receipts, compressed archives, test datasets, and CLI binaries pile up until finding anything becomes a chore.&lt;/p&gt;

&lt;p&gt;Like most engineers, I initially relied on ad-hoc Bash one-liners. But quick scripts have major flaws:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They fail silently or overwrite files when file names collide.&lt;/li&gt;
&lt;li&gt;One wrong flag or typo can accidentally move or delete critical files.&lt;/li&gt;
&lt;li&gt;There is no rollback mechanism if you change your mind.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve this once and for all, I built &lt;strong&gt;CleanDrop&lt;/strong&gt; — a lightweight, zero-dependency Node.js CLI utility designed to organize cluttered folders safely, predictably, and reversibly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Design Principles
&lt;/h2&gt;

&lt;p&gt;When designing CleanDrop, developer safety and ergonomics were the top priorities:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Dry-Run by Default Mentality (&lt;code&gt;--dry-run&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Moving hundreds of files without knowing where they will land is terrifying. CleanDrop allows you to simulate the entire sorting process first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cleandrop &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints an exact audit table of source paths, target categories, and planned file actions without touching disk storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Built-in Undo Support (Atomic Transaction Log)
&lt;/h3&gt;

&lt;p&gt;Accidents happen. CleanDrop logs batch actions to a local history journal. If you run a sort and realize you wanted to keep certain files in place, you can reverse the operation with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cleandrop &lt;span class="nt"&gt;--undo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Non-Destructive Conflict Resolution
&lt;/h3&gt;

&lt;p&gt;CleanDrop never silently overwrites an existing file. If &lt;code&gt;invoice.pdf&lt;/code&gt; already exists in &lt;code&gt;Documents/&lt;/code&gt;, the tool renames the incoming file safely (e.g., &lt;code&gt;invoice (1).pdf&lt;/code&gt;), ensuring zero data loss.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Zero Heavy Dependencies
&lt;/h3&gt;

&lt;p&gt;The entire CLI is powered by native Node.js filesystem APIs (&lt;code&gt;fs/promises&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;). It starts instantly and doesn't require bloated npm dependency trees.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it Categorizes Files
&lt;/h2&gt;

&lt;p&gt;CleanDrop inspects file extensions and maps them into clean, logical target folders:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Extensions Handled&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.pdf&lt;/code&gt;, &lt;code&gt;.docx&lt;/code&gt;, &lt;code&gt;.txt&lt;/code&gt;, &lt;code&gt;.xlsx&lt;/code&gt;, &lt;code&gt;.pptx&lt;/code&gt;, &lt;code&gt;.csv&lt;/code&gt;, &lt;code&gt;.md&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Images&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.png&lt;/code&gt;, &lt;code&gt;.jpg&lt;/code&gt;, &lt;code&gt;.jpeg&lt;/code&gt;, &lt;code&gt;.gif&lt;/code&gt;, &lt;code&gt;.svg&lt;/code&gt;, &lt;code&gt;.webp&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Archives&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.zip&lt;/code&gt;, &lt;code&gt;.tar.gz&lt;/code&gt;, &lt;code&gt;.rar&lt;/code&gt;, &lt;code&gt;.7z&lt;/code&gt;, &lt;code&gt;.tar&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code &amp;amp; Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.js&lt;/code&gt;, &lt;code&gt;.ts&lt;/code&gt;, &lt;code&gt;.py&lt;/code&gt;, &lt;code&gt;.json&lt;/code&gt;, &lt;code&gt;.sql&lt;/code&gt;, &lt;code&gt;.html&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Media&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.mp4&lt;/code&gt;, &lt;code&gt;.mov&lt;/code&gt;, &lt;code&gt;.mp3&lt;/code&gt;, &lt;code&gt;.wav&lt;/code&gt;, &lt;code&gt;.mkv&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;System files, hidden dotfiles (&lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;.git&lt;/code&gt;), and already organized subdirectories are safely ignored.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start in 5 Seconds
&lt;/h2&gt;

&lt;p&gt;You don't even need to install it globally. Run it directly with &lt;code&gt;npx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Preview changes first&lt;/span&gt;
npx cleandrop &lt;span class="nt"&gt;--dry-run&lt;/span&gt;

&lt;span class="c"&gt;# 2. Run the organization&lt;/span&gt;
npx cleandrop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Source Code &amp;amp; Contributing
&lt;/h2&gt;

&lt;p&gt;CleanDrop is 100% open-source under the MIT license. &lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/mahdyarmonfared/cleandrop" rel="noopener noreferrer"&gt;github.com/mahdyarmonfared/cleandrop&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would love to get your thoughts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What safety features do you look for in automated file management tools?&lt;/li&gt;
&lt;li&gt;What file types or custom rule options would you like to see next?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find it useful, feel free to drop a star ⭐ on GitHub or open an issue with your suggestions!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
