<?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: Nayananshu Garai</title>
    <description>The latest articles on DEV Community by Nayananshu Garai (@ngarai).</description>
    <link>https://dev.to/ngarai</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%2F3964950%2F3401bc07-7936-4788-a822-066adf4f65f0.png</url>
      <title>DEV Community: Nayananshu Garai</title>
      <link>https://dev.to/ngarai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ngarai"/>
    <language>en</language>
    <item>
      <title>Cifrasync</title>
      <dc:creator>Nayananshu Garai</dc:creator>
      <pubDate>Sun, 07 Jun 2026 14:12:41 +0000</pubDate>
      <link>https://dev.to/ngarai/cifrasync-3n06</link>
      <guid>https://dev.to/ngarai/cifrasync-3n06</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CifraSync&lt;/strong&gt; — a zero-dependency encrypted incremental backup tool written in C from scratch (200 KB binary, 31 source files, ~5000 lines). No Python, no Go, no npm. Just a single binary with chunk-based deduplication, RLE compression, HMAC stream cipher v2 (key separation, CSPRNG, 600K PBKDF2), incremental snapshots, remote sync, file-based mutual exclusion locks, and a colored interactive TUI.&lt;/p&gt;

&lt;p&gt;The project started as a partially-built C backup scaffold with stubs and unfinished wiring. I completed all 7 commands (init, backup, restore, list, verify, prune, sync), added a server mode, wired compression and encryption end-to-end with key separation and cryptographically secure randomness, built an interactive menu, added file-based locking, and wrote a comprehensive README and docs. All 14 tests pass with zero compiler warnings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key technical features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chunk-level deduplication&lt;/strong&gt; — identical data across files/snapshots stored once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RLE compression&lt;/strong&gt; — per-chunk, auto-decompressed on restore/verify&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HMAC stream cipher v2&lt;/strong&gt; — Blob version 2 with key separation (enc_key ≠ mac_key), cryptographically random salt/nonce (CryptGenRandom / /dev/urandom), 600K PBKDF2 iterations, constant-time tag verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key cache&lt;/strong&gt; — In-memory PBKDF2 cache eliminating redundant per-chunk derivation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Masked passphrase input&lt;/strong&gt; — No echo on terminal (SetConsoleMode / getpass)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File-based mutual exclusion&lt;/strong&gt; — flock/LockFileEx locks (EXCLUSIVE for writes, SHARED for reads)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic manifest writes&lt;/strong&gt; — &lt;code&gt;.tmp&lt;/code&gt; + &lt;code&gt;rename()&lt;/code&gt; crash safety&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Journal replay&lt;/strong&gt; — interrupted backups auto-resume&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform&lt;/strong&gt; — same codebase compiles on Windows (MinGW), Linux, macOS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive TUI&lt;/strong&gt; — colored numbered menu, no flags needed for daily use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;wake up cifra&lt;/code&gt;&lt;/strong&gt; — one-command launcher after one-time &lt;code&gt;setup.ps1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Build &amp;amp; Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/N-Garai/CifraSync.git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CifraSync&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;mingw32-make&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;release&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c"&gt;# builds bin/cifrasync.exe (200 KB)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;mingw32-make&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c"&gt;# 14/14 tests pass&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Quick Demo (interactive mode)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create test data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\demo\source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\demo\repo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\demo\restored&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"Hello world"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C:\demo\source\file1.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"Another file"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\demo\source\file2.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\demo\source\sub&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"Deep file"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C:\demo\source\sub\deep.txt&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Launch interactive menu&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cifrasync&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CLI Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cifrasync init &lt;span class="nt"&gt;--repo&lt;/span&gt; PATH
cifrasync backup &lt;span class="nt"&gt;--source&lt;/span&gt; PATH &lt;span class="nt"&gt;--repo&lt;/span&gt; PATH &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--compress&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--encrypt&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--label&lt;/span&gt; TEXT]
cifrasync list &lt;span class="nt"&gt;--repo&lt;/span&gt; PATH
cifrasync restore &lt;span class="nt"&gt;--repo&lt;/span&gt; PATH &lt;span class="nt"&gt;--snapshot&lt;/span&gt; ID &lt;span class="nt"&gt;--out&lt;/span&gt; PATH &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--source-file&lt;/span&gt; PATH]
cifrasync verify &lt;span class="nt"&gt;--repo&lt;/span&gt; PATH
cifrasync prune &lt;span class="nt"&gt;--repo&lt;/span&gt; PATH &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--keep-last&lt;/span&gt; N] &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--older-than&lt;/span&gt; DAYS]
cifrasync &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--repo&lt;/span&gt; PATH &lt;span class="nt"&gt;--remote&lt;/span&gt; HOST:PORT   &lt;span class="c"&gt;# pushes manifests + chunks to remote&lt;/span&gt;
cifrasync serve &lt;span class="nt"&gt;--bind&lt;/span&gt; HOST:PORT &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--repo&lt;/span&gt; PATH]   &lt;span class="c"&gt;# receives &amp;amp; stores sync data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; : The project had the CLI scaffold, core storage and chunking layers, basic tests, and tools. But 3 of 7 commands were stubs (verify, prune, sync returned "unsupported"). Compression and encryption were logged but not applied. Snapshot listing showed bare filenames only. No journal replay, no atomic writes, no config auto-load, no single-file restore, no interactive mode, no launcher. The project needed documentation, polish, and a clear demo path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt; : Every command is fully implemented. Compression and encryption are wired end-to-end (RLE before store, HMAC seal v2 with key separation, auto-decompress on restore/verify). Encryption uses cryptographically random salt+nonce, 600K PBKDF2 iterations, masked passphrase input, and key separation (enc_key ≠ mac_key). File-based mutual exclusion locks prevent concurrent corrupting writes. Journal replay resumes interrupted backups. Atomic &lt;code&gt;.tmp&lt;/code&gt; + &lt;code&gt;rename()&lt;/code&gt; prevents partial snapshots. Config auto-loads on startup. The snapshot list shows rich details (ID, timestamp, file count, size, label). There's a colored interactive TUI, a &lt;code&gt;serve&lt;/code&gt; command for remote sync, and a &lt;code&gt;wake up cifra&lt;/code&gt; one-command launcher. The README is comprehensive with a full interactive mode reference table. All 14 tests pass with zero warnings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sync Protocol (full data transfer)
&lt;/h3&gt;

&lt;p&gt;The sync now implements bidirectional chunk transfer via a custom TCP frame protocol:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HELLO&lt;/strong&gt; — client announces itself, server responds with repo path&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MANIFEST&lt;/strong&gt; — client sends full snapshot metadata + serialized manifest file (all file/chunk references)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing chunk negotiation&lt;/strong&gt; — server compares manifest chunk hashes against its local chunk store, returns list of only the missing hashes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CHUNK&lt;/strong&gt; — client reads each missing chunk from local store and sends it as binary frame (64-byte SHA-256 hash + raw data)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BYE&lt;/strong&gt; — clean disconnect&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only chunks the server doesn't already have are transferred, making re-syncs incremental. The server writes &lt;code&gt;.snapshot&lt;/code&gt; metadata files, &lt;code&gt;.manifest&lt;/code&gt; files, and stores chunks using the existing repository format. The client reads manifests from its local repo and sends chunk data on demand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key changes made:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implemented verify (chunk re-hash), prune (snapshot deletion + orphan GC), sync (full manifest + chunk data transfer), serve (server mode with --repo for storage)&lt;/li&gt;
&lt;li&gt;Wired RLE compression and HMAC encryption into the backup pipeline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blob v2 with key separation&lt;/strong&gt; — enc_key/mac_key derived via HMAC-SHA256 from master key; authentication tag computed with mac_key, stream cipher uses enc_key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cryptographically secure randomness&lt;/strong&gt; — replaced &lt;code&gt;time()+clock()&lt;/code&gt; salt/nonce with CryptGenRandom (Windows) / /dev/urandom (POSIX)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PBKDF2 iterations: 10K → 600K&lt;/strong&gt; — OWASP 2023 recommendation for SHA-256&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wired in key cache&lt;/strong&gt; — per-session static cache avoids recomputing PBKDF2 per chunk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Masked passphrase input&lt;/strong&gt; — SetConsoleMode disables echo on Windows; getpass on POSIX&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passphrase zeroization&lt;/strong&gt; — passphrase buffer cleared in backup cleanup path&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong-passphrase now fails loudly&lt;/strong&gt; — restore/verify previously silently produced garbage; now they return errors with log messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File-based mutual exclusion&lt;/strong&gt; — EXCLUSIVE lock for backup/prune/server-sync, SHARED lock for restore/verify/client-sync; cross-platform via flock / LockFileEx&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed Windows shared lock&lt;/strong&gt; — LockFileEx now called for shared mode too (was a no-op)&lt;/li&gt;
&lt;li&gt;Added journal replay on backup start&lt;/li&gt;
&lt;li&gt;Added atomic manifest writes&lt;/li&gt;
&lt;li&gt;Added rich snapshot listing with formatted columns&lt;/li&gt;
&lt;li&gt;Added single-file restore (&lt;code&gt;--source-file&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Added config auto-load&lt;/li&gt;
&lt;li&gt;Built interactive TUI with colored ASCII menu&lt;/li&gt;
&lt;li&gt;Created &lt;code&gt;wake up cifra&lt;/code&gt; launcher (&lt;code&gt;wake_up_cifra.cmd&lt;/code&gt; + &lt;code&gt;setup.ps1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Fixed platform detection (dynamic &lt;code&gt;#ifdef&lt;/code&gt; instead of hardcoded)&lt;/li&gt;
&lt;li&gt;Fixed verify with compressed chunks (decompress before hash check)&lt;/li&gt;
&lt;li&gt;Added POSIX fallbacks for cross-platform support&lt;/li&gt;
&lt;li&gt;Deduplicated 5 copies of &lt;code&gt;path_join&lt;/code&gt; into shared &lt;code&gt;cs_path_join&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fixed &lt;code&gt;\r&lt;/code&gt; carriage return bug in interactive input (was corrupting paths on Windows)&lt;/li&gt;
&lt;li&gt;Fixed NULL callback crash in journal.c&lt;/li&gt;
&lt;li&gt;Wrote comprehensive README, after.md, and submission docs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;I used GitHub Copilot throughout the completion process. Copilot was most helpful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate code generation&lt;/strong&gt; — writing repetitive CLI option parsing, path joining, and error handling patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test writing&lt;/strong&gt; — generating test cases and edge cases for unit tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt; — drafting README sections, the submission post, and code comments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bug finding&lt;/strong&gt; — suggesting fixes for the &lt;code&gt;\r&lt;/code&gt; carriage return issue and the verify decompression logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring&lt;/strong&gt; — suggesting how to deduplicate the &lt;code&gt;path_join&lt;/code&gt; functions across 5 files into a single shared implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every suggestion was reviewed, adjusted for the project's coding style and safety constraints, and validated against the test suite before being committed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo Video
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://youtu.be/IqonIOfXhFs" rel="noopener noreferrer"&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.amazonaws.com%2Fuploads%2Farticles%2Fgv46o87hlvtggapfalww.jpg" alt="CifraSync Demo" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Watch the full walkthrough: &lt;a href="https://youtu.be/IqonIOfXhFs" rel="noopener noreferrer"&gt;https://youtu.be/IqonIOfXhFs&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Repository: &lt;a href="https://github.com/N-Garai/CifraSync" rel="noopener noreferrer"&gt;N-Garai/CifraSync&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>githubcopilot</category>
      <category>c</category>
    </item>
  </channel>
</rss>
