<?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: Mathéo Delbarre</title>
    <description>The latest articles on DEV Community by Mathéo Delbarre (@etoile_bleu).</description>
    <link>https://dev.to/etoile_bleu</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%2F3984106%2F1a0896d3-ec34-45da-af5b-6c62036750a0.png</url>
      <title>DEV Community: Mathéo Delbarre</title>
      <link>https://dev.to/etoile_bleu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/etoile_bleu"/>
    <language>en</language>
    <item>
      <title>Show us what you're building for people who actually need it</title>
      <dc:creator>Mathéo Delbarre</dc:creator>
      <pubDate>Tue, 23 Jun 2026 21:14:50 +0000</pubDate>
      <link>https://dev.to/etoile_bleu/show-us-what-youre-building-for-people-who-actually-need-it-5h02</link>
      <guid>https://dev.to/etoile_bleu/show-us-what-youre-building-for-people-who-actually-need-it-5h02</guid>
      <description>&lt;p&gt;&lt;em&gt;By Mathéo Delbarre, 2nd year CS student at EPITECH Nancy, France&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I've been publishing about &lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;ZamSync&lt;/a&gt;, a sync engine I built for district clinics in Bhutan that run on 2G and lose power mid-transfer. The response genuinely surprised me. Strangers from countries I've never visited opened pull requests. Someone took 30 seconds to write a message just to say the problem mattered. A developer forked the repo to build offline-first sync for password managers.&lt;/p&gt;

&lt;p&gt;None of that was because I'm a great marketer. I'm a 19-year-old student with no following and no idea what I'm doing on that front. It happened because the problem was real.&lt;/p&gt;

&lt;p&gt;That made me curious about something: &lt;strong&gt;what else is out there?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not just projects. Problems too. The ones that don't have a solution yet, the ones where you've looked at the existing tools and thought "none of this actually fits." I want to hear about those just as much.&lt;/p&gt;




&lt;h2&gt;
  
  
  You don't need an idea to participate
&lt;/h2&gt;

&lt;p&gt;This is the part I want to be clear about.&lt;/p&gt;

&lt;p&gt;If you have a finished project, great. Share it.&lt;/p&gt;

&lt;p&gt;If you have a half-finished prototype, share it.&lt;/p&gt;

&lt;p&gt;If you have nothing built at all, but you work in a field where something is genuinely broken and you've spent years watching people work around it with spreadsheets, paper, or nothing at all... &lt;strong&gt;that is exactly what I want to read.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Describe the problem. Be specific. What breaks, when, for who, and why the existing tools don't fix it. Drop it in the comments or write a full article about it. That kind of description is not "less useful" than a GitHub link. It might be more useful, because someone reading it might have the technical background to actually build something.&lt;/p&gt;

&lt;p&gt;The best open source projects I know of started as someone describing a problem clearly enough that another person thought "I know how to fix that."&lt;/p&gt;




&lt;h2&gt;
  
  
  What I mean by "real problem"
&lt;/h2&gt;

&lt;p&gt;Not trying to gatekeep here, but there is a difference between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I want a better to-do app"&lt;/li&gt;
&lt;li&gt;"District nurses in my region track patient visits on paper because the official system requires internet, and records get lost every monsoon season"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are real problems! But only the second one is what this thread is about.&lt;/p&gt;

&lt;p&gt;The kind of problems I'm looking for share a few traits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The people affected don't have the resources to buy a commercial solution&lt;/li&gt;
&lt;li&gt;The existing tools are the wrong size, the wrong cost, or built for a different context&lt;/li&gt;
&lt;li&gt;The constraint is physical: no internet, old hardware, no power, a language nobody built software for, a population nobody considers a "market"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some directions to get you thinking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Healthcare in low-bandwidth or low-resource environments&lt;/li&gt;
&lt;li&gt;Education where connectivity isn't guaranteed&lt;/li&gt;
&lt;li&gt;Agriculture, livestock, field data collection without cloud access&lt;/li&gt;
&lt;li&gt;Accessibility tools for disabilities or languages that the big platforms ignore&lt;/li&gt;
&lt;li&gt;Logistics in places where the infrastructure is unreliable&lt;/li&gt;
&lt;li&gt;Local governance, civic tools, community record-keeping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've seen something like this up close, write about it. You don't need to have the solution. The problem alone is enough to start a conversation. 💡&lt;/p&gt;




&lt;h2&gt;
  
  
  What to share
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you have a project:&lt;/strong&gt; write about it. Any language, any stage, any size. What problem does it solve, who does it solve it for, what constraints did you hit, what would you do differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you have an idea or a sketch:&lt;/strong&gt; write about it. Explain the problem, explain your thinking so far, explain where you're stuck. Maybe someone in the comments has already solved part of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you have a problem and no solution:&lt;/strong&gt; write about it. Be specific. What is broken, for who, and why. That is the most valuable starting point.&lt;/p&gt;

&lt;p&gt;Then &lt;strong&gt;tag your article &lt;code&gt;#realworld&lt;/code&gt;&lt;/strong&gt; and drop a link in the comments here. Or just describe the problem directly in the comments. I'll read every one, and I think other people here will too. The goal is to discuss, debate, push back, suggest approaches, show off work finished or not started. Not to do AI slop ;)&lt;/p&gt;




&lt;h2&gt;
  
  
  My contribution to this thread
&lt;/h2&gt;

&lt;p&gt;I'll start. ZamSync is a Rust sync engine for district clinics in Bhutan: WAL replication with Hybrid Logical Clocks, Version Vectors, mTLS, and ChaCha20-Poly1305 encryption at rest. It runs in under 10 MB of RAM on a Raspberry Pi. It survives mid-transfer power cuts. It syncs exactly the missing delta and nothing more, because 2G bandwidth is not something you waste.&lt;/p&gt;

&lt;p&gt;The full story is here:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od" class="crayons-story__hidden-navigation-link"&gt;Building software for places where the internet barely exists&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/etoile_bleu" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3984106%2F1a0896d3-ec34-45da-af5b-6c62036750a0.png" alt="etoile_bleu profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/etoile_bleu" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mathéo Delbarre
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mathéo Delbarre
                
              
              &lt;div id="story-author-preview-content-3909672" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/etoile_bleu" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3984106%2F1a0896d3-ec34-45da-af5b-6c62036750a0.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mathéo Delbarre&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 15&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od" id="article-link-3909672"&gt;
          Building software for places where the internet barely exists
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/rust"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;rust&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            13 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;The problem that started it was simple: district clinics needed reliable synchronization in places where connectivity and power are unreliable. ZamSync is one attempt at solving part of that problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I'm doing this
&lt;/h2&gt;

&lt;p&gt;I've spent months inside one problem. I know that problem well. But I have no idea what equivalent things other people are quietly working on, in other fields, in other countries.&lt;/p&gt;

&lt;p&gt;I think there is a whole category of work happening at the margins of the industry that never shows up in the feeds. Not because it's not interesting, but because it's not being built by funded startups, it doesn't have a product launch, and the people doing it are too focused on the actual work to write about it. Or they assume nobody else would care.&lt;/p&gt;

&lt;p&gt;I think people would care, and I more think this community specifically would care.&lt;/p&gt;

&lt;p&gt;So let's find out. What are you building? What problem are you watching that nobody is fixing? What have you tried that didn't work and why?&lt;/p&gt;

&lt;p&gt;Drop it below. 👇&lt;/p&gt;

&lt;p&gt;-- Mathéo Delbarre&lt;/p&gt;

</description>
      <category>realworld</category>
      <category>discuss</category>
      <category>beginners</category>
      <category>healthydebate</category>
    </item>
    <item>
      <title>39 stars, my first open source contributor, and a message I keep re-reading</title>
      <dc:creator>Mathéo Delbarre</dc:creator>
      <pubDate>Sun, 21 Jun 2026 19:52:51 +0000</pubDate>
      <link>https://dev.to/etoile_bleu/39-stars-my-first-open-source-contributor-and-a-message-i-keep-re-reading-3bl2</link>
      <guid>https://dev.to/etoile_bleu/39-stars-my-first-open-source-contributor-and-a-message-i-keep-re-reading-3bl2</guid>
      <description>&lt;p&gt;A few weeks ago I published this article about &lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;ZamSync&lt;/a&gt;, a sync engine I built in Rust for district clinics in Bhutan that run on 2G and lose power mid-transfer:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od" class="crayons-story__hidden-navigation-link"&gt;Building software for places where the internet barely exists&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/etoile_bleu" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3984106%2F1a0896d3-ec34-45da-af5b-6c62036750a0.png" alt="etoile_bleu profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/etoile_bleu" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mathéo Delbarre
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mathéo Delbarre
                
              
              &lt;div id="story-author-preview-content-3909672" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/etoile_bleu" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3984106%2F1a0896d3-ec34-45da-af5b-6c62036750a0.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mathéo Delbarre&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 15&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od" id="article-link-3909672"&gt;
          Building software for places where the internet barely exists
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/rust"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;rust&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            13 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;I wasn't expecting much to be honest! I'm a 2nd-year CS student at EPITECH Nancy. I don't have a following, I don't know how to market things. I said as much in the original article, I just wanted to show the work and see if anyone cared about the problem.&lt;/p&gt;

&lt;p&gt;I was not prepared for what came next haha!&lt;/p&gt;




&lt;h2&gt;
  
  
  The numbers, because people like numbers 💝
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;39 GitHub stars&lt;/strong&gt; from people I've never met&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 external contributors&lt;/strong&gt; who forked the repo, wrote code, and opened pull requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1 message&lt;/strong&gt; in the GitHub discussions that I keep coming back to re-read&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;128 tests&lt;/strong&gt; passing across all platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6 forks&lt;/strong&gt; from a developer building offline-first tools for password managers, finding out someone found your code useful enough to fork is a strange and wonderful feeling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't big numbers, I know that :) . But this is my first open source project, ever. Before this, the only people who had read my code were my professors. Seeing a stranger from a different country open a pull request on something I built alone in my dorm room at 2am... I don't have a better word for it than surreal.&lt;/p&gt;




&lt;h2&gt;
  
  
  The contributor moment
&lt;/h2&gt;

&lt;p&gt;A developer named KairosOps opened &lt;a href="https://github.com/Etoile-Bleu/ZamSync/pull/93" rel="noopener noreferrer"&gt;PR #93&lt;/a&gt; to fix a bug: SQLite paths on Windows were getting a leading slash prepended, turning &lt;code&gt;C:/path/db&lt;/code&gt; into &lt;code&gt;/C:/path/db&lt;/code&gt;. It was a real bug, cleanly fixed with a &lt;code&gt;#[cfg(windows)]&lt;/code&gt; guard and a unit test that covers both platforms.&lt;/p&gt;

&lt;p&gt;I'm 19 years old and someone just contributed to my project! (I was so happy that someone saw my issue and added a fix 😭)&lt;/p&gt;

&lt;p&gt;I merged it the same day.&lt;/p&gt;




&lt;h2&gt;
  
  
  The message that got me
&lt;/h2&gt;

&lt;p&gt;Someone named per-oestergaard left this in the GitHub discussions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Hi. I read your article and just want to praise you for suggesting, and creating a solution for an important problem. I hope you manage to get it implemented. Unfortunately, I do not have any channels for supporting this."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's it. That's the whole message. No technical feedback, no code, just a person who read about district clinics in Bhutan, thought the problem mattered, and took 30 seconds to say so.&lt;/p&gt;

&lt;p&gt;I've been building ZamSync alone for weeks. The grind of writing tests, fixing edge cases at midnight, documenting things nobody might ever read, sometimes it's hard to remember why. That message reminded me. Someone out there thinks this problem is real and worth solving. That's enough to keep going.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's new since the first article
&lt;/h2&gt;

&lt;p&gt;I kept building. Here's what shipped:&lt;/p&gt;

&lt;h3&gt;
  
  
  Embedded status dashboard (Phase 20)
&lt;/h3&gt;

&lt;p&gt;ZamSync now ships with a browser dashboard at &lt;code&gt;/ui&lt;/code&gt;. No React, no build step, no CDN. Just a dark-themed HTML page served directly from the binary, showing live event counts, WAL size, uptime, and a real-time event stream via SSE.&lt;/p&gt;

&lt;p&gt;The SSE reconnect logic was something I'm proud of: exponential backoff from 1s to 30s, Visibility API support so the tab stops hammering the server when you switch away, and dedup logic so reconnects never replay already-seen events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Typed HTTP error codes (Phase 21)
&lt;/h3&gt;

&lt;p&gt;The API now returns structured JSON errors with stable machine-readable codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SCHEMA_VIOLATION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"missing required field: patient_id"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four codes: &lt;code&gt;INVALID_JSON&lt;/code&gt; (400), &lt;code&gt;SCHEMA_VIOLATION&lt;/code&gt; (422), &lt;code&gt;WAL_UNAVAILABLE&lt;/code&gt; (503), &lt;code&gt;INTERNAL_ERROR&lt;/code&gt; (500). The codes are additive and will never be renamed. Client code can safely switch on them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation site
&lt;/h3&gt;

&lt;p&gt;ZamSync now has a real documentation site built with &lt;a href="https://rust-lang.github.io/mdBook/" rel="noopener noreferrer"&gt;mdBook&lt;/a&gt;, the same tool used by the Rust book and rust-analyzer. Three pages: Introduction, REST API reference, and a full Error Codes guide with retry pattern examples. It deploys automatically to GitHub Pages on every push to main.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this is going
&lt;/h2&gt;

&lt;p&gt;The technical goal hasn't changed: I want to propose ZamSync to the Bhutan Ministry of Health as infrastructure for their ePIS district sync problem. I'm a student, not a healthcare professional, and I have no connections there. I don't know exactly how to make that happen. But I'm going to keep building until the thing is undeniably ready, and then figure out the rest.&lt;/p&gt;

&lt;p&gt;Next phases on the roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Async Tokio&lt;/strong&gt; -- replace OS threads with green threads so ARM nodes with 512 MB RAM can serve dozens of peers concurrently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conflict detection&lt;/strong&gt; -- flag when two clinics edit the same patient record at the same time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key rotation&lt;/strong&gt; -- re-key a node without losing its event history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work in global health, low-resource IT, or have any connection to healthcare systems in low-bandwidth regions, I would genuinely love to talk. My email is &lt;strong&gt;&lt;a href="mailto:matheo.delbarre@epitech.eu"&gt;matheo.delbarre@epitech.eu&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  A personal note
&lt;/h2&gt;

&lt;p&gt;I started this because I read about Bhutan's ePIS struggles and thought "I could try to fix that." I had never written a WAL. I had never implemented HLC or Version Vectors. I had never shipped a Rust binary to ARM. I just started and kept going.&lt;/p&gt;

&lt;p&gt;Seeing strangers find the project, fork it, contribute to it, and take a moment to say it matters, it changed how I think about what I'm building. This isn't a side project anymore. It's the most serious thing I've ever made, and I want to see it through.&lt;/p&gt;

&lt;p&gt;If you want to help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Star the repo&lt;/strong&gt; at &lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;github.com/Etoile-Bleu/ZamSync&lt;/a&gt;, it sounds small but it genuinely matters for visibility on lists like awesome-rust, which requires 50 stars. I'm at 39.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contribute&lt;/strong&gt; -- there are open good-first issues and I merge quickly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share&lt;/strong&gt; -- if you know someone working on offline-first systems, rural health tech, or Rust, send them the link&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Like and comment&lt;/strong&gt; -- it helps more people find this&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write to me&lt;/strong&gt; at &lt;a href="mailto:matheo.delbarre@epitech.eu"&gt;matheo.delbarre@epitech.eu&lt;/a&gt;, I'm a student, I have time, and I'd love to hear from anyone working on similar problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading. Thank you for the stars, the messages, and the pull requests. And thank you to whoever you are if you just took 30 seconds to say this problem matters. It does.&lt;/p&gt;

&lt;p&gt;-- Mathéo Delbarre&lt;/p&gt;

</description>
      <category>rust</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building software for places where the internet barely exists</title>
      <dc:creator>Mathéo Delbarre</dc:creator>
      <pubDate>Mon, 15 Jun 2026 22:15:57 +0000</pubDate>
      <link>https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od</link>
      <guid>https://dev.to/etoile_bleu/-i-built-a-sync-engine-for-clinics-that-run-on-2g-and-lose-power-mid-transfer-here-is-why-and-18od</guid>
      <description>&lt;p&gt;&lt;em&gt;By Mathéo Delbarre, 2nd year CS student at EPITECH Nancy, France&lt;/em&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F31yr1fru6kwvppnx1tat.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.amazonaws.com%2Fuploads%2Farticles%2F31yr1fru6kwvppnx1tat.png" alt="ZamSync" width="600" height="178"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; district health clinics in Bhutan need to sync patient data to a central hospital hub over 2G connections that drop constantly, with nodes that lose power mid-transfer.&lt;br&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; a Rust sync engine built around an encrypted append-only log, delta sync via version vectors, hybrid logical clocks for clock-drift tolerance, and mTLS for identity.&lt;br&gt;
&lt;strong&gt;Constraints:&lt;/strong&gt; under 10 MB RAM on a Raspberry Pi 3, static binary, no stable connection required.&lt;br&gt;
&lt;strong&gt;Result:&lt;/strong&gt; 5,000 events replicated with zero loss across a simulated 2G link with a mid-transfer TCP cut.&lt;br&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;github.com/Etoile-Bleu/ZamSync&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;I want to tell you about a problem I discovered last year that I couldn't stop thinking about. Not a LeetCode problem, not a framework benchmark: a real situation where data gets lost, patients get hurt, and the existing software world has basically nothing useful to offer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem, in one picture
&lt;/h2&gt;

&lt;p&gt;Before anything else, here is the system we are talking about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;District clinic (Raspberry Pi 3, 1GB RAM)
  WAL: [event_1][event_2]...[event_N]   &amp;lt;- encrypted on disk
  |
  | 2G link: 23 kbps, 600ms latency, drops every few hours
  | (or offline for days during monsoon season)
  |
Hub hospital server
  WAL: [all events from all clinics]    &amp;lt;- encrypted, segregated by origin
  |
  REST API / dashboard / reporting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each clinic writes events locally. When the link comes up, it syncs the delta to the hub. If the link drops mid-transfer, the next sync resumes exactly where it stopped. No re-transmission, no duplicates, no corrupted state.&lt;/p&gt;

&lt;p&gt;That is ZamSync. Now let me explain why I built it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The thing I found out about Bhutan
&lt;/h2&gt;

&lt;p&gt;Bhutan is a landlocked kingdom in the Himalayas with about 750,000 people spread across terrain that is some of the most difficult on Earth. The country runs a fully free public healthcare system: every citizen, regardless of location, has the right to healthcare.&lt;/p&gt;

&lt;p&gt;The problem is the word "location."&lt;/p&gt;

&lt;p&gt;Bhutan is divided into 20 administrative districts called dzongkhags. Many district hospitals and Basic Health Units (BHUs) sit at altitude, accessible only by mountain roads that wash out in monsoon season. The government has been expanding its Rural Connectivity Programme: as of 2022, over 850 villages have 2G or 4G access. But "access" and "reliable connectivity" are not the same sentence.&lt;/p&gt;

&lt;p&gt;In the highlands, the dominant backhaul option is satellite. Kacific-1, the high-throughput satellite serving Bhutan, delivers latency of around 600ms. In areas on 2G, the average rural broadband speed is 23 kilobits per second. That is not a typo. Twenty-three kbps. And it drops. Monsoons knock out towers. Terrain blocks signal. Power cuts happen.&lt;/p&gt;

&lt;p&gt;Into this landscape, the Bhutanese government started rolling out the &lt;strong&gt;electronic Patient Information System, or ePIS&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What ePIS is, and what happened when it launched
&lt;/h2&gt;

&lt;p&gt;ePIS is described as "a comprehensive, integrated Electronic Health Record system" managing patient demographics, diagnoses, medications, allergies, lab results, and imaging studies. It launched at the Jigme Dorji Wangchuck National Referral Hospital (JDWNRH) on April 18, 2023, &lt;a href="https://thebhutanese.bt/jdwnrh-to-start-using-electronic-patient-information-system-this-month/" rel="noopener noreferrer"&gt;as reported by The Bhutanese&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On launch day, when all staff logged in simultaneously, &lt;a href="https://thebhutanese.bt/electronic-patient-information-system-in-jdwnrh-hampered-by-slow-internet/" rel="noopener noreferrer"&gt;the network couldn't handle the load&lt;/a&gt;. People could not log in. Those who did were waiting to upload information on a single patient. The hospital leadership decided in real time: have every doctor see two or three patients through ePIS, and continue with paper prescriptions for the rest.&lt;/p&gt;

&lt;p&gt;Thirty to forty patients had to repeat blood tests because the lab integration failed and the system couldn't capture their phone numbers to notify them.&lt;/p&gt;

&lt;p&gt;This is a national referral hospital, in the capital, with the best connectivity in the country.&lt;/p&gt;

&lt;p&gt;They fixed it. By early 2024, ePIS had &lt;a href="https://www.bbs.bt/241390/" rel="noopener noreferrer"&gt;expanded to 58 hospitals and 186 Primary Health Centres nationwide&lt;/a&gt;, with over 604,000 patient records and 6,400 registered health professionals. Real progress.&lt;/p&gt;

&lt;p&gt;But the BBS article also says: &lt;em&gt;"extreme weather conditions and challenging terrain affect reliable internet connectivity"&lt;/em&gt;, and power and network issues continue to limit full utilization in remote areas. They are exploring Starlink. The previous system, BHMIS, used &lt;a href="https://moh.gov.bt/wp-content/uploads/2025/01/Bhutan-MoH-HSDP-Progress-Report-Blueprint-for-HIS.pdf" rel="noopener noreferrer"&gt;distributed MS Access databases where districts entered paper records and physically forwarded database files to the ministry for integration&lt;/a&gt;. No local analysis was possible.&lt;/p&gt;

&lt;p&gt;ePIS is a generational improvement over that. But the underlying challenge has not disappeared: you still need to get data from a clinic node to a hub node, over a connection that may be slow, unreliable, or completely absent for stretches of time.&lt;/p&gt;

&lt;p&gt;That is the problem I wanted to solve. I am a student, not the Ministry of Health. But the &lt;em&gt;class&lt;/em&gt; of problem: reliable, efficient, low-footprint data sync between nodes where connectivity is a privilege, not a given.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I didn't just use an existing tool
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of Rust, I spent weeks looking at what already existed. The short version:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CouchDB / PouchDB replication&lt;/strong&gt; is mature and battle-tested, but CouchDB carries ~150 MB of memory overhead and sends entire document revision trees. On a 23 kbps link, that tree overhead alone is unacceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IPFS&lt;/strong&gt; comes up often in "offline-first" conversations, and I want to address it directly because the mismatch with this use case runs deep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IPFS is built around immutable content addressing. Updating a patient record means creating a new block, getting a new CID, and updating a mutable pointer. Significant overhead for a high-frequency append-only log.&lt;/li&gt;
&lt;li&gt;The go-ipfs daemon consumes 200-400 MB at rest. A Raspberry Pi 3 with 1GB RAM cannot sustain that.&lt;/li&gt;
&lt;li&gt;IPLD graph traversal requires many round-trips per sync. On 600ms satellite latency, this is &lt;a href="https://github.com/ipfs-inactive/dynamic-data-and-capabilities/issues/3" rel="noopener noreferrer"&gt;noted in IPFS's own issue tracker&lt;/a&gt; as a bottleneck serious enough to consider "ditching the IPFS DAG sync" in favor of vector-clock-based protocols.&lt;/li&gt;
&lt;li&gt;IPFS assumes a semi-open DHT network. A clinic-to-hospital topology is &lt;em&gt;controlled&lt;/em&gt;: known nodes, fixed addresses, mTLS identity. DHT discovery is overhead you don't need and a security surface you don't want in a medical deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SQLite with manual sync logic&lt;/strong&gt; is closest, but it pushes the entire sync problem onto the application: no standard protocol, no connection-drop handling, no ordering guarantees across nodes with drifting clocks.&lt;/p&gt;

&lt;p&gt;Here is the comparison across the axes that actually matter:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;ZamSync&lt;/th&gt;
&lt;th&gt;CouchDB/PouchDB&lt;/th&gt;
&lt;th&gt;IPFS&lt;/th&gt;
&lt;th&gt;SQLite (manual)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Memory at rest&lt;/td&gt;
&lt;td&gt;~4 MB&lt;/td&gt;
&lt;td&gt;~150 MB&lt;/td&gt;
&lt;td&gt;~200-400 MB&lt;/td&gt;
&lt;td&gt;~2 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline-first native&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;No protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta sync&lt;/td&gt;
&lt;td&gt;Version vectors&lt;/td&gt;
&lt;td&gt;Partial (rev tree)&lt;/td&gt;
&lt;td&gt;No (full DAG)&lt;/td&gt;
&lt;td&gt;You build it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARM static binary&lt;/td&gt;
&lt;td&gt;Yes (~9 MB)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mTLS built-in&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection drop resume&lt;/td&gt;
&lt;td&gt;Exact position&lt;/td&gt;
&lt;td&gt;Restarts&lt;/td&gt;
&lt;td&gt;Restarts&lt;/td&gt;
&lt;td&gt;You build it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clock drift handling&lt;/td&gt;
&lt;td&gt;HLC&lt;/td&gt;
&lt;td&gt;NTP required&lt;/td&gt;
&lt;td&gt;NTP required&lt;/td&gt;
&lt;td&gt;You build it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  How ZamSync works: the three core ideas
&lt;/h2&gt;

&lt;p&gt;Before going deep, here is a quick map. ZamSync is built on three ideas that compound on each other. Each one solves a specific failure mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failure mode                 Solution in ZamSync
--------------------------   -------------------
Data lost on power cut    -&amp;gt; Write-Ahead Log (WAL): encrypted, append-only, CRC32 per record
Clock drift across nodes  -&amp;gt; Hybrid Logical Clocks: stays near wall time but tolerates drift
Bandwidth wasted on resync -&amp;gt; Version Vectors: exact delta, zero re-transmission
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me go through each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Write-Ahead Log
&lt;/h2&gt;

&lt;p&gt;An append-only log is one of the oldest ideas in databases. When you want durability without the cost of random-access writes, you write everything sequentially to a file. The WAL in ZamSync stores events as fixed-format binary records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────────────────────┐
│  Record wire format                                                    │
│                                                                        │
│  [4 bytes: magic]  [8 bytes: HLC timestamp]  [8 bytes: sequence]      │
│  [4 bytes: payload_len]  [PAYLOAD]  [4 bytes: CRC32]                  │
│                                                                        │
│  Nonce (96 bits, random per record) prepended when encryption enabled  │
└────────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every record has a CRC32 integrity check. If the process dies mid-write, the partial record is detected on recovery and truncated. This is the same principle PostgreSQL's WAL uses, just much simpler because we don't need transactions or rollback.&lt;/p&gt;

&lt;p&gt;Encryption is ChaCha20-Poly1305 with a fresh random 96-bit nonce per record. ChaCha20 was chosen over AES-GCM deliberately: it requires no hardware acceleration, which matters on ARMv7 Raspberry Pi 3s where AES-NI does not exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Hybrid Logical Clocks: solving the clock drift problem
&lt;/h2&gt;

&lt;p&gt;Here is a thing that breaks almost every naive sync implementation: clocks drift.&lt;/p&gt;

&lt;p&gt;A quick primer on the problem for those not deep in distributed systems: in a system where multiple nodes each generate events independently, you need a way to order those events globally. The obvious approach is to use the node's wall clock. The problem: clocks drift. A clinic offline for three days will have a clock that is slightly off from the hub's clock. Event A arrives with timestamp T+5, event B arrives with timestamp T+3, and now your log is out of order even though B happened after A.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lamport clocks&lt;/strong&gt; (Leslie Lamport, 1978) solve causality by using a logical counter that advances on every event and communication. But they lose all connection to physical time, making debugging and forensic analysis painful.&lt;/p&gt;

&lt;p&gt;ZamSync uses &lt;strong&gt;Hybrid Logical Clocks (HLC)&lt;/strong&gt;, from the 2014 paper &lt;a href="https://link.springer.com/chapter/10.1007/978-3-319-14472-6_2" rel="noopener noreferrer"&gt;"Logical Physical Clocks and Consistent Snapshots in Globally Distributed Databases"&lt;/a&gt; by Kulkarni, Demirbas, Madeppa, and Avva. The idea: maintain a logical component like a Lamport clock, but keep it anchored close to physical time so values remain human-readable. The logical part only advances beyond physical time when the network requires it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HLC = max(physical_now, last_seen_hlc) + ticks_if_collision

Normal case (clocks in sync):
  Wall clock:  |--T1-----T2--T3--------T4--|
  HLC:         |--T1-----T2--T3--------T4--|  &amp;lt;- stays near wall clock

Clock drift (clinic B is 2 seconds behind):
  Wall clock B:|--T1'----T2'------------|
  HLC B:       |--T1-----T2------------|  &amp;lt;- corrected by received HLC from A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A node offline for a week with a drifted clock will produce correctly ordered events when it reconnects. The HLC is monotonically increasing within a node and converges to global order across nodes.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Version Vectors: knowing exactly what you don't know
&lt;/h2&gt;

&lt;p&gt;The sync protocol is built around &lt;strong&gt;Version Vectors&lt;/strong&gt;. If you haven't encountered them before: a version vector is a map of &lt;code&gt;node_id -&amp;gt; max_sequence_seen&lt;/code&gt;. Each node maintains one. When two nodes connect, they exchange vectors and compute the gap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hub vector:      { clinic_a: 847, clinic_b: 312, hub: 5001 }
Clinic A vector: { clinic_a: 923, clinic_b: 0,   hub: 4800 }

Gap analysis:
  Hub is missing:      clinic_a events 848..923
  Clinic A is missing: clinic_b events 1..312
                       hub events 4801..5001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;find_gaps&lt;/code&gt; function computes the exact missing ranges. Only those are transmitted. No full state transfer, no polling, no "did you get this?" handshakes. Bandwidth usage is proportional to the actual delta.&lt;/p&gt;

&lt;p&gt;Here is what this means concretely on a constrained link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5000-event backlog, 30 KB/s link:

Without delta sync:
  Full transfer: ~18 MB
  At 30 KB/s:   613 seconds = 10+ minutes
  On drop:      start over from zero

With ZamSync:
  Session 1 (cut at 8s): events 1-240 transferred, 24 KB
  Session 2 (resume):    gap vector shows 241-5000 missing
                         picks up at 241, zero re-transmission
  Total overhead:        0 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  mTLS and access control
&lt;/h2&gt;

&lt;p&gt;In a health deployment, you cannot let arbitrary nodes connect to the hub. ZamSync uses mutual TLS: both sides present a certificate during the handshake.&lt;/p&gt;

&lt;p&gt;The hub generates a CA certificate and signs node certificates for each clinic. If a clinic doesn't have a certificate signed by the hub's CA, the connection is rejected at the TLS layer before any data is read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hub CA (ECDSA P-256)
   |
   +-- hub node cert    (signed by CA)
   +-- clinic_a cert    (signed by CA, issued via: zamsync sign)
   +-- clinic_b cert    (signed by CA, issued via: zamsync sign)

clinic_c with a self-signed cert: rejected before any payload is read.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beyond authentication, ZamSync implements &lt;code&gt;--policy own&lt;/code&gt;: when Clinic A syncs, the hub only returns events that originate from Clinic A. Clinic B's data is at the hub but never disclosed to Clinic A. This is enforced at the protocol level, not middleware.&lt;/p&gt;




&lt;h2&gt;
  
  
  What running it actually looks like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Hub: generate CA and hub cert, then serve&lt;/span&gt;
zamsync keygen /var/lib/zamsync/hub
zamsync serve &lt;span class="nt"&gt;--data-dir&lt;/span&gt; /var/lib/zamsync/hub &lt;span class="nt"&gt;--bind&lt;/span&gt; 0.0.0.0:7700 &lt;span class="nt"&gt;--policy&lt;/span&gt; own

&lt;span class="c"&gt;# Clinic: generate keypair, get signed by hub&lt;/span&gt;
zamsync keygen /var/lib/zamsync/clinic_a
zamsync sign /var/lib/zamsync/clinic_a/tls/node.csr &lt;span class="nt"&gt;--ca&lt;/span&gt; /var/lib/zamsync/hub

&lt;span class="c"&gt;# Push an event and sync&lt;/span&gt;
zamsync push &lt;span class="nt"&gt;--data-dir&lt;/span&gt; /var/lib/zamsync/clinic_a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"patient_id":"BT-0042","event":"registration"}'&lt;/span&gt;

zamsync &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--data-dir&lt;/span&gt; /var/lib/zamsync/clinic_a &lt;span class="nt"&gt;--hub&lt;/span&gt; 192.168.1.10:7700

&lt;span class="c"&gt;# Check status&lt;/span&gt;
zamsync status &lt;span class="nt"&gt;--data-dir&lt;/span&gt; /var/lib/zamsync/clinic_a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Node ID:     clinic_a
  WAL records: 1247
  Last sync:   2 minutes ago  (hub: 192.168.1.10:7700)
  Pending:     0 events unsynced
  WAL size:    124 KB (encrypted)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The binary is a single static musl executable (~9 MB) with no shared library dependencies. Installation on a remote node is one &lt;code&gt;curl&lt;/code&gt; command, no package manager, no dependency resolution.&lt;/p&gt;




&lt;h2&gt;
  
  
  The test that actually matters
&lt;/h2&gt;

&lt;p&gt;I did not want to claim "this works on bad networks" based on localhost benchmarks.&lt;/p&gt;

&lt;p&gt;The integration test suite uses Docker Compose and &lt;a href="https://github.com/Shopify/toxiproxy" rel="noopener noreferrer"&gt;Toxiproxy&lt;/a&gt;, Shopify's network condition simulator. The setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hub and clinic in separate containers, Toxiproxy between them.&lt;/li&gt;
&lt;li&gt;Link configured: 600ms latency, 100ms jitter, 30 KB/s bandwidth cap.&lt;/li&gt;
&lt;li&gt;5,000 events generated on the clinic.&lt;/li&gt;
&lt;li&gt;Sync starts.&lt;/li&gt;
&lt;li&gt;Mid-transfer: TCP connection cut for several seconds.&lt;/li&gt;
&lt;li&gt;Reconnect, sync resumes.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clinic                Toxiproxy (2G sim)           Hub
  |                        |                         |
  |------ connect --------&amp;gt;|------- connect --------&amp;gt;|
  |&amp;lt;-- version vectors ----|&amp;lt;-- version vectors ------|
  |--- events 1..240 -----&amp;gt;|---- events 1..240 ------&amp;gt;|
  |                        |                         |
  |      [TCP cut injected by Toxiproxy here]        |
  |                        |                         |
  |------ reconnect ------&amp;gt;|------- reconnect -------&amp;gt;|
  |&amp;lt;-- version vectors ----|&amp;lt;-- {clinic_a: 240} ------|
  |--- events 241..5000 --&amp;gt;|--- events 241..5000 -----&amp;gt;|
  |&amp;lt;---- sync complete ----|&amp;lt;----- 5000/5000. 0 dup ---|
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: all 5,000 events replicated, zero loss, zero duplicates, no corrupted state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Etoile-Bleu/ZamSync
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; tests/docker-compose.test.yml up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;--abort-on-container-exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Memory footprint
&lt;/h2&gt;

&lt;p&gt;Phase 14 benchmark: 5-clinic simulation, all Toxiproxy constraints active.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hub node:      ~7.8 MB RSS at peak   (5000 events, 5 concurrent clinic connections)
Clinic node:   ~4.2 MB RSS at peak   (1000 local events, 1 outbound sync)

For comparison:
  CouchDB:        ~150 MB at rest
  PostgreSQL:     ~120 MB at rest
  go-ipfs daemon: ~200-400 MB at rest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under 10 MB is a hard design requirement, not an accident.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned the hard way
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Clock synchronization is weirder than you expect.&lt;/strong&gt; Raspberry Pis that reconnect to a network after being offline sometimes have their clocks jump backward by several seconds when NTP kicks in. If the HLC does not handle the monotonicity invariant during that window, you produce events with timestamps lower than previously issued ones, and the version vector logic breaks silently. I found this the first time I ran the simulation on actual hardware, not on localhost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upstream library breaking changes are a real thing.&lt;/strong&gt; The &lt;code&gt;rcgen&lt;/code&gt; crate (TLS certificate generation) changed &lt;code&gt;CertificateParams::signed_by&lt;/code&gt; from three arguments to two in version 0.14: instead of passing &lt;code&gt;(node_key, ca_cert, ca_key)&lt;/code&gt; separately, you now construct an &lt;code&gt;Issuer&lt;/code&gt; struct first with &lt;code&gt;Issuer::from_params(&amp;amp;ca_params, ca_key)&lt;/code&gt;, which moves the key. This means serializing the CA key PEM before the move, or losing access to it. Forty-line fix, not obvious, failed CI until I read the source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Release automation and Cargo.lock.&lt;/strong&gt; The release workflow bumped the version in &lt;code&gt;Cargo.toml&lt;/code&gt; via &lt;code&gt;sed&lt;/code&gt;, then tried to &lt;code&gt;cargo publish&lt;/code&gt;. Failed every time with a "dirty working tree" error. The reason: &lt;code&gt;sed&lt;/code&gt; updated &lt;code&gt;Cargo.toml&lt;/code&gt; but never ran cargo, so &lt;code&gt;Cargo.lock&lt;/code&gt; still had the old version. Fix: &lt;code&gt;cargo generate-lockfile&lt;/code&gt; in the release job before committing. Obvious in hindsight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker ARM emulation vs real hardware.&lt;/strong&gt; QEMU's ARM emulation does not accurately represent CPU scheduling on real ARMv7 silicon. A race condition in the connection accept loop appeared only on real hardware under load and was masked by QEMU's single-threaded emulation. The Phase 14 test is designed to eventually run on real hardware, not just CI containers, for this reason.&lt;/p&gt;




&lt;h2&gt;
  
  
  Codebase structure
&lt;/h2&gt;

&lt;p&gt;The project is a Rust workspace with four crates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZamSync/
├── crates/
│   ├── zamsync-core/       Pure logic: events, HLC, version vectors, port traits
│   │                       No I/O. Tested with fake backends.
│   ├── zamsync-storage/    WAL, SQLite metadata, encryption
│   ├── zamsync-network/    TCP transport, frame protocol, mTLS
│   └── zamsync-testing/    Shared test helpers, in-process test nodes
└── src/                    CLI: serve, sync, keygen, sign, status, push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The architecture is hexagonal: &lt;code&gt;zamsync-core&lt;/code&gt; defines port traits, the other crates implement them. Core logic has zero I/O dependencies and can be fully tested without disk or network.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this is going
&lt;/h2&gt;

&lt;p&gt;ZamSync is a sync engine, not an application. It does not know what a "patient" is. An application layer sits on top: the thing that renders a patient form, validates data, calls &lt;code&gt;push_event(payload)&lt;/code&gt; and &lt;code&gt;pull_events()&lt;/code&gt;. That layer is out of scope for me alone.&lt;/p&gt;

&lt;p&gt;The vision is to present ZamSync to the Ministry of Health of Bhutan as a building block for exactly this: when a BHU loses connectivity for hours or days, local data survives and syncs cleanly when the link returns. That is a solved problem with ZamSync.&lt;/p&gt;

&lt;p&gt;The roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 15 (in progress):&lt;/strong&gt; async Tokio runtime for better concurrency on single-core hardware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 16:&lt;/strong&gt; mDNS peer discovery for local clinic networks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 17:&lt;/strong&gt; conflict resolution primitives (last-write-wins default, application-level merge hooks).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 18:&lt;/strong&gt; bandwidth budgeting, &lt;code&gt;sync_budget_kbps&lt;/code&gt; to leave capacity for actual clinical use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 21:&lt;/strong&gt; structured error codes Z1xx-Z6xx with colored CLI output and JSON formatting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full roadmap: &lt;a href="https://github.com/Etoile-Bleu/ZamSync/blob/main/ROADMAP.md" rel="noopener noreferrer"&gt;ROADMAP.md&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;The core ideas in ZamSync are not mine. I implemented them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kulkarni, Demirbas, Madeppa, Avva.&lt;/strong&gt; "Logical Physical Clocks and Consistent Snapshots in Globally Distributed Databases." OPODIS 2014. &lt;a href="https://link.springer.com/chapter/10.1007/978-3-319-14472-6_2" rel="noopener noreferrer"&gt;Springer&lt;/a&gt;. The HLC paper.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mattern, F.&lt;/strong&gt; "Virtual Time and Global States of Distributed Systems." 1989. Version vectors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dean &amp;amp; Ghemawat.&lt;/strong&gt; "The Google File System." SOSP 2003. Append-only log design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rust crates:&lt;/strong&gt; &lt;code&gt;rustls&lt;/code&gt;, &lt;code&gt;rcgen&lt;/code&gt;, &lt;code&gt;rusqlite&lt;/code&gt;, &lt;code&gt;zstd&lt;/code&gt;, &lt;code&gt;tokio&lt;/code&gt;, &lt;code&gt;chacha20poly1305&lt;/code&gt;. Production-grade, audited.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopify's Toxiproxy&lt;/strong&gt; for network simulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full citations: &lt;a href="https://github.com/Etoile-Bleu/ZamSync/blob/main/ACKNOWLEDGEMENTS.md" rel="noopener noreferrer"&gt;ACKNOWLEDGEMENTS.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Bhutan sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thebhutanese.bt/electronic-patient-information-system-in-jdwnrh-hampered-by-slow-internet/" rel="noopener noreferrer"&gt;The Bhutanese: ePIS hampered by slow internet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thebhutanese.bt/introduction-of-epis-transforms-patient-information-management-in-bhutan/" rel="noopener noreferrer"&gt;The Bhutanese: Introduction of ePIS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bbs.bt/241390/" rel="noopener noreferrer"&gt;BBS: Power and network issues affect ePIS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://moh.gov.bt/wp-content/uploads/2025/01/Bhutan-MoH-HSDP-Progress-Report-Blueprint-for-HIS.pdf" rel="noopener noreferrer"&gt;Ministry of Health Bhutan: HIS Blueprint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdn.who.int/media/docs/default-source/digital-health-documents/global-observatory-on-digital-health/ict-masterplan-final-.pdf" rel="noopener noreferrer"&gt;WHO: Healthcare ICT Master Plan, Bhutan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netstoneanalytics.com/2026/05/19/through-the-government-s-rural-connectivity-programme-mobile-network-coverage-ha/" rel="noopener noreferrer"&gt;Netstone: Bhutan rural mobile coverage expansion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tech.gov.bt/episproject/" rel="noopener noreferrer"&gt;GovTech Bhutan: ePIS project page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How you can help
&lt;/h2&gt;

&lt;p&gt;I can write Rust. I cannot market a project. I want to be direct about what kind of help would actually move this forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want to give technical feedback:&lt;/strong&gt;&lt;br&gt;
The areas I am least confident about are the &lt;code&gt;receive()&lt;/code&gt; fairness behavior under concurrent clinic connections and whether the Version Vector gap detection handles all edge cases. Open an issue, leave a comment, send a message. Code review is the most valuable thing anyone can offer right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want to contribute code:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/Etoile-Bleu/ZamSync/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22" rel="noopener noreferrer"&gt;Good First Issues&lt;/a&gt; are labeled. The current one is implementing the &lt;code&gt;zamsync setup --hub&lt;/code&gt; interactive wizard: keygen, systemd unit installation, and setup checklist in a guided flow. Self-contained, no prior codebase knowledge required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you work in health tech, field data systems, or low-resource computing:&lt;/strong&gt;&lt;br&gt;
I would genuinely like to know if this kind of problem is something you have encountered in other contexts: humanitarian data collection, field research, rural logistics, anything that involves nodes that sync intermittently over constrained links. And if you have any idea how to get this in front of the people at health ministries who actually make infrastructure decisions, I would love to hear it.&lt;/p&gt;

&lt;p&gt;A star on &lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;the repository&lt;/a&gt; also helps more than you might think.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;https://github.com/Etoile-Bleu/ZamSync&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;p&gt;Mathéo&lt;/p&gt;

</description>
      <category>rust</category>
      <category>showdev</category>
      <category>opensource</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Simulating 2G to build an offline-first sync engine in Rust for rural clinics</title>
      <dc:creator>Mathéo Delbarre</dc:creator>
      <pubDate>Sun, 14 Jun 2026 16:11:46 +0000</pubDate>
      <link>https://dev.to/etoile_bleu/simulating-2g-to-build-an-offline-first-sync-engine-in-rust-for-rural-clinics-38bd</link>
      <guid>https://dev.to/etoile_bleu/simulating-2g-to-build-an-offline-first-sync-engine-in-rust-for-rural-clinics-38bd</guid>
      <description>&lt;p&gt;hey dev.to,&lt;/p&gt;

&lt;p&gt;so I am Mathéo, a 2nd year CS student at EPITECH Nancy (France). I want to share a project I have been building for the last few months: &lt;strong&gt;ZamSync&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Basically, it is a lightweight synchronization engine written in Rust. It is made for places where the internet is either extremely slow, drops every five minutes, or does not exist at all (think remote areas, Raspberry Pi nodes, offline-first apps).&lt;/p&gt;

&lt;p&gt;The exact scenario I had in mind is the electronic Patient Information System (ePIS) of Bhutan. In rural Bhutan, district clinics need to register patients and sync the data back to central hospital hubs. The problem is the network: they often have 2G connections, 600ms latency, and constant cuts. &lt;/p&gt;

&lt;p&gt;I looked at existing tools, but they either require a permanent stable connection or need a heavy database engine running on the client. I wanted to build something that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Works over days of total disconnection.&lt;/li&gt;
&lt;li&gt;Recover instantly if the network drops mid-sync (without duplicating data).&lt;/li&gt;
&lt;li&gt;Runs on tiny hardware (under 10MB of RAM on a Raspberry Pi).&lt;/li&gt;
&lt;li&gt;Keeps everything secure (encrypted at rest and in transit).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the GitHub repository if you want to inspect the code:&lt;br&gt;
&lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;https://github.com/Etoile-Bleu/ZamSync&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  How it works under the hood
&lt;/h2&gt;

&lt;p&gt;The engine is built around a simple principle: append-only logs. &lt;/p&gt;

&lt;p&gt;Instead of syncing a database state directly, ZamSync syncs &lt;strong&gt;events&lt;/strong&gt; (which can carry JSON payloads, like patient check-ins). &lt;/p&gt;

&lt;p&gt;Here is the technical stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write-Ahead Log (WAL):&lt;/strong&gt; Events are written locally to an encrypted, append-only file. Each record has a CRC32 integrity check to prevent corruption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid Logical Clocks (HLC):&lt;/strong&gt; Since clinics do not have reliable NTP servers to sync their system clocks, we use HLCs to order events deterministically across different nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Vectors:&lt;/strong&gt; When two nodes connect, they exchange their version vectors (essentially a map of who has seen what sequence number). They compare them, find the exact gaps, and only send the missing events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAL Encryption:&lt;/strong&gt; The log file is encrypted at rest using ChaCha20-Poly1305. A random 96-bit nonce is generated for every single record, and keys can be rotated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mTLS (mutual TLS):&lt;/strong&gt; The network layer uses custom mTLS certificates signed by the hub CA. If a node does not have a valid signed certificate, it is rejected during the TLS handshake before any data can be read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also implemented a &lt;code&gt;--policy own&lt;/code&gt; access control. If Clinic A and Clinic B both sync to the same Hospital Hub, Clinic A cannot download the events submitted by Clinic B. The hub keeps everything, but restricts sync replies based on the client certificate identity.&lt;/p&gt;


&lt;h2&gt;
  
  
  Simulating the 2G network with Toxiproxy
&lt;/h2&gt;

&lt;p&gt;To verify if this actually works in real-world conditions, I did not want to just write standard unit tests on localhost. &lt;/p&gt;

&lt;p&gt;I set up a test suite using Docker Compose and &lt;strong&gt;Toxiproxy&lt;/strong&gt; (an awesome tool by Shopify to simulate network failure). The test setup does this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Starts a hub node and a client node.&lt;/li&gt;
&lt;li&gt;Interposes Toxiproxy between them.&lt;/li&gt;
&lt;li&gt;Sets the link to emulate a bad rural 2G network: 600ms latency, 100ms jitter, 30 KB/s bandwidth cap.&lt;/li&gt;
&lt;li&gt;Generates 5,000 events.&lt;/li&gt;
&lt;li&gt;In the middle of the transfer, the test script cuts the connection completely for a few seconds.&lt;/li&gt;
&lt;li&gt;Reconnects and runs the sync again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, all 5,000 events are replicated with zero loss, zero duplicates, and no corrupted states.&lt;/p&gt;

&lt;p&gt;If you have Docker installed, you can run this test yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; tests/docker-compose.test.yml up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;--abort-on-container-exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The codebase structure
&lt;/h2&gt;

&lt;p&gt;The project uses a clean Rust workspace split into 4 crates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;zamsync-core&lt;/code&gt;: Pure state logic, events, HLCs, and ports. No I/O.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zamsync-storage&lt;/code&gt;: The engine implementation and the WAL filesystem code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zamsync-network&lt;/code&gt;: The network layer, TCP transport, frame format, and mTLS logic.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zamsync-testing&lt;/code&gt;: Helper tools and integration tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also cross-compile static musl binaries for &lt;code&gt;x86_64&lt;/code&gt;, &lt;code&gt;aarch64&lt;/code&gt; (Raspberry Pi 4), and &lt;code&gt;armv7&lt;/code&gt; (Raspberry Pi 3) so that installing it on remote Linux machines is just a curl command with no library dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next steps &amp;amp; feedback
&lt;/h2&gt;

&lt;p&gt;This is my first time building a low-level sync engine in Rust, and I would love to get feedback from other systems engineers. &lt;/p&gt;

&lt;p&gt;Does the WAL approach make sense for this use case? Are there edge cases in my Version Vector implementation that I might have missed?&lt;/p&gt;

&lt;p&gt;If you want to check the architecture or play with the CLI, here is the link:&lt;br&gt;
&lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;GitHub - Etoile-Bleu/ZamSync&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mathéo&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>architecture</category>
      <category>networking</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
