<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Nomad Code]]></title><description><![CDATA[Solo entrepreneur trying to build my own apps next to my 9-5 job📱
Passionate about traveling ✈️
Vue, Strapi, React, Flutter and way more.]]></description><link>https://thenomadcode.tech</link><generator>RSS for Node</generator><lastBuildDate>Wed, 09 Sep 2026 03:54:41 GMT</lastBuildDate><atom:link href="https://thenomadcode.tech/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Indie Log #4 — I rewrote the entire UI and I don't regret it (yet)]]></title><description><![CDATA[Alright so it's been a minute since the last log. A LOT happened. Flowmodoro 2.0 is in closed beta with my testers, the public release is coming in the next few days, the whole app looks different, ru]]></description><link>https://thenomadcode.tech/indie-log-4-i-rewrote-the-entire-ui-and-i-don-t-regret-it-yet</link><guid isPermaLink="true">https://thenomadcode.tech/indie-log-4-i-rewrote-the-entire-ui-and-i-don-t-regret-it-yet</guid><dc:creator><![CDATA[Christophe Dupont]]></dc:creator><pubDate>Sun, 29 Mar 2026 09:18:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/63cc19b30a811f9ef11ca1d4/8ae620fd-6b5d-4bd4-852c-929c60cc3bda.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Alright so it's been a minute since the last log. A LOT happened. Flowmodoro 2.0 is in closed beta with my testers, the public release is coming in the next few days, the whole app looks different, runs on tablets, doesn't crash on random Android devices anymore (we hope), and I started working on web support. Let's get into it.</p>
<h2>Web support (work in progress)</h2>
<p>OK let me rant for a second. Shipping a mobile app is pain. Pure, uncut pain. You've got Apple's review process that rejects you because your screenshot has the wrong corner radius. You've got Google Play asking you to fill out 47 forms about data safety. Provisioning profiles, signing certificates, App Store Connect, Google Play Console, TestFlight, internal tracks, release tracks — bro I just want people to use my app, not file taxes in two different countries.</p>
<p>And right now I'm living this nightmare in real time. The app is done. 2.0 is built, tested, running in closed beta. My testers are using it. But can I ship it? No. Because I'm drowning in App Store Connect legal requirements, payment setup, screenshot specs for 47 different device sizes, marketing copy, Google Play data safety declarations... I've spent more time on store compliance than I did on some actual features. The code has been ready for days. The paperwork is what's holding me back.</p>
<p>So yeah, I'm bringing Flowmodoro to the web. Partly because it makes sense for the product. But mostly because I am SO done with the mobile shipping experience. Honestly? My next product will be web-first. No app stores, no review queues, no certificates. Just deploy and it's live. What a concept.</p>
<p>Anyway. Step one was a commit called "Remove dart:io and platform guards for web compilation." Sounds innocent right? Turns out half the codebase casually imports <code>dart:io</code> like it's free. File paths, platform checks, notification APIs — everything just assumes you're on a phone. Spoiler: the browser is not a phone.</p>
<p>I built this thing called <code>ResponsiveShell</code> that figures out if you're on a phone, tablet, or desktop, and gives you the right layout. Bottom tab bar on phones, floating sidebar on tablets, full sidebar with a mini timer on desktop. The mini timer sits in the sidebar so you can see your session running without switching tabs. That one was actually fun to build.</p>
<p>Notifications were... less fun. Web doesn't have local notifications the way mobile does, so I had to split the notification service into platform-specific implementations. Mobile keeps its existing system, web gets nothing for now. But the abstraction is clean enough that plugging in the Web Notifications API later should be straightforward.</p>
<p>The app compiles and runs on web but there's still a bunch of web-specific stuff to handle before I ship it — hosting, data management, all that good stuff. Soon though.</p>
<h2>The glass UI makeover (AKA "I'll just tweak some colors real quick")</h2>
<p>You know how it goes. "Let me just adjust this one color." Three days later you've rewritten the entire visual layer of the app.</p>
<p>I did it in phases because touching every screen at once is how you end up mass deleting your <code>lib/</code> folder at 3am:</p>
<p><strong>Phase 1</strong> — Centralized theme. <code>FlowColors</code>, <code>FlowRadius</code>, <code>FlowSpacing</code>. No more random hex codes sprinkled across 40 files like confetti. Brand purple <code>#5F1DF9</code> and a deep dark base <code>#0D0B1A</code>. That's the palette. Done. Move on.</p>
<p><strong>Phase 2</strong> — Navigation. Killed the default Material bottom bar (sorry Google) and replaced it with a floating glass tab bar. <code>BackdropFilter</code> for the frosted blur, wrapped in a <code>GlassContainer</code> widget I made reusable so I wouldn't have to copy-paste blur logic into 15 files. On tablets it becomes a glass sidebar. Same component, different layout. This is the one time abstraction actually paid off immediately.</p>
<p><strong>Phase 3</strong> — Timer screen. The old circular progress indicator looked like a loading spinner from 2019. Replaced it with a thin glow ring and a glass start/stop button. Added a mesh gradient background that subtly animates — gives the glass something to actually blur against. The gradient intensity ramps up when you're in a session. Nobody will consciously notice this. I spent four hours on it anyway.</p>
<p><strong>Phases 4-9</strong> — Went full glassmorphism on literally every other screen. Stats, history, achievements, settings, onboarding, auth, paywall, tags. Purple-tinted glass in light mode, white-frosted glass in dark mode. By phase 7 I was mass replacing containers in my sleep.</p>
<p><strong>Phase 10</strong> — The "stare at every pixel" pass. Button glows looking weird in light mode? Gone. Content scrolling under the nav bar? Fixed. Achievement grid not responsive? Fixed. Equal-height stat cards? Fixed. The kind of stuff nobody notices unless it's broken, and then it's ALL they notice.</p>
<p>Then iPad happened. Portrait mode was trying to render the desktop sidebar in a narrow screen. The mini timer didn't work in a horizontal bottom bar. The mesh gradient was glitching on certain iPad models. I spent days fixing all of this. iPads, man.</p>
<h2>Sentry and PostHog — or, "I should have done this 2 years ago"</h2>
<p>I was flying completely blind. No crash reports. No analytics. Just vibes and "works on my machine."</p>
<p>Added Sentry for crash tracking. Within the FIRST DAY it flagged three crashes I had no idea about. One was a crash at launch on certain Android devices. A crash. At launch. And I had zero clue. Fixed all three in a few hours. Two years without error tracking. What was I thinking.</p>
<p>PostHog for analytics. Now I can actually see which features people use, where they drop off, how long sessions last. The setup was fine but Apple's privacy plist requirements... listen, if you enjoy filling out forms about why your app needs to know what time it is, iOS development is for you.</p>
<h2>Tablet support</h2>
<p>Flowmodoro runs on iPads and Android tablets now. This sounds like a one-liner checkbox feature. It was not.</p>
<p>The responsive shell handles the layout switching, but there were edge cases everywhere: stats period selector hiding behind the app bar, settings spacing breaking at certain widths, timer not centering in landscape. Death by a thousand papercuts.</p>
<p>The iPad portrait fix was the worst. I had to detect portrait mode and fall back to the phone layout instead of cramming the desktop sidebar into a portrait screen. Because apparently a 768px wide iPad in portrait mode is NOT the same as a 768px wide laptop screen. Who knew. (Everyone knew. I didn't.)</p>
<h2>The small stuff</h2>
<p><strong>In-app feedback</strong> — Added a FeatureBase WebView in settings. Users can submit bugs and feature requests without leaving the app. Took like an hour to implement. Probably the best ROI feature I've shipped.</p>
<p><strong>Font bundling</strong> — Was loading Google Fonts at runtime. Works great until your user has bad wifi and the app looks like a ransom note in system fallback fonts. Fonts now ship with the binary. Heavier app, no more ugly surprises.</p>
<p><strong>Auth fix</strong> — Flowmodoro lets you use the app without signing up (anonymous-first). But when anonymous users finally created an account, the sign-up flow was making a NEW user instead of linking to the existing one. Translation: all your data? Gone. Poof. Fixed that real quick and wrote an integration test against real Supabase so it never happens again.</p>
<p><strong>Tag validation</strong> — You could create duplicate tag names. That broke stats. Now you can't. Also added a delete confirmation because deleting tags should require at least one extra click of intentionality.</p>
<h2>Version 2.0</h2>
<p>After all of this — full UI rewrite, crash tracking, analytics, tablet support, web groundwork, and roughly 80 bug fixes — I bumped to 2.0.0.</p>
<p>Then immediately pushed 2.0.1, 2.0.2, 2.0.3, and 2.0.4 to my beta testers because nothing says "stable release" like four hotfixes in 24 hours.</p>
<p>574 commits total. The app is in closed beta right now, and the public release is dropping in the next few days. Just need to finish wrestling with Apple and Google's store requirements. You know, the fun part where you spend three days uploading screenshots and filling out legal forms instead of writing code.</p>
<p>The mobile app is polished, stable, and ready. Web is on its way. Now the hard part: getting people to actually use it. Building the thing was the easy part apparently.</p>
<hr />
<p><em>Follow the chaos <a href="https://x.com/the_nomad_code">@the_nomad_code</a> on X. Building in public, one commit (and four hotfixes) at a time.</em></p>
]]></content:encoded></item><item><title><![CDATA[Indie Log #3]]></title><description><![CDATA[Hey everyone!I am ashamed that I didn't give news in... checks calendar ...over a year! Yeah, I know. The last update was in July 2024, and here we are in November 2025. Time really does fly when you're juggling a full-time job, a moving 3000km away,...]]></description><link>https://thenomadcode.tech/indie-log-3</link><guid isPermaLink="true">https://thenomadcode.tech/indie-log-3</guid><category><![CDATA[indiedev]]></category><category><![CDATA[Developer]]></category><category><![CDATA[dev-diaries]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[focus]]></category><category><![CDATA[pomodoro]]></category><dc:creator><![CDATA[Christophe Dupont]]></dc:creator><pubDate>Sun, 02 Nov 2025 14:34:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/SuwFvHU-z-o/upload/3b20c4f64ac51b7799924f7471dc5748.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey everyone!<br />I am ashamed that I didn't give news in... <em>checks calendar</em> ...over a year! Yeah, I know. The last update was in July 2024, and here we are in November 2025. Time really does fly when you're juggling a full-time job, a moving 3000km away, and a now almost-2-year-old daughter who loves ducks, jumping and does not take “no” for a final answer. Trying to build a productivity app in the middle of that sounds quite ironic.</p>
<p>But here's the thing: I've been heads down, <strong>actually finishing the app</strong>.</p>
<h2 id="heading-life-updates-the-good-kind-of-chaos">Life Updates (The Good Kind of Chaos)</h2>
<p>Last time I wrote, my daughter was 5 months old and I was figuring out the new rhythm of office work plus side projects. Well, she's now a tiny human with opinions, a surprising vocabulary, and an uncanny ability to wake up exactly when I hit a flow state at 10 PM. Classic.  </p>
<p>Oh, and I also moved from Barcelona to Copenhagen. Because apparently juggling a full-time job, a toddler, and a side project wasn't challenging enough. Let's throw an international relocation into the mix. The good news? Danish work-life balance is real, and the extra daylight (in summer, at least) actually helped me squeeze in more coding sessions.</p>
<p>But somehow, in between the chaos of toddlerhood, I managed to build more features in this past year than I did in the first 6 months of the project combined. Turns out, having extremely limited time forces you to actually ship instead of endlessly tweaking neumorphic shadows (more on that later).</p>
<h2 id="heading-what-got-built-the-full-list">What Got Built: The Full List</h2>
<p>Let me just get this out of the way: <strong>Flowmodoro is feature-complete</strong>. I'm not even joking. The app I envisioned back in February 2024 is now real, polished, and ready to ship. Here's everything that landed since July 2024:</p>
<h3 id="heading-the-big-stuff">The Big Stuff</h3>
<p><strong>Cloud Sync, free for everyone.</strong> Remember when I mentioned scalability concerns with SQLite? Yeah, that's solved. I bit the bullet and integrated Supabase for backend storage. Your focus sessions now sync across devices, there's proper user authentication, and I can finally sleep knowing the database won't explode when someone has 10,000 entries. This was probably the scariest migration I've tackled solo, but it works beautifully.</p>
<p><strong>Tags System</strong> You can now categorize your focus sessions! Working on a "Deep Work" session? Tag it. Client project? Tag it. "Avoiding emails while looking productive"? Tag it. The tag system includes:</p>
<ul>
<li><p>Full tag management (create, edit, delete, color-coding)</p>
</li>
<li><p>Tag analytics showing which activities get the most focus time</p>
</li>
<li><p>Tag filtering in history and statistics</p>
</li>
<li><p>Usage counts to see your most-used categories</p>
</li>
</ul>
<p>This feature alone changed how I use the app. Turns out, seeing that I spent 12 hours on "React debugging" vs 2 hours on "Actual feature work" is... humbling.</p>
<p><strong>Achievement System</strong> I went full game designer on this one. Flowmodoro now has an achievement system with Bronze, Silver, Gold, and Special tier achievements. There are achievements for:</p>
<ul>
<li><p>Streak milestones (3 days, 7 days, 30 days, etc.)</p>
</li>
<li><p>Total focus time accumulated</p>
</li>
<li><p>Session counts</p>
</li>
<li><p>Specific behaviors like "Early Bird" (sessions before 8 AM) and "Night Owl" (after 10 PM)</p>
</li>
<li><p>"Perfect Week" challenges</p>
</li>
<li><p>Break-comeback tracking</p>
</li>
</ul>
<p>Each achievement has a celebration animation, progress tracking, and yes, I spent way too much time making the confetti effect look good. No regrets.</p>
<p><strong>PRO Subscription via RevenueCat</strong> The monetization question is answered! I integrated RevenueCat for in-app purchases and subscriptions. The free tier is genuinely useful (unlimited focus tracking, basic stats), but PRO unlocks:</p>
<ul>
<li><p>Cloud sync and backup</p>
</li>
<li><p>Advanced analytics and insights</p>
</li>
<li><p>Data export and import.</p>
</li>
<li><p>Achievement system</p>
</li>
<li><p>Tags</p>
</li>
<li><p>Colors customisation</p>
</li>
</ul>
<p>Pricing is intentionally fair—I'm not holding your data hostage, and there's no "freemium hell" where the app is useless without paying. I'm aiming for $1.59/month or $9.99/year, which feels honest for the value. If you do not want subscription, you can have a lifetime pro for $29.99.</p>
<h3 id="heading-the-polish">The Polish</h3>
<p><strong>Material 3 Redesign</strong> I finally admitted defeat on the neumorphic design. It looked cool in Figma but felt weird in practice. The app now uses Material 3 with dynamic theming, cleaner layouts, and way better accessibility. The tab bar is gone, settings are tucked in the top-right, and the whole interface just feels... right.</p>
<p><strong>Dark Mode (Properly Implemented)</strong> Not the hacky toggle from before—this is full Material 3 dynamic theming with dark mode support. It respects system preferences, looks gorgeous, and doesn't burn your retinas at 2 AM.</p>
<p><strong>Notifications</strong> The app now sends gentle notifications when you've been in focus mode for a while or when it's time for a break. No aggressive productivity-shaming—just friendly reminders that honor the Flowmodoro philosophy of working with your natural rhythm. Of course you can remove them if you do not want them.</p>
<p><strong>Entry Editing</strong> You can finally edit past focus sessions! Accidentally left the timer running overnight? Fix it. Want to adjust session times or tags? Done. This was a surprisingly complex feature involving database updates, UI state management, and making sure the statistics recalculate properly.</p>
<p><strong>Onboarding Flow</strong> New users now get a clean onboarding experience explaining how Flowmodoro works, what makes it different from rigid Pomodoro timers, and how to get started. No more throwing people into the deep end.</p>
<p><strong>Statistics Overhaul</strong> The stats page got completely rebuilt with:</p>
<ul>
<li><p>Beautiful graphs (thanks fl_chart!)</p>
</li>
<li><p>Weekly, monthly, and yearly views</p>
</li>
<li><p>Best focus time insights (turns out I'm most productive 9-11 AM and 8-10 PM)</p>
</li>
<li><p>Streak tracking</p>
</li>
<li><p>Tag-based breakdowns</p>
</li>
</ul>
<p><strong>PostHog Analytics</strong> I added privacy-respecting analytics via PostHog so I can understand how people use the app without being creepy about it. This will help me prioritize future features based on actual usage, not my assumptions. The analytics data will be hosted in Europe and is full GDPR compliant. Everything is of course anonymous.</p>
<p><strong>Data Export</strong> You can export all your data as CSV. Your data is yours. Period.</p>
<h2 id="heading-the-technical-journey">The Technical Journey</h2>
<p>This was <strong>229 commits</strong> since July 2024. Not all of them were pretty. I:</p>
<ul>
<li><p>Fought with Xcode and Android build systems more times than I care to admit</p>
</li>
<li><p>Learned RevenueCat's subscription SDK (surprisingly well-documented!)</p>
</li>
<li><p>Set up GitHub Actions for automated releases with Shorebird for hot patching</p>
</li>
<li><p>Wrote actual tests (gasp!) for the achievement system</p>
</li>
<li><p>Migrated from local SQLite to Supabase without losing anyone's data in testing</p>
</li>
</ul>
<p>The biggest lesson? <strong>Shipping beats perfecting</strong>. The neumorphic design I spent weeks on in early 2024? Gone in a day, replaced with something better. The achievement system I thought would take a month? Done in a week once I stopped overthinking it.</p>
<h2 id="heading-whats-next-launch-time">What's Next: Launch Time</h2>
<p>Here's the part where I commit publicly: <strong>I'm rolling out Flowmodoro in the coming weeks</strong>.</p>
<p>The app is on TestFlight and Google Play internal testing right now. I'm doing final bug fixes, polishing the onboarding flow, and preparing the app store listings. Barring any catastrophes (knock on wood), the public launch is happening this month.</p>
<p>I'm planning a staged rollout:</p>
<ol>
<li><p><strong>Week 1</strong>: Friends, family, and beta testers</p>
</li>
<li><p><strong>Week 4</strong>: Soft launch on Product Hunt and indie maker communities</p>
</li>
<li><p><strong>Week 8</strong>: Full public release on iOS and Android app stores</p>
</li>
</ol>
<p>If you want early access, shoot me a message. I'm still taking beta testers and genuinely want feedback before the big launch.</p>
<h2 id="heading-the-emotional-rollercoaster">The Emotional Rollercoaster</h2>
<p>I won't lie—there were moments where I questioned if anyone would actually use this. The productivity app space is crowded. There are Pomodoro timers everywhere. Why would anyone care about Flowmodoro?</p>
<p>But then I used it myself for this past year. Really used it. And I noticed something: <strong>I stopped fighting the timer</strong>. With traditional Pomodoro apps, I'd constantly pause, reset, or ignore the timer when I was deep in flow. With Flowmodoro, I just... work. The app adapts to me, not the other way around.</p>
<p>That's when I knew this was worth shipping.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>So yeah, after nearly 2 years of development (from newborn to toddler, full-time job, and way too much YouTube), Flowmodoro is ready.</p>
<p>Check out Flowmodoro at <a target="_blank" href="http://flowmodoro.thenomadcode.tech">flowmodoro.thenomadcode.tech</a>. Follow the journey on <a target="_blank" href="https://x.com/the_nomad_code">Twitter/X</a> if you're into that. And if you've been following along since Indie Log #1 back in February 2024, thank you. Your support kept me going through the rough patches.</p>
<p>Now if you'll excuse me, I have an app to launch and a toddler who just discovered that climbing furniture is her new favorite activity.</p>
<p>Until the next update (hopefully not in another year)!</p>
<hr />
<p><strong>P.S.</strong> — If you're building a side project while juggling life, my advice: embrace the constraints. Limited time forced me to cut scope ruthlessly and ship something real instead of something "perfect someday." Your future self will thank you.</p>
]]></content:encoded></item><item><title><![CDATA[Indie Log #2]]></title><description><![CDATA[Ajustment to the new reality
Wow 5 month already ! I am ashamed that I didn't gave news in 5 month !
Well the honest truth is that I spend a lot of time with my baby daughter and that I have no regrets about this, but I should have keep you more in t...]]></description><link>https://thenomadcode.tech/indie-log-2</link><guid isPermaLink="true">https://thenomadcode.tech/indie-log-2</guid><category><![CDATA[Flutter]]></category><category><![CDATA[app development]]></category><category><![CDATA[indiedev]]></category><category><![CDATA[focus]]></category><category><![CDATA[flow]]></category><dc:creator><![CDATA[Christophe Dupont]]></dc:creator><pubDate>Sat, 06 Jul 2024 15:26:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Y_LgXwQEx2c/upload/011d4c0a6bbed03a9f1a90ab6324e742.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-ajustment-to-the-new-reality">Ajustment to the new reality</h1>
<p>Wow 5 month already ! I am ashamed that I didn't gave news in 5 month !</p>
<p>Well the honest truth is that I spend a lot of time with my baby daughter and that I have no regrets about this, but I should have keep you more in touch ! I also went <strong>back to the office</strong>, and therefore I don't have a lot of time during my days. I am working all day long, and the few hours that I have left during my day, I spend them with my daughter, cook, go to groceries, and try my best to sleep.</p>
<p>My daughter is now 5 month old and start to be more regular to sleep. I am getting back to a normal level of tiredness. I feel a new energy and motivation. When I build an app I don't build it only for me. I build it also for her. I already have a future idea of for a different web app that I would like to commercialize. But I want to finish properly <a target="_blank" href="https://flowmodoro.thenomadcode.tech">Flowmodoro</a> first. To keep me away from the YouTube productivity black hole, I decided to make myself a temporary desk in the leaving room, <strong>without any speakers</strong>. This make YouTube way less attractive.</p>
<h1 id="heading-design-changes">Design changes</h1>
<p>As I spend more and more time with my app, specifically working on the Statistics screen, I realize something : I don't like the design of it! Maybe I saw it too much already, but things does not make sense to me anymore :</p>
<ul>
<li><p>Neumorphic design is nice, but not for everything. For example I do not like the way the settings looks. This is why I reviewed the setting screen like this.</p>
</li>
<li><p>The tab bar does not really make sense and should be removed to have more space. Settings and statistics have been moved on the top right corner of the app. The history is now accessible directly with a button on the main screen. The goal of the history screen will be to quickly be able to edit a past entry. For now editing an entry is still to be done.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720277740571/66f6c298-c431-43b9-aff8-60638c102fd1.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-colors-and-times">Colors and times</h1>
<p>The colors I used for the stopwatch and a bit everywhere in the app are not really relevant. I changed them to this scheme :</p>
<ul>
<li><p>Purple when we didn't reach the minimum focus time (the stopwatch is filling up)</p>
</li>
<li><p>Green when we reach the minimum focus time to pretend to a break. We are now in a flow state.</p>
</li>
<li><p>Red when we reach the maximum focus time, we need a break because our focus is not a quality one anymore.</p>
</li>
</ul>
<p>Yes, I decided to add a maximum focus. By this I mean that all of us, if we are focus during 5 hours in a row about something, we should take a break at a point of time, because our brain is melting. I think that if a user don't like this feaure he can put a maxumun focus time of 100 hours and will never get bothered by it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720277543542/3db7b1c8-60bb-4e39-b9e2-01f5a16355c1.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-statistics-screen">Statistics screen</h1>
<p>I made some decent progress with the statistics screen, specifically learning how to use the excellent fl_chart library. I first implemented a basic chart and then added some selectors to select the zoom level for the data : week, month, or year.</p>
<p>I could make a bar chart pretty easily. Once we get how works the library thanks to all the examples, it is pretty simple. Most of the complication is more about how to extract the data from the SQLite DB in an efficient way and make calculations on it.</p>
<p>I also had to implement a way to generate fake data in the database, this will be usefull later in case of UI testing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720277392615/95200c21-ccbf-4ef7-8d98-615afaeee9d4.png" alt class="image--center mx-auto" /></p>
<p>I realize that I should probably refactor soon to use Isar <a target="_blank" href="https://isar.dev/">https://isar.dev/</a> instead of the default SQLite, for better performance. Because the app could struggle a bit when loading the entries for an entire year. But this will be for later !</p>
<h1 id="heading-what-is-next">What is next</h1>
<p>One missing feature would be the entries edition, to give the ability to edit a past entry. Then I think Dark mode is important to be here since the very first version of the App. My phone is set to swtich to light to dark mode at night, and I would appreciate my app do be able to do so as well. Then a bug report system is a must to be able to have feedback from user crashed and be responsive in correcting them. I also need to think about a pricing model. If I want to continue developing this app, I will need some financial motivation. I will not want to make the user a hostage by keeping his data behind a pay model. This is why I have to think about something fair. If you keep your focus thanks to me, that could worse a bit of compensation don't you think ?</p>
<p>All what is left on my list are more "nice to have" tasks. But for example, shorebird would be amazing to push corrections quickly. Isar could be also nice because it will be difficult to change the database system afterwards. As always I share with you my list for the sake of transparency.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720278214585/d2a44b86-f8eb-49fe-91bd-a3232958189f.png" alt class="image--center mx-auto" /></p>
<p>See you on the next update !  </p>
<p><em>If you like what I do, and would be part of the private Beta, register yourself here :</em><a target="_blank" href="https://flowmodoro.thenomadcode.tech/">https://flowmodoro.thenomadcode.tech/</a></p>
]]></content:encoded></item><item><title><![CDATA[Indie Log #1]]></title><description><![CDATA[New member in the family, New beginnings
As you may know is quite difficult to work with a new-born at home. The first days it was impossible for me to do anything, all the time is focused on the new little member of the family, and I was feeling exh...]]></description><link>https://thenomadcode.tech/indie-log-1</link><guid isPermaLink="true">https://thenomadcode.tech/indie-log-1</guid><category><![CDATA[logging]]></category><category><![CDATA[journal]]></category><category><![CDATA[Build In Public]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[beta]]></category><dc:creator><![CDATA[Christophe Dupont]]></dc:creator><pubDate>Sun, 04 Feb 2024 20:03:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/0qvBNep1Y04/upload/cc3ec16d32e5fc2128e902d5894e224a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-new-member-in-the-family-new-beginnings">New member in the family, New beginnings</h1>
<p>As you may know is quite difficult to work with a new-born at home. The first days it was impossible for me to do anything, all the time is focused on the new little member of the family, and I was feeling exhausted.</p>
<p>After a few days, I started to find some moments of freedom during the day. Still to much tired to do anything constructive, I used it to think more about the strategy.</p>
<p>I realized how it is very important for me to communicate more on my developments. Not because I am famous and it interest someone, but because it will force me to keep being motivated on this project. Thus the creation of this log. I don't give you the guaranty I will make one every week, it will mainly depend of if I have interesting things to say or not !</p>
<p>As you may know I am freelance, and have a contract with a company, that ask me a regular 9-5 dedication. After my working days, I realized that it can be very difficult to keep the focus on my side projects. I can finish work late, I have other things to do, cooking, groceries, appointments etc... I realized that starting 18:00 I have a lot of trouble focusing, and a coding sessions often ends up in YouTube browsing about topics that have nothing to do with my work :D</p>
<p>I tried many times to adopt an <em>Wake up early</em> routine, but it has always been difficult. I am living in Spain, I culturally tend to go to bed late, and therefore wake up late.</p>
<p>But I have to use this amazing new arrival in my life : My Daughter ! As she wakes us up every 3 hours, it is the perfect little clock I needed. Plus, on the side of my contract, I am in paternity leave for the next 4 weeks, so I will try to make the most of it !</p>
<h1 id="heading-why-building-flowmodoro">Why building Flowmodoro ?</h1>
<p>I am a senior developer with a big tendency to loose focus. I tend to take my time to code and easily fall into the YouTube trap. When the deadline is getting near, my stress levels are visible from space.</p>
<p>So I tried a lot of different techniques. Websites blockers and time blocking didn't work for me. Pomodoro was quite motivating but I found it a problem with Pomodoro : if you are focused, it will break this focus every 25 minutes.</p>
<p>When developing, 25 minutes is not a big focus time to me. Stopping after 25 minutes is pulling me out of my "flow" state. If I was not interrupted by this imposed break, I could have keep my focus for a longer period of time.</p>
<p>This is where Flowmodoro comes :</p>
<p>The Flowmodoro method, also known as the Flowtime technique, is a compelling variation of the Pomodoro technique. Unlike Pomodoro, which relies on alarms and predefined time intervals, Flowtime encourages working without alarms or strict time blocks. Instead of adhering to set timeframes, the focus is on working for as long as one can maintain concentration, fostering a "flow state" where individuals are fully engrossed in a task.</p>
<p>Unlike Pomodoro, where alarms may disrupt the flow state, Flowtime allows individuals to work continuously until they feel the need for a break. This technique is particularly suitable for those who find constant alarms limiting or interruptive. The key is to select a primary task to achieve a flow state and work on it until a break is warranted. Unlike Pomodoro, there is no strict time constraint; the emphasis is on preserving the flow state.</p>
<p>In Flowtime, breaks are taken when individuals feel fatigued or distracted, and the duration of these breaks is flexible. Although the tasks are still segmented, and breaks are rewarded, the absence of a timer makes Flowtime an excellent alternative for those who require more time to immerse themselves in their work and stay focused.</p>
<p>The break time after a focus session should also depend on the focus time. The longer the focus, the longer the break.</p>
<p>Interested ? Register in the mailing list to be part of the private Beta : <a target="_blank" href="https://flowmodoro.thenomadcode.tech/">https://flowmodoro.thenomadcode.tech/</a></p>
<h1 id="heading-the-actual-state-of-the-app">The actual state of the app</h1>
<p>A few months ago I started developing this app. The goal was to be able to track my focus sessions, calculate my breaks time. Give me some alarm when I have to go back to work. And more importantly, try to understand when my body feels the best for a focus session.</p>
<p>The app in it's actual development state include a working "Stopwatch" screen, a "Settings" screen, and a basic "History" screen to see the last entries.</p>
<p>Here is how it currently looks.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707021002399/40703004-8f54-40e8-bb07-5e127f984576.png" alt class="image--center mx-auto" /></p>
<p>Even if it is very basic, all this is functional so far ! You can quit the app and come back, the stopwatch will sill be running so even if your phone run out of battery, this should not be an issue. The only issue I have so far is that is that I load <strong>all</strong> the entries on the History page. This could result, with time, in slower loading times. I guess this will be a tiny refactor to do in the future :)</p>
<h1 id="heading-work-for-the-upcoming-weeks">Work for the upcoming weeks</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707051156782/94b8aa5b-8381-484b-939c-028eb01cc465.png" alt class="image--center mx-auto" /></p>
<p>Next week I will start working on the "Stats" page. To display some graph about your flow history. This is actually where the most important part of the app will be. My idea is that we should be able to identify which days, and which hours are the most productive. I already made a draft of the general idea for this page :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707051165696/dd2aed64-1395-4e1b-8974-be0b4873e739.png" alt class="image--center mx-auto" /></p>
<p>When this is done I will try to handling interruption count. The idea behind interruption counts is to be able, with one tap of a button, to say "I lost focus for a few seconds because my colleague came to tell me how was his weekend, but I am still in a flow and I don't want to take a break yet". The goal is to be able to have statistics about these interruptions, to put them in correlation with the flow state</p>
<p>Then I will try to add some bug reporting system, to have quick alerts if something bad happens, and also suggestions. I would also like to have a look on Shorebird to be able to push dart code update hotfixes quickly.</p>
<p>Then I will adapt the existing dark mode to make it really usable. For now it is automatic according to the device settings, and you need to restart the app if the settings change. Not ideal. I plan to improve on this as I know some of you are more in the dark side of the force.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707051177755/52195350-e402-46d6-89c6-1dd2a7edc8a8.gif" alt class="image--center mx-auto" /></p>
<p>This is all for this log ! See you next time !</p>
<p><em>If you like what I do, and would be part of the private Beta, register yourself here :</em> <a target="_blank" href="https://flowmodoro.thenomadcode.tech/">https://flowmodoro.thenomadcode.tech/</a></p>
]]></content:encoded></item><item><title><![CDATA[(MergerFS + SnapRAID) is the new RAID 5]]></title><description><![CDATA[Feeling more like watching youtube?  Check out my tutorial in video on my youtube channel.
I am travelling a lot, and as you must know, travelling with external hard drives, is heavy, and mostly unsafe. If anything happens to your hard drive, you wil...]]></description><link>https://thenomadcode.tech/mergerfs-snapraid-is-the-new-raid-5</link><guid isPermaLink="true">https://thenomadcode.tech/mergerfs-snapraid-is-the-new-raid-5</guid><category><![CDATA[NASStorage]]></category><category><![CDATA[raid]]></category><category><![CDATA[snapraid]]></category><category><![CDATA[mergerfs]]></category><category><![CDATA[homeserver]]></category><dc:creator><![CDATA[Christophe Dupont]]></dc:creator><pubDate>Fri, 03 Feb 2023 19:51:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/X_j3b4rqnlk/upload/ccaa9a3f9882808f23e840f847104fb9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em><mark>Feeling more like watching youtube? </mark></em> <a target="_blank" href="https://www.youtube.com/watch?v=tX5MA-c6Qq4&amp;ab_channel=TheNomadCode"><em><mark>Check out my tutorial in video on my youtube channel.</mark></em></a></p>
<p>I am travelling a lot, and as you must know, travelling with external hard drives, is heavy, and mostly unsafe. If anything happens to your hard drive, you will lose all your precious travel memories. Google One and its Google Photo app are very good, but you cannot store your videos in full definition, and you have to pay a monthly fee. Not ideal when you want to store a massive quantity of 4K videos.</p>
<p>Because of this, a few years ago I decided to set up my own Home Server, aka NAS. I used old components from my previous PC, and others were found second-hand. I prefer to save my money to travel. 🤑</p>
<p>I use it mainly for storing my photos and video and my files. In this NAS I have one M2 SSD drive for the system and temporary files, and 4 disks of 2TB, in RAID5, which means that I had a failure tolerance of one drive and could use 6TB of storage. This was enough size for my usage, and it was accessible from everywhere, 24/7. The dream.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675449750350/40e98004-f939-4f7d-a576-5a83e3ae1137.png" alt="My previous RAID5 configuration" class="image--center mx-auto" /></p>
<p><strong><em><mark>Until that one day when I could not access it. </mark></em></strong> 🤯</p>
<p>I tried to recover the best I could, but between the fact that 2 drives were down, and LVM on top of it, because of a bad choice made a long time ago. That was impossible.</p>
<p>What probably happened is that one day, a disk silently crashed. I was sure I set up email notifications, but I never received anything about it. The system continued working on 3 disks. Until a few days ago when a second disk crashed, and I lost it all.</p>
<h1 id="heading-changing-the-drives">Changing the drives</h1>
<p>Anyway, it was time for me to grieve my lost data, and be grateful for the few was hanging on another computer or with still copy of it on an SD card.</p>
<p>Then I put everything out and remove the 2 faulty drives. I had 2 x 12TB external hard drive laying around from when mining CHIA was a thing. I decided to use them so this repair will not cost me any additional money.</p>
<p>As you realised by now, I have a heterogenous drive configuration :</p>
<ul>
<li><p>2x2TB</p>
</li>
<li><p>2x12TB</p>
</li>
</ul>
<p>We could use it in RAID in a 2xRAID1 configuration, but we will lose half of the drives in capacity.</p>
<h1 id="heading-my-thoughs-about-raid-solutions">My thoughs about Raid solutions</h1>
<p>On Paper, Raid is a great system for a NAS. We are losing one or 2 drives' capacity, depending if we choose RAID5 or RAID6 in exchange for 1 or 2 drive failure tolerance.</p>
<p>Every time you write a file in a RAID5 Array, every block of it is written in a different drive, with its parity information stored in the parity drive.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675449779482/52bf2345-fbc4-4b48-b0e4-bcdacc1b4f87.png" alt class="image--center mx-auto" /></p>
<p>Now if like me you are losing 2 drives, well you lose everything.</p>
<p><strong><em><mark>Well, I assure you that when it is happening to you would love to be able to get back some data from the 2 last disks! Well with RAID you can't. </mark></em></strong> 😑</p>
<p>Then RAID has some annoying limitations when you are on a budget :</p>
<ul>
<li><p>Replacing disks is not trivial, you need to put your array in degraded state and rebuild it.</p>
</li>
<li><p>All disks must be the same size or you will just lose space, so an upgrade can be pretty salty. 💸</p>
</li>
</ul>
<h1 id="heading-the-solution-i-choose">The solution I choose</h1>
<p>And If I was telling you that with two different software, you could have the same level of safety, with more advantages? MergerFS and SnapRAID are not related to each other but are very complementary.</p>
<p><strong>MergerFS</strong></p>
<ul>
<li><p>MergerFS is a simple tool that allows you to group your disks and make them appear as one. When you write a file to this group, by default the file is written on the disk with more available space, on all the branches where the relative path exists. I invite you to have a look at the extensive <a target="_blank" href="https://github.com/trapexit/mergerfs/tree/2.34.1#policy-descriptions">list of policies</a> to find the one that matches the best.</p>
</li>
<li><p>Unlike RAID, the split is not made on the block level, but on the file level. That means that if you lose one disk because it is just temporarily offline, or dead, you still have access to the files that are on the others. When your lost disk is back online, the missing files will magically appear.</p>
</li>
<li><p>There are ways with a bit of scripting, to have an SSD cache of the recently used files (I won't cover this for now).</p>
</li>
<li><p>Downside: that also can bring slower read/write times, because the load is not spread across all the drives.</p>
</li>
</ul>
<p><strong>SnapRAID</strong></p>
<ul>
<li><p>SnapRAID allows you to store parity data from other drives, on 1 or 2 drives to have the same disk failure tolerance as RAID 5 or RAID 6</p>
</li>
<li><p>It is stored in files, so if you change of hard drive, you just have to copy these parity files to the new one.</p>
</li>
<li><p>You can exclude some folders if you don't want to generate parity for them.</p>
</li>
<li><p>Downside: The parity is not generated in real-time, you have to run a command to analyse the drives and create the parity data for the files you just added or modified.</p>
</li>
</ul>
<p>By combining these 2, we will have a storage solution, similar to RAID, but that will allow you to recover some data even with more than 2 drives failure. Yes, the parity is not in real-time, so there is a risk that you can lose the files between the moment you add them, and the moment you launch a sync. But my home server is mainly to store non-critical files, not every second, so it will be enough for my usage.</p>
<h1 id="heading-setting-it-up-on-my-machine">Setting it up on my machine</h1>
<p>I have 4 drives, 2 x 2TB and 2 x 12 TB. It is recommended to use one of your biggest hard drives for SnapRAID parity data, so I will use one 12TB drive. So I will have :</p>
<ul>
<li><p>/mnt/data1</p>
</li>
<li><p>/mnt/data2</p>
</li>
<li><p>/mnt/data3</p>
</li>
<li><p>/mnt/parity1</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675449789504/ca109793-df33-4651-a214-0dc47bdb0d7f.png" alt /></p>
<h2 id="heading-formating-the-drives-and-mounting-them">Formating the drives and mounting them</h2>
<p>First, on all the storage drives, we will create a new empty partition with fdisk, and format them in XFS.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># For my first drive</span>
sudo fdisk /dev/sda
<span class="hljs-comment"># Then use : </span>
<span class="hljs-comment"># g : Create a new partition table</span>
<span class="hljs-comment"># n : Create a new partition on your drive</span>
<span class="hljs-comment"># w : Write the new table on the drive</span>
</code></pre>
<p>Start over with all the other drives</p>
<p>Then we format them all</p>
<pre><code class="lang-bash">sudo mkfs.xfs /dev/sd{a,b,c,d}1
</code></pre>
<p>Then we will format them all :</p>
<p>Then we will print the ids of the different drives with the <code>blkid</code> command. We use these IDs to complete our /etc/fstab file. The goal of it is to mount automatically the drives on startup.</p>
<p>In the end, my /etc/fstab looks like this :</p>
<pre><code class="lang-bash"><span class="hljs-comment"># /etc/fstab: static file system information.</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># Use 'blkid' to print the universally unique identifier for a</span>
<span class="hljs-comment"># device; this may be used with UUID= as a more robust way to name devices</span>
<span class="hljs-comment"># that works even if disks are added and removed. See fstab(5).</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># &lt;file system&gt; &lt;mount point&gt;   &lt;type&gt;  &lt;options&gt;       &lt;dump&gt;  &lt;pass&gt;</span>
<span class="hljs-comment"># / was on /dev/nvme0n1p2 during curtin installation</span>
/dev/disk/by-uuid/b9da617f-0084-47ff-93ac-f53f91e8a838 / ext4 defaults 0 1
<span class="hljs-comment"># /boot/efi was on /dev/nvme0n1p1 during curtin installation</span>
/dev/disk/by-uuid/BFA1-BF33 /boot/efi vfat defaults 0 1
/swap.img    none    swap    sw    0    0

<span class="hljs-comment"># Data drives</span>
UUID=<span class="hljs-string">"a7005af1-e11f-4a49-8c32-47a19711d39f"</span>    /mnt/data1    xfs    defaults    0    2
UUID=<span class="hljs-string">"99322a38-d6e1-45ab-96eb-21c10bbb621d"</span>    /mnt/data2    xfs    defaults    0    2
UUID=<span class="hljs-string">"eedaa80f-e3a9-4b89-8412-c1eeba7a9077"</span>    /mnt/data3    xfs    defaults    0    2

<span class="hljs-comment"># Parity drive</span>
UUID=<span class="hljs-string">"77bbec02-1662-4953-83c3-8f5f7367b1f0"</span>    /mnt/parity1    xfs    defaults    0    2
</code></pre>
<p>I save the file and create these folders and mount all the drives at once :</p>
<pre><code class="lang-bash">sudo mkdir -p /mnt/data{1,2,3}
sudo mkdir -p /mnt/parity1
sudo mount -a
</code></pre>
<p>Ok, we have our drives formatted and mounted!</p>
<h2 id="heading-mergerfs">MergerFS</h2>
<p>We install MergerFS with a simple command :</p>
<pre><code class="lang-bash">sudo apt install fuse mergerfs <span class="hljs-comment"># MergerFS rely on fuse</span>
</code></pre>
<p><strong><mark>Warning : on my Ubuntu 22.04 LTS, the package version is giving me the resonably outdated version 2.33.3-1 ( Actual version from the Github repo at the moment of writing this post : 2.34.1). If you want the latest bugfixes, install your package from </mark></strong> <a target="_blank" href="https://github.com/trapexit/mergerfs/releases"><strong><mark>GitHub releases</mark></strong></a></p>
<p>We go back to our <code>/etc/fstab</code> file and we are creating a new mount.</p>
<p>Here I decided to group all the drives mounted in <code>/mnt/data*</code> and the grouped drive should be accessible under <code>/mnt/pool</code>. As I want the data that I create to be spread across the drives according to their percentage of free space, I choose the <code>pfrd</code> policy. Feel free to choose another (see <a target="_blank" href="https://github.com/trapexit/mergerfs/tree/2.34.1#policy-descriptions">Policies</a>). For me the line I add looks like this :</p>
<pre><code class="lang-bash"><span class="hljs-comment">#MergerFS</span>
/mnt/data*    /mnt/pool    fuse.mergerfs    allow_other,use_ino,cache.files=partial,dropcacheonclose=<span class="hljs-literal">true</span>,category.create=pfrd    0    0
</code></pre>
<p>We create the directory <code>/mnt/pool</code> and we mount everything again :</p>
<pre><code class="lang-bash">sudo mkdir -p /mnt/pool
sudo mount -a
sudo df -h
</code></pre>
<p>We check and you will see a new drive appeared, having the cumulated capacity of all the drives in the array! If you create one file in the pool mount, you will see it appear on one of your drives.</p>
<p>Cool no? 😎</p>
<h2 id="heading-snapraid">SnapRAID</h2>
<p>OK. We have one big drive, but not of the data in it is safe from one drive failing.</p>
<p>First, we install SnapRAID easily with :</p>
<pre><code class="lang-bash">sudo apt install snapraid
</code></pre>
<p>Then we will edit <code>/etc/snapraid.conf</code> If this file does not exist, don't worry, what has to be inside is pretty simple and we can write it ourselves.</p>
<ol>
<li><p>We want to specify where to store the parity data.</p>
</li>
<li><p>We want to specify which data disk to use.</p>
</li>
<li><p>We want to give to SnapRAID some content files. The content files are like an index of your data. It does not take that much space, so more won't hurt. You can even add one content file on the boot drive.</p>
</li>
<li><p>You can add exclusions for the folders or files you don't want to have the parity generated, to save resources and space, and sync faster.</p>
</li>
<li><p>Last but not least, a little option I like to add, <code>autosave 100</code> specify that during a synchronisation, every 100Gb, the state should be saved. In case of a crash, or whatever happens, this will be safe.</p>
</li>
</ol>
<p>My <code>/etc/snapraid.conf</code> look like this at the end:</p>
<pre><code class="lang-plaintext">parity /mnt/parity1/snapraid.parity
data d1 /mnt/data1/
data d2 /mnt/data2/
data d3 /mnt/data3/
content /home/thenomadcode/snapraid.content
content /mnt/data1/.snapraid.content
content /mnt/data2/.snapraid.content
content /mnt/data3/.snapraid.content
exclude /Backup/
exclude /tmp/
exclude *.bak
autosave 100
</code></pre>
<p>You can launch your first sync with <code>snapraid sync</code> it is quite fast but it also can take a while depending on how much data you have. When this command completes, your data is now <strong>safe</strong> 🎉.</p>
<h1 id="heading-auto-sync">Auto sync</h1>
<p>Wait a minute, my data is safe now, but if I add or modify my files?</p>
<p>Indeed with snapRAID, nothing is running in the background. You have to launch the process manually. To sync it on regular basis, I used a script inspired by Zack Reed. This script was supposed to send an email when finished with a report.</p>
<p>From experience, email is nice, but it needs a configuration. You need to use a mail provider like <em>Mailchimp</em> or <em>Sendgrid</em> to send your email reliably, or you can also use your own Gmail account with a bit of configuration. Personally, I prefer to have my mailbox as clean as possible, so would prefer another solution: <em>Sending a notification to a Slack channel.</em></p>
<h2 id="heading-slack-notifications">Slack notifications</h2>
<p>To receive slack notifications, nothing more simple. Go to <a target="_blank" href="https://api.slack.com">https://api.slack.com</a>. From there click on "Create a new App". Then choose from scratch, enter a name for it, and a workspace, and click create.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675449818750/ce25e828-c8bc-4c3d-a186-aa983b98a5d1.png" alt /></p>
<p>Then to be able to send notifications, go to Incoming Webhook. Active the feature, and click on the "Add a new webhook to your workspace" button. Then grant access for your app to one channel.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675449823385/537a562d-3800-4353-8758-6bbce8b7efc2.png" alt /></p>
<p>Once this is done, you will be given an example of a webhook that you can call using curl.</p>
<p>If you just copy-paste this into your terminal, you will receive it on slack directly! Perfect! 🔔</p>
<p>Slack is always open because it is the way my main customer decided to work. So I find it handy to be able to receive this notification here. But feel free to use the app you want, Discord or Microsoft Teams (if you are into that) also have a webhooks feature.</p>
<h2 id="heading-automated-parity-script">Automated parity script</h2>
<p>Now that this works, let's copy the script of Zack Reed that you can find <a target="_blank" href="https://zackreed.me/updated-snapraid-sync-script/">here</a>, and adapt it to send slack messages instead. You can find <a target="_blank" href="https://gist.github.com/thenomadcode/678ddbbfe7042ca9e0b8ede763ad9405">my slack version on my gist right here</a>.</p>
<p>This script has several variables that you can modify to suit your needs better.</p>
<ul>
<li><p>The <code>DEL_THRESHOLD</code> and <code>UP_THRESHOLD</code> will allow you to track the number of deleted or updated files. If the number of those files is exceeding the threshold numbers, the script will stop and trigger an alert (with slack in my case). This is preventing the script to modify the existing parity in case of accidental deletions of files and can prevent encryption malware that would encrypt all your files. This way you should be able to recover your deleted or modified files to their original state. Set it as you want according to what you consider to be an unusual number.</p>
</li>
<li><p>The <code>SYNC_WARN_THRESHOLD</code> variable is here to force a synchronisation after a certain amount of warning generated by the Updated/deleted files thresholds. Imagine you are changing a lot of files on purpose and the <code>SYNC_WARN_THRESHOLD=1</code>, you will receive an alert. As you know that you are the one responsible for all these changes, you can just ignore them. The next time the script run, it will sync everything because one warning was already emitted.</p>
</li>
<li><p>You can also set up some settings for the scrubbing, so you don't verify all your data each time you run the script, but a percentage of it, thanks to :</p>
</li>
</ul>
<pre><code class="lang-bash">SCRUB_PERCENT=20
SCRUB_AGE=10
</code></pre>
<ul>
<li>Don't forget to set the <code>SLACK_WEBHOOK_URL</code> to your webhook endpoint :</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-comment"># Slack</span>
SLACK_WEBHOOK_URL=<span class="hljs-string">"https://hooks.slack.com/services/XXXXXXXX/XXXXXXX/XXXXXXXXXXXXXXXXXXXXXXX"</span>
</code></pre>
<p>Everything set? Good, save the script, and make it executable. Launch it, just to see that everything works fine and that you receive the notification. 🔔</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675450399528/9e06b6f8-7bf2-4c11-8acd-c3f860cfc7e4.png" alt class="image--center mx-auto" /></p>
<p>Then you have one last decision to make: <strong>when to call this script</strong>. Indeed, if you add a new file to your pool, it will be unprotected and you can lose it in case of a failure until the parity synchronisation is done. For my usage, I choose to call it once a night. I set up a cron task for that, and if I add a lot of pictures at once, I call it manually to feel safer.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># m h  dom mon dow   command</span>
0 3 * * * /home/thenomadcode/scripts/snapsync.sh
</code></pre>
<p>But you can call it several times a day if you don't have that much data or if you consider that your data is more precious than mine. <em>Your Server, Your Rules.</em> 😎</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>We have now a real alternative to RAID systems. <strong>MergerFS</strong> to see all your disks as one, and <strong>SnapRAID</strong> to handle parity are <strong>working seamlessly together</strong>. The parity is handled at the <strong>file level</strong> and not at the block one, and it brings <strong>more simplicity</strong>.</p>
<p>If tomorrow's drive sizes increase and I want to replace my drives with bigger ones, I will just have to copy content over the new drive and make it mount at the same mount point as the old one. This is a <strong>game changer</strong> compared to having to put your RAID array in a degraded state, and rebuild it, soliciting heavily CPU and all drives, with the possibility it can create a failure while doing it.</p>
<p>If like me you just use your home server to store files from time to time, movies that you want to watch or content that you can re-download, this is more than enough! If you want to use this in a more intensive context, with a lot of critical files changing all the time that you can't afford to lose, then RAID is still the way to go.</p>
<p>PS: If you want to receive more stories like this, don't hesitate to <a target="_blank" href="https://thenomadcode.tech/newsletter">subscribe to my newsletter</a> or to <a target="_blank" href="https://twitter.com/the_nomad_code">follow me on twitter</a></p>
]]></content:encoded></item></channel></rss>