<?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>POC of a silent data-loss bug in Primero, UNICEF's child</title>
      <dc:creator>Mathéo Delbarre</dc:creator>
      <pubDate>Fri, 17 Jul 2026 20:40:20 +0000</pubDate>
      <link>https://dev.to/etoile_bleu/poc-of-a-silent-data-loss-bug-in-primero-unicefs-child-388d</link>
      <guid>https://dev.to/etoile_bleu/poc-of-a-silent-data-loss-bug-in-primero-unicefs-child-388d</guid>
      <description>&lt;p&gt;Two protection officers, one case. A child, unaccompanied, flagged for a possible trafficking risk near a border crossing. Neither officer has a signal. One of them updates the case's risk level. So does the other, a few days later, from a different village, not knowing the first one touched it.&lt;/p&gt;

&lt;p&gt;Whichever update reaches the server second just wins. The first one isn't shown anywhere. Nobody gets an error, nobody gets a warning. The only way to know it happened is to go digging through an audit log you had no reason to open.&lt;/p&gt;

&lt;p&gt;That's what I found in Primero, the case management system UNICEF uses for child protection and gender-based violence response in conflict zones and refugee camps. Here's how I found it, and where I got it wrong before I got it right.&lt;/p&gt;

&lt;p&gt;I build &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"&gt;ZamSync&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Primero runs offline in the browser. It queues your edits in IndexedDB and replays them as PATCH requests once you're back online. My first guess was that two people editing the same case offline at the same time would step on each other, full stop. I was wrong about that, and I'd rather say so than quietly bury my first draft.&lt;/p&gt;

&lt;p&gt;The PATCH body is a real field-level diff:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compactValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValues&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;difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValues&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two devices editing different fields on the same case merge fine. One updates a follow-up note, the other updates a risk level, both survive. That's the correct design, and I checked it carefully because I almost reported it as broken.&lt;/p&gt;

&lt;p&gt;The gap only opens when two devices touch the &lt;em&gt;same&lt;/em&gt; field while both offline. I went looking for the check I expected, something comparing the incoming value against whatever the server currently holds before writing it. There isn't one. Not in the client queue, not in the Rails controller either: the update action loads the record, applies the patch, saves it. No version check in between.&lt;/p&gt;

&lt;p&gt;Last write wins. The earlier value isn't gone from the database, Primero logs a &lt;code&gt;RecordHistory&lt;/code&gt; row on every save, so someone determined enough could dig it back out. But nothing merges the two edits, nothing flags the conflict, nothing tells either worker to go look. The case just quietly shows the second number, as if the first report never came in.&lt;/p&gt;

&lt;p&gt;There's a second, separate problem, &lt;code&gt;QUEUE_ALLOWED_RETRIES&lt;/code&gt; is hardcoded to 3. A queued edit that fails to sync three times gets dropped for good, and for anything that isn't an attachment, nothing tells you it happened. A field worker whose connection just won't cooperate for a few days, not a rare situation in the places this runs, can lose an edit with no error and no trace, because it never reached the server in the first place. There's no history row for something that was never saved.&lt;/p&gt;

&lt;p&gt;I've read enough writeups where "I read the code and I think there's a bug" turned out to be wrong that I didn't want to stop there. So I built a small proof of concept: &lt;a href="https://github.com/Etoile-Bleu/zamsync-primero-poc" rel="noopener noreferrer"&gt;zamsync-primero-poc&lt;/a&gt;. It takes zamsync-core, unmodified, compiles it to WebAssembly, and runs it against both scenarios next to a faithful copy of Primero's real queue and update logic, built straight from their source. The difference showed up exactly where the code said it would. Same-field conflicts get preserved and ordered instead of overwritten. A sync attempt that fails doesn't get a strike counted against it, it just sits in a durable log until one succeeds, whether that's the fifth try or the five hundredth.&lt;/p&gt;

&lt;p&gt;I opened &lt;a href="https://github.com/primeroIMS/primero/issues/508" rel="noopener noreferrer"&gt;an issue&lt;/a&gt; with both findings, the exact files and lines, and a link to the POC. I tried to be careful about two things. I'm not claiming Primero's offline mode is broken in general, only that these two scenarios behave this way, and I said that plainly. And if this turns out to be a known, accepted tradeoff, I said I'd rather understand why than assume I'd found something nobody had thought about. I haven't heard back yet.&lt;/p&gt;

&lt;p&gt;The part worth admitting: my first draft of this claimed any two offline edits to the same case would conflict, not just edits to the same field. That's not what the code does. If I'd published that version I'd have been describing a bug next to one that doesn't exist, and muddying the one that does. Rereading the diffing logic narrowed the claim to something smaller and, I think, more useful, a precise report with a file and a line number is a lot harder to wave off than a vague one.&lt;/p&gt;

&lt;p&gt;If you work on Primero, or on offline-first systems in general, I'd like to hear where I've got this wrong. And if this kind of thing is useful to you, &lt;a href="https://github.com/Etoile-Bleu/ZamSync" rel="noopener noreferrer"&gt;ZamSync&lt;/a&gt; and &lt;a href="https://github.com/Etoile-Bleu/MenSung" rel="noopener noreferrer"&gt;MenSung&lt;/a&gt; are both open.&lt;/p&gt;

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

</description>
      <category>showdev</category>
      <category>rust</category>
      <category>opensource</category>
      <category>security</category>
    </item>
    <item>
      <title>Checking drug interactions in places where the internet barely exists</title>
      <dc:creator>Mathéo Delbarre</dc:creator>
      <pubDate>Thu, 16 Jul 2026 10:31:41 +0000</pubDate>
      <link>https://dev.to/etoile_bleu/checking-drug-interactions-in-places-where-the-internet-barely-exists-2om1</link>
      <guid>https://dev.to/etoile_bleu/checking-drug-interactions-in-places-where-the-internet-barely-exists-2om1</guid>
      <description>&lt;p&gt;&lt;em&gt;By Mathéo Delbarre, 3rd year CS student at EPITECH Nancy, France&lt;/em&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; medical workers in war zones, refugee camps, and rural clinics have to make drug interaction decisions with no internet, on hardware that predates most currently-supported software. A missed contraindication there is not a bug report, it is a patient at risk.&lt;br&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; MenSung, an offline medication interaction checker in Rust. One static binary, one database file, every lookup fully local.&lt;br&gt;
&lt;strong&gt;Constraints:&lt;/strong&gt; runs on a 10-15 year old laptop (Core 2 Duo, 2-4 GB RAM, slow HDD), lookups under 5ms, database integrity verified on every open.&lt;br&gt;
&lt;strong&gt;Data:&lt;/strong&gt; DDInter interactions enriched with RxNorm, WHO ATC, PubChem, and openFDA drug labels. Every license checked directly, not assumed.&lt;br&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Etoile-Bleu/MenSung" rel="noopener noreferrer"&gt;github.com/Etoile-Bleu/MenSung&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;The last time I wrote here, I told you about ZamSync, a sync engine 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.us-east-2.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.us-east-2.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;5&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;
              

              5&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;And then I wrote about what happened after, because I genuinely was not prepared for it:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/etoile_bleu/39-stars-my-first-open-source-contributor-and-a-message-i-keep-re-reading-3bl2" class="crayons-story__hidden-navigation-link"&gt;39 stars, my first open source contributor, and a message I keep re-reading&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.us-east-2.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-3957573" 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.us-east-2.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/39-stars-my-first-open-source-contributor-and-a-message-i-keep-re-reading-3bl2" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 21&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/39-stars-my-first-open-source-contributor-and-a-message-i-keep-re-reading-3bl2" id="article-link-3957573"&gt;
          39 stars, my first open source contributor, and a message I keep re-reading
        &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/beginners"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;beginners&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/39-stars-my-first-open-source-contributor-and-a-message-i-keep-re-reading-3bl2" 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/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&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;9&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/39-stars-my-first-open-source-contributor-and-a-message-i-keep-re-reading-3bl2#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              4&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;
            5 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 need to start this article by talking about that, because without it, MenSung would not exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  What your kindness actually did 💝
&lt;/h2&gt;

&lt;p&gt;Since those articles: strangers starred the repo. People forked it to build things I never imagined, like offline-first sync for password managers. KairosOps opened a pull request that fixed a real Windows bug in my code, and I still remember sitting there staring at the notification (I was so happy that someone saw my issue and added a fix 😭). And per-oestergaard left that message in the discussions, the one I keep re-reading, just to say the problem mattered and he was sorry he couldn't help more. He apologized for only being kind. I still think about that.&lt;/p&gt;

&lt;p&gt;I said in my last article that the message reminded me why I was building. What I didn't fully understand at the time is that it also changed &lt;em&gt;what&lt;/em&gt; I would build next. When strangers from countries you've never visited tell you "this problem is real, keep going", the question stops being "should I keep doing this?" and becomes "okay, what else is broken out there?"&lt;/p&gt;

&lt;p&gt;Also, some personal news: I passed my second year! 🎉 I'm now a third-year CS student at EPITECH Nancy. The exams happened somewhere between two pull requests, and honestly, the energy from you all carried me through more of that revision period than I'd like to admit haha.&lt;/p&gt;

&lt;p&gt;So this is the next project. It stays in the same world as ZamSync, offline-first software for places where the internet barely exists, but it moves one step further down the chain: from &lt;em&gt;moving&lt;/em&gt; medical data to &lt;em&gt;using&lt;/em&gt; it at the moment of care.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem I couldn't stop thinking about (again)
&lt;/h2&gt;

&lt;p&gt;While researching how clinics in low-connectivity regions actually work for ZamSync, I kept running into a moment in the workflow that scared me more than data loss did.&lt;/p&gt;

&lt;p&gt;A patient in a field clinic is already taking one drug. A medical worker is about to give them a second one. The question is simple and brutal: &lt;strong&gt;is this combination safe?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have internet, this is a solved problem. &lt;a href="https://reference.medscape.com/drug-interactionchecker" rel="noopener noreferrer"&gt;Medscape&lt;/a&gt;, &lt;a href="https://go.drugbank.com/drug-interaction-checker" rel="noopener noreferrer"&gt;DrugBank&lt;/a&gt;, a dozen interaction checkers, all one search away. If you don't have internet, the answer is a paper reference book that may be years out of date, someone's memory at the end of a 14-hour shift, or nothing.&lt;/p&gt;

&lt;p&gt;And this is not a small or hypothetical population. Here is what the numbers say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The WHO estimates the global cost of medication errors at &lt;strong&gt;US$42 billion annually&lt;/strong&gt;, and made reducing medication-related harm its &lt;a href="https://www.who.int/initiatives/medication-without-harm" rel="noopener noreferrer"&gt;third Global Patient Safety Challenge&lt;/a&gt;. Medication harm accounts for roughly half of all preventable harm in medical care.&lt;/li&gt;
&lt;li&gt;Drug-drug interactions specifically are a major slice of that: a &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC9483724/" rel="noopener noreferrer"&gt;2022 retrospective study of ten years of adverse drug reaction reports&lt;/a&gt; found that roughly one in six adverse drug reactions was correlated with a drug-drug interaction, and a &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC12459552/" rel="noopener noreferrer"&gt;descriptive analysis of the FDA's adverse event reporting system&lt;/a&gt; links DDIs to tens of thousands of emergency visits and around 195,000 hospitalizations per year in the US alone. That is a country &lt;em&gt;with&lt;/em&gt; internet and &lt;em&gt;with&lt;/em&gt; pharmacists at every corner.&lt;/li&gt;
&lt;li&gt;Now move to the settings I actually care about. A &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC9223639/" rel="noopener noreferrer"&gt;study in Zaatari refugee camp in Jordan&lt;/a&gt; identified &lt;strong&gt;3,572 drug-therapy problems across 1,533 patients, a mean of 2.33 problems per patient&lt;/strong&gt;, driven by the lack of structured pharmaceutical care. A &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC8450024/" rel="noopener noreferrer"&gt;review of hospitalized patients in low-resource settings&lt;/a&gt; found potential drug-drug interactions in the majority of the patients studied, with the tools to catch them largely absent. And pharmacists working in humanitarian crises have been &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC7150111/" rel="noopener noreferrer"&gt;documenting for years&lt;/a&gt; that they are asked to do this job with almost no infrastructure.&lt;/li&gt;
&lt;li&gt;Meanwhile, per the &lt;a href="https://www.itu.int/itu-d/reports/statistics/2024/11/10/ff24-internet-use/" rel="noopener noreferrer"&gt;ITU's Facts and Figures 2024&lt;/a&gt;, &lt;strong&gt;2.6 billion people are still offline&lt;/strong&gt;, and in low-income countries only 27% of the population uses the internet at all. The places with the least connectivity and the oldest hardware are very often the same places with the least pharmaceutical support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read those side by side and the gap is hard to unsee. The best interaction data in the world is sitting on web servers, and the people who need it most are the ones who can't reach it.&lt;/p&gt;




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

&lt;p&gt;Same discipline as last time: before writing a line of Rust, I looked hard at what already exists, because "I rebuilt something that already existed but in Rust" is not a contribution, it's a hobby (which is fine! but it's not this).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web checkers&lt;/strong&gt; (&lt;a href="https://reference.medscape.com/drug-interactionchecker" rel="noopener noreferrer"&gt;Medscape&lt;/a&gt;, &lt;a href="https://go.drugbank.com/drug-interaction-checker" rel="noopener noreferrer"&gt;DrugBank&lt;/a&gt;, Drugs.com) are excellent and free. They also require internet for every single query. Offline, they are worth exactly nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.epocrates.com/online/interaction-check" rel="noopener noreferrer"&gt;Epocrates&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://www.wolterskluwer.com/en/solutions/uptodate/about/mobile-apps/lexidrug" rel="noopener noreferrer"&gt;UpToDate Lexidrug&lt;/a&gt;&lt;/strong&gt; genuinely do offline: you download the database to your phone and query locally. These are serious, professional tools and I want to be fair to them. But look at the assumptions they carry: a reasonably modern smartphone, an app store account to install and update, and a paid subscription for the full feature set. In a refugee camp or a rural health post, the machine that's actually available is more often a donated 12-year-old laptop than a current iPhone with a Wolters Kluwer subscription. And neither is open source, so nobody can audit what the tool does with queries, adapt it, or redistribute it freely to a hundred clinics on USB sticks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open source?&lt;/strong&gt; I searched. I found hospital-specific projects, academic prototypes, and APIs that wrap the same public datasets, all of which assume a server or a connection somewhere. I could not find a single tool that is simultaneously open source, fully offline, and built to run on genuinely old hardware.&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;MenSung&lt;/th&gt;
&lt;th&gt;Web checkers&lt;/th&gt;
&lt;th&gt;Epocrates / Lexidrug&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Works with zero connectivity&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (after setup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runs on a 15-year-old laptop&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Browser-dependent&lt;/td&gt;
&lt;td&gt;No (smartphone)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open source, auditable&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;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free to redistribute (USB, LAN)&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;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free (online)&lt;/td&gt;
&lt;td&gt;Subscription&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That empty corner of the table is where MenSung lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  So I built MenSung 💊
&lt;/h2&gt;

&lt;p&gt;The name keeps the thread from ZamSync: it comes from Dzongkha, the language of Bhutan. &lt;em&gt;Men&lt;/em&gt; means medicine, &lt;em&gt;sung&lt;/em&gt; means protection. A medical shield.&lt;/p&gt;

&lt;p&gt;Before anything else, the disclaimer that also ships inside the tool itself, because in this domain it's not legal boilerplate, it's the truth: &lt;strong&gt;MenSung is an offline informational aid. It does not replace professional medical judgement, clinical protocols, or qualified healthcare decisions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What it is: one statically linked Rust binary, one database file sitting next to it. No server, no cloud, no GPU, no installation procedure beyond copying two files. You type two or more drug names, it tells you every known interaction between them, most severe first:&lt;/p&gt;

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

&lt;p&gt;Warfarin information:&lt;/p&gt;

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




&lt;h2&gt;
  
  
  The two rules everything else obeys
&lt;/h2&gt;

&lt;p&gt;Before writing much code, I wrote a &lt;a href="https://github.com/Etoile-Bleu/MenSung/blob/main/MEDICAL_DATA_POLICY.md" rel="noopener noreferrer"&gt;medical data policy&lt;/a&gt;. Two rules came out of it, and honestly they have vetoed more of my ideas than any technical constraint has.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero false negatives.&lt;/strong&gt; If an interaction exists in the data, it is shown. Recall beats precision, every time. An extra warning costs the worker a moment of attention. A missed interaction can cost a patient's life. So there is no clever noise filtering, no "hide minor results", no pagination that quietly drops entries. Code that would remove a true interaction from the output does not get merged, period.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No silent correction.&lt;/strong&gt; Drug names are hard to spell, and MenSung only accepts International Nonproprietary Names, no brand names, because brands change across borders and INN doesn't. When a typed name doesn't match exactly, the tool refuses to guess:&lt;/p&gt;

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

&lt;p&gt;Ranked candidates, similarity scores, and it &lt;em&gt;waits&lt;/em&gt;. There is no code path anywhere in the project where the software substitutes its own guess for what the human typed. In most software, autocorrect is a convenience. In this one, it would be a way to hurt someone.&lt;/p&gt;




&lt;h2&gt;
  
  
  The .men file
&lt;/h2&gt;

&lt;p&gt;The database is a custom binary format. SQLite would have been reasonable, but I wanted three things it doesn't give me for free: zero-copy reads, a layout I fully control, and a file that verifies itself.&lt;/p&gt;

&lt;p&gt;The first version of the format weighed about 28 MB. Then I noticed how absurdly repetitive medical interaction text is: the same risk descriptions reused thousands of times across drug pairs. Version 2 deduplicates every string into a shared string table. Same data, &lt;strong&gt;9.3 MB&lt;/strong&gt;. The reader maps the file and reads records in place, no deserialization, no intermediate allocations, which is how lookups stay under the 5ms budget on Core 2 Duo class hardware.&lt;/p&gt;

&lt;p&gt;The header also embeds a SHA-256 checksum of the payload, verified on &lt;strong&gt;every single open&lt;/strong&gt;. That sounds paranoid until you picture how this file actually travels: on a USB stick, laptop to laptop, through machines nobody maintains. A corrupted medical database that loads anyway is precisely the failure this project exists to prevent. Checksum mismatch, file refuses to open, end of story.&lt;/p&gt;

&lt;p&gt;The architecture is the same hexagonal discipline as ZamSync (a workspace of five crates, dependencies pointing one way, the domain crate knowing nothing about files or networks or terminals), just enforced more strictly this time, because on ZamSync I let a couple of boundaries blur early on and paid for it for weeks.&lt;/p&gt;

&lt;p&gt;Performance budgets, treated as bugs when violated: startup under 100ms, lookup under 5ms, memory under 50 MB, binary under 10 MB.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the data comes from, and the licensing rabbit hole
&lt;/h2&gt;

&lt;p&gt;Interactions come from &lt;a href="http://ddinter.scbdd.com/" rel="noopener noreferrer"&gt;DDInter&lt;/a&gt;, a curated academic drug-drug interaction database. On top of it, a build pipeline enriches every drug from four more sources: &lt;a href="https://www.nlm.nih.gov/research/umls/rxnorm/index.html" rel="noopener noreferrer"&gt;RxNorm&lt;/a&gt; for standard identifiers, WHO ATC classification, &lt;a href="https://pubchem.ncbi.nlm.nih.gov/" rel="noopener noreferrer"&gt;PubChem&lt;/a&gt; for chemical properties, and &lt;a href="https://open.fda.gov/" rel="noopener noreferrer"&gt;openFDA&lt;/a&gt; for the drug's own label facts: boxed warnings, contraindications, pregnancy warnings.&lt;/p&gt;

&lt;p&gt;Here is something I learned that I wish someone had told me earlier: &lt;strong&gt;check every data license yourself, directly, before you ship anything.&lt;/strong&gt; I spent an entire evening reading terms-of-service pages instead of assuming, and it genuinely changed the project. DDInter is CC BY-NC-SA 4.0, which means the installed &lt;em&gt;database&lt;/em&gt; carries a non-commercial license completely separate from the &lt;em&gt;code's&lt;/em&gt; MIT/Apache-2.0 license, and the docs have to say so in plain words. WHO ATC data carries its own restrictions. PubChem is not the blanket public domain everyone on the internet assumes it is (their own site policy says so). openFDA, mercifully, is CC0. All of it is written down in the data policy with sources, because "I think it's fine" is not a licensing strategy for a medical tool.&lt;/p&gt;

&lt;p&gt;The enriched database weighs about 27 MB and lives on a GitHub release. First run, MenSung asks permission, downloads it over verified HTTPS with a little progress bar, and that is the last time it ever touches the network. If the download fails mid-way (more on that below...), it falls back to fetching DDInter's public CSV export and compiling a smaller database locally, so even a half-broken connection still ends in a working tool. And you can always skip the network entirely by copying a pre-built &lt;code&gt;.men&lt;/code&gt; file next to the binary by hand.&lt;/p&gt;




&lt;h2&gt;
  
  
  Small things I learned the hard way
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Keyboard shortcuts are a real question.&lt;/strong&gt; The drug-info screen was on F1. Turns out F1 is intercepted by half the terminals and operating systems on Earth for their own help screens, so for many users the key just... did nothing, silently or open Edge's help. Fine, use the letter &lt;code&gt;i&lt;/code&gt; then. Except users are &lt;em&gt;typing drug names&lt;/em&gt; into a text field, and "Ibuprofen" contains an i. The answer was Alt+I, and the lesson was that even a one-key decision deserves five minutes of thinking about whose hands are actually on the keyboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unit tests don't look at the screen.&lt;/strong&gt; All my TUI tests were green while the real interface was happily truncating long warning texts at the edge of the box, invisible unless you actually ran it. I found more real bugs driving the binary in a deliberately tiny tmux pane than in any test file. Tests matter, but nothing replaces looking at the thing with your own eyes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write the policy before the code.&lt;/strong&gt; The medical data policy has vetoed shortcuts I absolutely would have taken at 1am. Past me, writing rules in a calm state, keeps protecting the project from tired me. I can't recommend this enough for any project where correctness has stakes.&lt;/p&gt;




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

&lt;p&gt;The honest status: MenSung works today. Releases are built for Linux, Windows, and macOS, the database installs itself on first run, and within the limits of its data and with clinical judgement on top, it answers the question it was built to answer.&lt;/p&gt;

&lt;p&gt;What I'm exploring next, carefully: &lt;strong&gt;a second, independent interaction source&lt;/strong&gt;, so important interactions can be cross-confirmed instead of resting on one database alone. The data model already supports multiple sources per claim, that part is built and tested. The hard part turned out to be entirely non-technical.&lt;/p&gt;

&lt;p&gt;The dataset I keep coming back to is &lt;a href="https://nsides.io/" rel="noopener noreferrer"&gt;TwoSIDES&lt;/a&gt;, from the Tatonetti Lab. Nicholas Tatonetti is a researcher who now directs his lab at Cedars-Sinai, and back when he was at Stanford he and Russ Altman published a &lt;a href="https://pubmed.ncbi.nlm.nih.gov/22422992/" rel="noopener noreferrer"&gt;now-classic 2012 paper in Science Translational Medicine&lt;/a&gt; on mining the FDA's adverse event reporting system for drug effects nobody had catalogued. TwoSIDES came out of that work: over 3,300 drugs and 63,000 combinations connected to millions of potential adverse reactions, all derived from real-world reports rather than curated literature. It's a genuinely different lens on interactions than DDInter, which is exactly why cross-confirming against it would mean something.&lt;/p&gt;

&lt;p&gt;The problem is that I could not determine the license. The lab says the resources are free and open for academic use, which is lovely, but "academic use" and "an open source tool redistributing a compiled database to clinics" are not obviously the same thing, and I found different statements in different places. And the site itself notes the data is quite a bit out of date. So instead of guessing, which is precisely the mistake I already promised myself I would never make with medical data, I wrote to Nicholas Tatonetti directly to ask him what the license actually permits. I'm waiting to hear back. I'd rather ship one clean, well-licensed source than three legally ambiguous ones, even if it means the feature waits.&lt;/p&gt;

&lt;p&gt;Further out: dosage support, and possibly translations of the clinical texts, since INN names are already international but the warnings are currently English-only.&lt;/p&gt;




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

&lt;p&gt;Same honesty as always: I can write Rust, I cannot market a project, and this one needs different kinds of eyes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're a pharmacist, doctor, nurse, or have done medical work in a low-resource setting:&lt;/strong&gt; you are the person I most want to hear from. Does the severity display make sense? Is the name-confirmation flow safe enough? What would you never trust a tool like this to do? Ten minutes of your criticism is worth more than a hundred stars to me. 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; and I answer everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you write Rust:&lt;/strong&gt; the &lt;a href="https://github.com/Etoile-Bleu/MenSung" rel="noopener noreferrer"&gt;repo&lt;/a&gt; is open, the architecture docs are real, and I merge quickly. The fuzzy matcher and the binary format reader are the areas where a second pair of eyes would matter most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you know an interaction dataset with a clear, redistributable license:&lt;/strong&gt; please, please tell me. This is currently my biggest bottleneck and it's not even a technical one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And if you just want to help in 5 seconds:&lt;/strong&gt; a star on &lt;a href="https://github.com/Etoile-Bleu/MenSung" rel="noopener noreferrer"&gt;github.com/Etoile-Bleu/MenSung&lt;/a&gt; genuinely matters, it's how the next stranger decides the project is worth their five minutes. I learned that from all of you last time.&lt;/p&gt;




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

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.who.int/initiatives/medication-without-harm" rel="noopener noreferrer"&gt;WHO: Medication Without Harm, the third Global Patient Safety Challenge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC9483724/" rel="noopener noreferrer"&gt;Adverse drug reactions and correlations with drug-drug interactions: a retrospective study of reports from 2011 to 2020 (PMC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC12459552/" rel="noopener noreferrer"&gt;Drug-drug interactions: a descriptive analysis of the FDA adverse event reporting system (PMC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC9223639/" rel="noopener noreferrer"&gt;Identifying drug-therapy problems among Syrian refugees in Zaatari refugee camp (PMC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC8450024/" rel="noopener noreferrer"&gt;Potential drug-drug interactions in hospitalized medical patients: data from low resource settings (PMC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC7150111/" rel="noopener noreferrer"&gt;Role of pharmacists in responding to humanitarian crisis (PMC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.itu.int/itu-d/reports/statistics/2024/11/10/ff24-internet-use/" rel="noopener noreferrer"&gt;ITU: Facts and Figures 2024, internet use&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Data sources used by MenSung, with licenses documented in &lt;a href="https://github.com/Etoile-Bleu/MenSung/blob/main/MEDICAL_DATA_POLICY.md" rel="noopener noreferrer"&gt;MEDICAL_DATA_POLICY.md&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://ddinter.scbdd.com/" rel="noopener noreferrer"&gt;DDInter&lt;/a&gt; (CC BY-NC-SA 4.0)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nlm.nih.gov/research/umls/rxnorm/index.html" rel="noopener noreferrer"&gt;RxNorm, US National Library of Medicine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;WHO ATC classification (via RxClass)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pubchem.ncbi.nlm.nih.gov/" rel="noopener noreferrer"&gt;PubChem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://open.fda.gov/" rel="noopener noreferrer"&gt;openFDA&lt;/a&gt; (CC0 1.0)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thank you for reading. And thank you, really, to everyone who starred, forked, fixed, commented, and wrote after the last articles. I said the response changed how I think about what I'm building. Here is the proof: it built the next project. 💙&lt;/p&gt;

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

</description>
      <category>rust</category>
      <category>showdev</category>
      <category>opensource</category>
      <category>discuss</category>
    </item>
    <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.us-east-2.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.us-east-2.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;5&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;
              

              5&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.us-east-2.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.us-east-2.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;5&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;
              

              5&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>
