<?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: Pavel Bashkardin</title>
    <description>The latest articles on DEV Community by Pavel Bashkardin (@ng256).</description>
    <link>https://dev.to/ng256</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%2F4015151%2F2b8a16d4-469a-4c35-9f97-63851ff16b4f.jpg</url>
      <title>DEV Community: Pavel Bashkardin</title>
      <link>https://dev.to/ng256</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ng256"/>
    <language>en</language>
    <item>
      <title>Single-file INI editor for .NET that preserves formatting by editing the original text</title>
      <dc:creator>Pavel Bashkardin</dc:creator>
      <pubDate>Sat, 25 Jul 2026 23:11:44 +0000</pubDate>
      <link>https://dev.to/ng256/single-file-ini-editor-for-net-that-preserves-formatting-by-editing-the-original-text-k2b</link>
      <guid>https://dev.to/ng256/single-file-ini-editor-for-net-that-preserves-formatting-by-editing-the-original-text-k2b</guid>
      <description>&lt;p&gt;I wrote a single-file INI parser/editor for .NET that preserves formatting using regex instead of dictionaries with JSON support.&lt;/p&gt;

&lt;p&gt;I've been experimenting with a different approach to editing INI files in C#.&lt;/p&gt;

&lt;p&gt;Most libraries parse the file into dictionaries or objects and then regenerate the entire file when saving. That works well for configuration data, but it usually destroys comments, blank lines, indentation, entry order, or duplicate keys.&lt;/p&gt;

&lt;p&gt;Instead, I treat the INI file as plain text and index it with a single regular expression. The parser stores only regex matches and edits the original text directly, so unchanged parts of the file remain byte-for-byte identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some features:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preserves formatting&lt;/strong&gt; - comments, blank lines and whitespace remain untouched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate keys&lt;/strong&gt; - &lt;code&gt;Path=/bin&lt;/code&gt;, &lt;code&gt;Path=/usr/bin&lt;/code&gt; → &lt;code&gt;ReadStrings()&lt;/code&gt; returns both values.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Paths]&lt;/span&gt;
&lt;span class="py"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin&lt;/span&gt;
&lt;span class="py"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global entries&lt;/strong&gt; - supports key/value pairs before the first section.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configurable comparison&lt;/strong&gt; - choose &lt;code&gt;Ordinal&lt;/code&gt;, &lt;code&gt;InvariantCulture&lt;/code&gt;, case-sensitive or insensitive matching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escape sequences&lt;/strong&gt; - &lt;code&gt;"Hello\nWorld!"&lt;/code&gt; ↔ actual multiline text:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hello&lt;br&gt;
  World!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiline values&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;  &lt;span class="py"&gt;Script&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;#!/bin/sh
&lt;/span&gt;  &lt;span class="err"&gt;echo&lt;/span&gt; &lt;span class="err"&gt;Hello&lt;/span&gt;
  &lt;span class="err"&gt;echo&lt;/span&gt; &lt;span class="err"&gt;World&lt;/span&gt;
  &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extracted text:&lt;/p&gt;

&lt;p&gt;#!/bin/sh&lt;br&gt;
  echo Hello&lt;br&gt;
  echo World&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Embedded JSON&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;  &lt;span class="py"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;"timeout":&lt;/span&gt; &lt;span class="err"&gt;30,&lt;/span&gt;
    &lt;span class="err"&gt;"retry":&lt;/span&gt; &lt;span class="err"&gt;5&lt;/span&gt;
  &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read/write it as a &lt;strong&gt;string&lt;/strong&gt; or as an &lt;strong&gt;object&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Object mapping&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;  &lt;span class="nn"&gt;[Network]&lt;/span&gt;
  &lt;span class="py"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8080&lt;/span&gt;
  &lt;span class="py"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;IniSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Network"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NetworkSettings&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;IniEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Port"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ConnectionPort&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;IniIgnore&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Comment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single source file&lt;/strong&gt; - just add &lt;code&gt;IniFile.cs&lt;/code&gt; to project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No external dependencies.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The multiline support was probably the most interesting part. The parser recognizes balanced &lt;code&gt;{ ... }&lt;/code&gt; blocks using .NET balancing groups, so embedded JSON doesn't get confused with INI section headers.&lt;/p&gt;

&lt;p&gt;I'd appreciate any feedback, especially if you spot edge cases I haven't considered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ng256/IniFile" rel="noopener noreferrer"&gt;Github project&lt;/a&gt;&lt;br&gt;
&lt;a href="https://regex101.com/r/mul0C2/13" rel="noopener noreferrer"&gt;Regex playground&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Wan Monitor — Tiny WAN Failover Controller for OpenWrt</title>
      <dc:creator>Pavel Bashkardin</dc:creator>
      <pubDate>Sat, 04 Jul 2026 14:48:34 +0000</pubDate>
      <link>https://dev.to/ng256/wan-monitor-tiny-failover-controller-for-openwrt-4o8e</link>
      <guid>https://dev.to/ng256/wan-monitor-tiny-failover-controller-for-openwrt-4o8e</guid>
      <description>&lt;p&gt;How I built a tiny &lt;a href="https://github.com/ng256/WanMonitor" rel="noopener noreferrer"&gt;WAN failover tool&lt;/a&gt; for my 8‑MB OpenWrt router (and why mwan3 wasn’t an option)&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Problem: when the Internet goes down, so does my home network
&lt;/h2&gt;

&lt;p&gt;Like many of us, I rely on a wired broadband connection for my home internet. But things happen — the provider goes offline for maintenance, a cable gets cut, or (let’s be honest) you forget to pay the bill. When that happens, I wanted my router to automatically switch to a backup connection: a Wi‑Fi hotspot from my phone.&lt;/p&gt;

&lt;p&gt;I’m running &lt;strong&gt;OpenWrt&lt;/strong&gt; on a tiny &lt;strong&gt;Xiaomi Mi Router C4&lt;/strong&gt; . It’s a cheap, reliable device, but it comes with severe storage constraints. Here’s what the filesystem looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/root                 6912      6912         0 100% /rom
tmpfs                    28584       336     28248   1% /tmp
/dev/mtdblock9            5760      5628       132  98% /overlay
overlayfs:/overlay        5760      5628       132  98% /
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only &lt;strong&gt;~5.7 MB&lt;/strong&gt; of writable flash are available, and I’m already using 98% of it. Every kilobyte counts. I needed a solution that would fit into this cramped space and still do the job reliably.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Existing solutions: mwan3 – the obvious choice
&lt;/h2&gt;

&lt;p&gt;The most popular tool for multi‑WAN management on OpenWrt is &lt;strong&gt;mwan3&lt;/strong&gt;. It’s mature, well‑documented, and offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load balancing across multiple connections&lt;/li&gt;
&lt;li&gt;Policy‑based routing (e.g., specific IPs or ports through specific interfaces)&lt;/li&gt;
&lt;li&gt;Advanced failover with custom tracking (ICMP, DNS, ARP)&lt;/li&gt;
&lt;li&gt;A full LuCI web interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On paper, mwan3 should be able to handle my failover scenario. But when I looked at its requirements, I hit a wall.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why mwan3 didn’t fit
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storage footprint&lt;/strong&gt;: mwan3 itself is not huge, but its dependency chain (iptables extensions, tracking modules, and the LuCI UI) quickly grows beyond what I could comfortably afford on an 8‑MB flash device. Installing everything would have consumed the little remaining space I had.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory overhead&lt;/strong&gt;: mwan3 introduces persistent tracking processes and conntrack load, which can become noticeable on low‑memory devices (64 MB RAM) under heavy NAT usage. It’s not “several MB” per se, but the cumulative effect of conntrack and multiple trackers can strain the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Configuring mwan3 involves defining interfaces, members, policies, and tracking rules — powerful, but overkill for my simple "switch to backup when primary is down" use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also considered other lightweight options like &lt;strong&gt;ifplugd&lt;/strong&gt; or custom scripts that watch the default gateway, but they either lacked quality‑based switching or required too much hand‑tuning.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Why I decided to "reinvent the wheel"
&lt;/h2&gt;

&lt;p&gt;I realised that my requirements were actually quite simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Periodically check if the primary interface is still usable.&lt;/li&gt;
&lt;li&gt;Measure not just "up/down", but also &lt;strong&gt;latency&lt;/strong&gt;, &lt;strong&gt;packet loss&lt;/strong&gt;, and &lt;strong&gt;jitter&lt;/strong&gt; — so I can switch before the link becomes unusable.&lt;/li&gt;
&lt;li&gt;Automatically change the default route to the best available interface.&lt;/li&gt;
&lt;li&gt;Be &lt;em&gt;extremely&lt;/em&gt; lightweight — preferable under 100 KB of flash, with no kernel modules or extra daemons.&lt;/li&gt;
&lt;li&gt;Provide a basic web interface to see status and manually override.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing on the market met all these criteria at once. So I decided to write my own tool. I called it &lt;strong&gt;wanmon&lt;/strong&gt; (WAN Monitor).&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The birth of wanmon: a minimalist approach
&lt;/h2&gt;

&lt;p&gt;Wanmon is a collection of shell scripts that work together in three independent stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Measurement&lt;/strong&gt; (&lt;code&gt;wm-state.sh&lt;/code&gt;) — sends pings to a configurable host and records latency, loss, and jitter for each interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decision&lt;/strong&gt; (&lt;code&gt;wm-select.sh&lt;/code&gt;) — computes a quality score for each interface, applies smoothing and hysteresis, and picks the best candidate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application&lt;/strong&gt; (&lt;code&gt;wm-apply.sh&lt;/code&gt;) — updates the kernel routing table accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Daemon&lt;/strong&gt; (&lt;code&gt;wm-daemon.sh&lt;/code&gt;) — a service that continuously checks the status of network interfaces by running aforementioned scripts in a loop, with a specified interval. If necessary, it switches the connection to a more preferred one.&lt;/p&gt;

&lt;p&gt;These and other scripts are stored in the &lt;code&gt;/usr/bin&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;This separation of concerns makes wanmon a mini‑SD‑WAN controller in its own right. The key idea is that connectivity is not binary. A WAN link can be "up" but still unusable due to high latency or packet loss. Wanmon treats network quality as a &lt;strong&gt;continuous cost function&lt;/strong&gt; rather than a reachability flag.&lt;/p&gt;

&lt;p&gt;The score for each interface is defined as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;score = (latency + jitter) + (loss² / loss_divisor) + bias
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lowest score wins. Smoothing and hysteresis prevent flapping. The entire system uses no iptables, no conntrack, and no heavy dependencies—just &lt;code&gt;jq&lt;/code&gt; for JSON parsing (which I already had installed). The total size of all scripts and the web interface is &lt;strong&gt;~80 KB&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How wanmon compares to mwan3
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;mwan3&lt;/th&gt;
&lt;th&gt;wanmon&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;≥1–2 MB (with dependencies)&lt;/td&gt;
&lt;td&gt;&amp;lt;100 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAM usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate (daemon + conntrack + trackers)&lt;/td&gt;
&lt;td&gt;Minimal (fork‑exec loop)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complex (policies, members, trackers)&lt;/td&gt;
&lt;td&gt;Single JSON file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Load balancing + failover&lt;/td&gt;
&lt;td&gt;Failover only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decision logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reachability (up/down)&lt;/td&gt;
&lt;td&gt;Latency + loss + jitter (cost‑based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Web interface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Heavy LuCI integration&lt;/td&gt;
&lt;td&gt;Lightweight standalone HTML or single LuCI page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Modifiability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard (C/shell, complex architecture)&lt;/td&gt;
&lt;td&gt;Easy (simple shell, tweakable in minutes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Policy‑based routing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Per‑connection tracking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QoS integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Possible via scripts&lt;/td&gt;
&lt;td&gt;Not included&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This table shows that wanmon is not a replacement for mwan3 in all scenarios—it deliberately trades flexibility for minimalism.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Wanmon in action – a real‑world setup
&lt;/h2&gt;

&lt;p&gt;On my Xiaomi C4, I connected my phone’s Wi‑Fi hotspot as a second WAN interface (mobile hotspot). Wanmon automatically detects both the wired  interface (&lt;code&gt;wan&lt;/code&gt;) and the hotspot interface (&lt;code&gt;wwan&lt;/code&gt;). I configured a &lt;code&gt;bias&lt;/code&gt; (metric) to prefer the wired link as long as its quality is acceptable.&lt;/p&gt;

&lt;p&gt;When the wired connection degrades (e.g., ISP outage), wanmon notices the increased latency or packet loss. The score rises, and after the hysteresis threshold is crossed, it switches the default route to the hotspot. Once the wired link recovers, it switches back — smoothly and without user intervention.&lt;/p&gt;

&lt;p&gt;The web interface at &lt;code&gt;http://router-ip/wanmon.html&lt;/code&gt; or via &lt;code&gt;Services → WAN Monitor&lt;/code&gt; in LuCI gives me a clear dashboard showing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daemon status&lt;/li&gt;
&lt;li&gt;Current default route&lt;/li&gt;
&lt;li&gt;Live scores and telemetry (latency, loss, jitter) per interface&lt;/li&gt;
&lt;li&gt;A dropdown to manually force any interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this fits within the 5.7 MB overlay — leaving room for other essential tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Conclusion: sometimes, less is more
&lt;/h2&gt;

&lt;p&gt;mwan3 is a fantastic tool for routers with ample resources and complex routing needs. But for devices like the Xiaomi C4 — with just 8 MB flash and 64 MB RAM — it’s often overkill. Wanmon proves that you don’t need a heavyweight solution to get reliable, intelligent WAN failover.&lt;/p&gt;

&lt;p&gt;If you’re in a similar situation — struggling to squeeze more functionality into a small OpenWrt device — I encourage you to try wanmon. It’s open source, easy to customise, and it just works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check it out on GitHub:&lt;/strong&gt; &lt;a href="https://github.com/ng256/WanMonitor" rel="noopener noreferrer"&gt;https://github.com/ng256/WanMonitor&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have questions or ideas? Feel free to &lt;a href="https://github.com/ng256/WanMonitor/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt; or &lt;a href="https://github.com/ng256/WanMonitor/pulls" rel="noopener noreferrer"&gt;submit a pull request&lt;/a&gt;. Let’s keep our routers smart, even when they’re small.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>openwrt</category>
      <category>networkfailover</category>
      <category>embeddedlinux</category>
      <category>wanmon</category>
    </item>
  </channel>
</rss>
