<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Gabriel Windlin</title>
      <link>https://gawindlin.com</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://gawindlin.com/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 21 Feb 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>What a Custom Linux Desktop Actually Looks Like</title>
          <pubDate>Sat, 21 Feb 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/1011-post/</link>
          <guid>https://gawindlin.com/blog/1011-post/</guid>
          <description xml:base="https://gawindlin.com/blog/1011-post/">&lt;p&gt;Most people don&#x27;t think about their desktop as something you can change. Windows manages your windows. macOS puts the dock somewhere. You drag things around. That&#x27;s that.&lt;&#x2F;p&gt;
&lt;p&gt;On Linux, none of that is fixed. The thing that draws your windows, handles your taskbar, decides how you switch between apps — it&#x27;s just a program. You can swap it out, or configure it yourself.&lt;&#x2F;p&gt;
&lt;p&gt;This post isn&#x27;t &quot;Linux is better.&quot; Here&#x27;s what&#x27;s possible and what it looks like in practice.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;hyprland&quot;&gt;Hyprland&lt;&#x2F;h2&gt;
&lt;p&gt;The centerpiece is &lt;strong&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hyprland.org&#x2F;&quot;&gt;Hyprland&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;, a tiling window manager. If you haven&#x27;t used one, the concept takes five minutes to understand and then it&#x27;s hard to go back.&lt;&#x2F;p&gt;
&lt;p&gt;In a normal desktop, windows float. You resize them, drag them, snap them to sides. In a tiling manager, windows arrange themselves. Open a terminal, full screen. Open a second, split in half. Open a third, thirds. Every pixel is used, nothing overlaps, and you move between them with the keyboard.&lt;&#x2F;p&gt;
&lt;p&gt;It sounds rigid. In practice, you stop thinking about window placement. The layout just happens. You think about your work instead.&lt;&#x2F;p&gt;
&lt;p&gt;Hyprland runs on Wayland (the modern display system) and is smooth with good performance. Everything—every keybinding, animation, behavior—is in a plain text config file.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;pywal&quot;&gt;Pywal&lt;&#x2F;h2&gt;
&lt;p&gt;One of the cooler parts: &lt;strong&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dylanaraps&#x2F;pywal&quot;&gt;Pywal&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;. It looks at your wallpaper, extracts colors, and applies them everywhere—terminal, status bar, window borders, app launcher. Change the wallpaper, the whole system recolors itself.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not just cosmetic. The desktop feels visually coherent without manual work. Pick an image, run one command, everything updates.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-tools&quot;&gt;The Tools&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s what actually runs day-to-day:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Kitty&lt;&#x2F;strong&gt; — GPU-accelerated terminal. Scrolling through thousands of lines of logs never stutters. Bonus: it displays images inline, which matters for the file manager below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Zsh + Starship&lt;&#x2F;strong&gt; — Starship shows your directory, git branch, unstaged changes, language runtime. Small things, but they remove a lot of &lt;code&gt;git status&lt;&#x2F;code&gt; reflexes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Yazi&lt;&#x2F;strong&gt; — A terminal file manager. Three-column layout, navigate with &lt;code&gt;h&#x2F;j&#x2F;k&#x2F;l&lt;&#x2F;code&gt;, previews files as you move. Text files show syntax-highlighted content. Images render inline via Kitty. PDFs, videos, archives — all previewed without opening anything. When you quit, your shell follows you to wherever you were.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Zoxide + fzf&lt;&#x2F;strong&gt; — &lt;code&gt;cd&lt;&#x2F;code&gt; is tedious. Zoxide tracks which directories you visit and weights them by frequency. After a few days, &lt;code&gt;j dot&lt;&#x2F;code&gt; jumps straight to &lt;code&gt;~&#x2F;dotfiles&lt;&#x2F;code&gt;. Pair it with fzf and you get a fuzzy jump menu in under a second.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Zellij&lt;&#x2F;strong&gt; — Terminal multiplexer. Split panes, persistent sessions. You close your laptop, come back the next morning, reattach, and everything is exactly where you left it—server still running, logs still scrolling. Once you use it, a single terminal window feels wasteful.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Neovim (LazyVim)&lt;&#x2F;strong&gt; — A text editor operated almost entirely through keyboard shortcuts. No mouse, no menus. The learning curve is steep but the ceiling is high. LazyVim gives you LSP, fuzzy file finding, git integration, and syntax highlighting out of the box.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Zathura&lt;&#x2F;strong&gt; — PDF viewer controlled by keyboard. Open a slide deck, navigate with &lt;code&gt;j&#x2F;k&lt;&#x2F;code&gt;, search with &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;. No mouse, no toolbar.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Aerc&lt;&#x2F;strong&gt; — Email in the terminal. Sounds like a joke until you realize how much calmer it is without a browser tab competing for your attention.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;eza, bat, ripgrep, delta&lt;&#x2F;strong&gt; — Modern replacements for &lt;code&gt;ls&lt;&#x2F;code&gt;, &lt;code&gt;cat&lt;&#x2F;code&gt;, &lt;code&gt;grep&lt;&#x2F;code&gt;, and git diffs. Colored output, icons, syntax highlighting, way faster. They don&#x27;t change what you type. They just make the output worth reading.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-status-bar&quot;&gt;The Status Bar&lt;&#x2F;h2&gt;
&lt;p&gt;Waybar shows active workspace, window title, CPU, network, volume, brightness, clock. Every element is positioned and formatted in a config file. Add a weather widget? Add a module. Remove the clock? Delete a line.&lt;&#x2F;p&gt;
&lt;p&gt;Nothing is decided for you. Nothing is mysterious. It&#x27;s all text files.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;what-if-it-breaks&quot;&gt;What If It Breaks?&lt;&#x2F;h2&gt;
&lt;p&gt;Arch has a reputation for being fragile. It can break, and you&#x27;re more responsible for fixing it than on Ubuntu or macOS. That&#x27;s a real tradeoff.&lt;&#x2F;p&gt;
&lt;p&gt;The upside: when something breaks, you understand why. This setup includes an &lt;code&gt;install.sh&lt;&#x2F;code&gt; script that automates everything — packages, config files, symlinks via GNU Stow. Fresh Arch install to fully working desktop takes under 20 minutes.&lt;&#x2F;p&gt;
&lt;p&gt;The full configuration is on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;dotfiles&quot;&gt;Codeberg&lt;&#x2F;a&gt; if you want to look through the files.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>Building Kiroku-TUI: My First Rust TUI and Why I Needed It</title>
          <pubDate>Wed, 18 Feb 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/1010-post/</link>
          <guid>https://gawindlin.com/blog/1010-post/</guid>
          <description xml:base="https://gawindlin.com/blog/1010-post/">&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h2&gt;
&lt;p&gt;I take a lot of notes. They&#x27;re mostly markdown files — quick thoughts, daily logs, stuff I want to reference later. The problem is I live in the terminal, and most note apps assume you don&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;I tried Obsidian. It&#x27;s fine, I guess — the graph view is cool, plugins are everywhere, and I get why everyone recommends it. But I open terminals all day. The idea of clicking out of my workflow just to write a note felt wrong. I wanted something that lived where I already lived.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kiroku&quot;&gt;Kiroku&lt;&#x2F;h2&gt;
&lt;p&gt;So I built &lt;strong&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;kiroku&quot;&gt;Kiroku&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;. It&#x27;s a TUI note manager. Fuzzy search, markdown preview, git sync for backup. No Electron, no GUI, just a binary that runs in any terminal.&lt;&#x2F;p&gt;
&lt;p&gt;The workflow is basically: type to search, arrow keys to navigate, enter to open. Sort of like lazygit meets yazi, but for notes. That&#x27;s really all I wanted.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;learning-rust&quot;&gt;Learning Rust&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been wanting to learn Rust for years. I&#x27;d do exercism problems, read the book, build tiny toy programs — and then forget everything a week later. I needed something real to work on.&lt;&#x2F;p&gt;
&lt;p&gt;Kiroku was that thing. It&#x27;s small enough to finish, but big enough to run into real problems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The borrow checker fought me on the search logic for way too long&lt;&#x2F;li&gt;
&lt;li&gt;Working with &lt;code&gt;serde_yaml&lt;&#x2F;code&gt; for frontmatter was straightforward once I figured out the right types&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ratatui&lt;&#x2F;code&gt; is well-documented but there&#x27;s a learning curve to immediate-mode rendering&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One specific moment: I spent 3 hours on a compiler error that turned out to be a missing &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt;. That&#x27;s Rust in a nutshell for me so far.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-stack&quot;&gt;The Stack&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ratatui&lt;&#x2F;strong&gt; — TUI framework. Good docs, active maintenance.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;fuzzy-matcher&lt;&#x2F;strong&gt; — For search. Simple, fast.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;serde&lt;&#x2F;strong&gt; + &lt;strong&gt;serde_yaml&lt;&#x2F;strong&gt; — Parsing YAML frontmatter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The search code is the part I&#x27;m most happy with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; update_search&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;search_query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;is_empty&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;notes &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;all_notes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; matcher&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F9E2AF;font-style: italic;&quot;&gt; SkimMatcherV2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;        let mut&lt;&#x2F;span&gt;&lt;span&gt; matches&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F9E2AF;font-style: italic;&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DCEB;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F9E2AF;font-style: italic;&quot;&gt;Note&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; i64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DCEB;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt; self&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span&gt;all_notes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;filter_map&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;note&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                matcher&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;                    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;fuzzy_match&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;note&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;search_query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;                    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;score&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;note&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; score&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;            })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;collect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        matches&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;sort_by&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;cmp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;notes &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; matches&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;into_iter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; _&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;())&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;collect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s Next&lt;&#x2F;h2&gt;
&lt;p&gt;I use Kiroku every day now. A few things I want to add:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Calendar view for time-based note browsing&lt;&#x2F;li&gt;
&lt;li&gt;Better file watcher performance (it chokes on large directories)&lt;&#x2F;li&gt;
&lt;li&gt;Homebrew and AUR packages&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you try it and have thoughts, feel free to open an issue. Or don&#x27;t — it&#x27;s here if you need it.&lt;&#x2F;p&gt;
&lt;p&gt;Source on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;kiroku&quot;&gt;Codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>Our Place: Building a Shared Home Across 9,000 Kilometers</title>
          <pubDate>Sat, 14 Feb 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/1001-post/</link>
          <guid>https://gawindlin.com/blog/1001-post/</guid>
          <description xml:base="https://gawindlin.com/blog/1001-post/">&lt;h2 id=&quot;the-vision-a-digital-apartment&quot;&gt;The Vision: A Digital Apartment&lt;&#x2F;h2&gt;
&lt;p&gt;Long-distance relationships are often lived through a fragmented series of apps: WhatsApp for chatting, Zoom for calls, and shared Google Sheets for travel planning. For our one-year anniversary, I wanted to create something more cohesive—a single &quot;Digital Apartment&quot; that serves as a shared space for my girlfriend in Brazil and me in Switzerland.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;our-place&quot;&gt;&lt;strong&gt;&quot;Our Place&quot;&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; is a React-based web application that mimics a physical home. By navigating through different &quot;rooms,&quot; we can interact with shared memories, plan our future, and spend time together in real-time.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-guided-tour-of-the-apartment&quot;&gt;A Guided Tour of the Apartment&lt;&#x2F;h2&gt;
&lt;p&gt;The core of the app is the &lt;strong&gt;Room Controller&lt;&#x2F;strong&gt;. Instead of a traditional navigation bar, I used an AI-generated image of a cozy apartment as the interface. Using relative positioning and &quot;Click Zones,&quot; different furniture items trigger specific modules, each living in its own dedicated component:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Fridge (&lt;code&gt;Fridge.jsx&lt;&#x2F;code&gt;):&lt;&#x2F;strong&gt; A real-time, drag-and-drop canvas using &lt;code&gt;@dnd-kit&lt;&#x2F;code&gt; for notes and photos. It features a &lt;strong&gt;Deep Dive Clipboard&lt;&#x2F;strong&gt; that serves a new thought-provoking question every day.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The TV (&lt;code&gt;LivingRoom.jsx&lt;&#x2F;code&gt;):&lt;&#x2F;strong&gt; A synchronized YouTube player where playback and video selection are synced in real-time across the ocean.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Couch (&lt;code&gt;Couch.jsx&lt;&#x2F;code&gt;):&lt;&#x2F;strong&gt; Our digital photo album. It handles image uploads to Supabase Storage and uses the &lt;strong&gt;OpenCage API&lt;&#x2F;strong&gt; to geocode our memories.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Map (&lt;code&gt;Timeline.jsx&lt;&#x2F;code&gt;):&lt;&#x2F;strong&gt; An interactive &lt;strong&gt;Leaflet&lt;&#x2F;strong&gt; map that pins our geocoded memories, followed by a vertical scrollytelling timeline of our relationship.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Window (&lt;code&gt;FutureDreams.jsx&lt;&#x2F;code&gt;):&lt;&#x2F;strong&gt; A collaborative planner for travel bucket lists and long-term life goals, complete with progress bars and financial tracking.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The PC (&lt;code&gt;Arcade.jsx&lt;&#x2F;code&gt;):&lt;&#x2F;strong&gt; A &quot;Relationship RPG&quot; that gamifies our daily life, allowing us to earn gold for tasks and spend it on shared rewards.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Carpet (&lt;code&gt;Milestones.jsx&lt;&#x2F;code&gt;):&lt;&#x2F;strong&gt; A digital ledger that tracks our relationship tenure down to the day and aggregates shared stats from across the app.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Title Area (&lt;code&gt;Utilities.jsx&lt;&#x2F;code&gt;):&lt;&#x2F;strong&gt; A set of practical tools for international life, including dual clocks for Switzerland and Brazil and a real-time BRL&#x2F;CHF currency converter.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This spatial metaphor makes the app feel less like a tool and more like a destination.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;real-time-presence-the-digital-fridge-living-room&quot;&gt;Real-Time Presence: The Digital Fridge &amp;amp; Living Room&lt;&#x2F;h2&gt;
&lt;p&gt;To make the apartment feel &quot;alive,&quot; I used &lt;strong&gt;Supabase Realtime&lt;&#x2F;strong&gt; for instant synchronization across the Atlantic.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-fridge&quot;&gt;The Fridge&lt;&#x2F;h3&gt;
&lt;p&gt;The &quot;Fridge&quot; module is a drag-and-drop canvas using &lt;code&gt;@dnd-kit&lt;&#x2F;code&gt;. We can leave sticky notes, &quot;magnet&quot; photos, and answer a &lt;strong&gt;Daily Deep Dive&lt;&#x2F;strong&gt; question. When one of us moves a note in Switzerland, it moves instantly on the screen in Brazil. It captures that small, domestic joy of leaving a note for your partner to find in the morning.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-virtual-living-room&quot;&gt;The Virtual Living Room&lt;&#x2F;h3&gt;
&lt;p&gt;Watching movies together is a staple of LDR life. I implemented a shared YouTube player using &lt;code&gt;react-youtube&lt;&#x2F;code&gt;. The state (play&#x2F;pause, current time, video URL) is persisted in a Supabase table. If I pause the video to grab a snack, her player pauses too, keeping us in perfect sync without the &quot;3, 2, 1, click!&quot; countdown.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mapping-our-journey-the-couch-timeline&quot;&gt;Mapping Our Journey: The Couch &amp;amp; Timeline&lt;&#x2F;h2&gt;
&lt;p&gt;As we travel between Europe and South America, our history becomes a collection of coordinates.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Couch (Archive):&lt;&#x2F;strong&gt; When we upload a photo, I use the &lt;strong&gt;OpenCage Geocoding API&lt;&#x2F;strong&gt; to turn city names into latitude and longitude.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Timeline:&lt;&#x2F;strong&gt; These memories are then mapped using &lt;strong&gt;React-Leaflet&lt;&#x2F;strong&gt; on a dark-themed interactive map. Scrolling down reveals a vertical &quot;Scrollytelling&quot; timeline, where &lt;strong&gt;Framer Motion&lt;&#x2F;strong&gt; handles the animations to make the journey feel fluid.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;playful-logistics-the-relationship-rpg&quot;&gt;Playful Logistics: The Relationship RPG&lt;&#x2F;h2&gt;
&lt;p&gt;To make daily chores and goal-setting more engaging, I built the &lt;strong&gt;Relationship RPG&lt;&#x2F;strong&gt;. It’s a gamified task tracker where we earn &quot;Gold&quot; for completing shared or individual tasks. This currency can then be redeemed for custom rewards (like &quot;Partner chooses the next movie&quot;).&lt;&#x2F;p&gt;
&lt;p&gt;It’s backed by a straightforward PostgreSQL schema, but the impact on our daily interaction is significant—it turns the logistics of a relationship into a collaborative game.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;&quot;Our Place&quot; taught me that the best use of technology is often the most personal one. By using modern web abstractions like React and Supabase, I was able to build a functional, reliable, and emotionally resonant space that helps bridge the 9,000-kilometer gap.&lt;&#x2F;p&gt;
&lt;p&gt;Happy Valentine&#x27;s Day &amp;lt;3&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>Deep Dive: What actually happens when you type &#x27;ls&#x27;?</title>
          <pubDate>Wed, 11 Feb 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/1000-post/</link>
          <guid>https://gawindlin.com/blog/1000-post/</guid>
          <description xml:base="https://gawindlin.com/blog/1000-post/">&lt;h2 id=&quot;the-interview-question&quot;&gt;The Interview Question&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;ve ever interviewed for a systems engineering role, you know this question is coming: &lt;em&gt;&quot;What happens when you type &lt;code&gt;ls -l&lt;&#x2F;code&gt; and hit Enter?&quot;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The typical answer? &lt;em&gt;&quot;The shell finds the &lt;code&gt;ls&lt;&#x2F;code&gt; program and runs it.&quot;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;ll get you a polite handshake and a &quot;we&#x27;ll be in touch.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;The actual answer? It involves shell parsers, &lt;code&gt;$PATH&lt;&#x2F;code&gt; lookups, &lt;code&gt;fork()&lt;&#x2F;code&gt; and &lt;code&gt;execve()&lt;&#x2F;code&gt; system calls, the ELF loader, and filesystem inodes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-1-the-shell-reads-your-input&quot;&gt;Step 1: The Shell Reads Your Input&lt;&#x2F;h2&gt;
&lt;p&gt;Before &lt;code&gt;ls&lt;&#x2F;code&gt; does anything, your shell (Bash, Zsh, or Fish) is running a loop, waiting for your next command. It&#x27;s blocking on a &lt;code&gt;read()&lt;&#x2F;code&gt; system call to stdin (file descriptor 0).&lt;&#x2F;p&gt;
&lt;p&gt;You type &lt;code&gt;ls -l&lt;&#x2F;code&gt; and hit Enter.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Tokenization:&lt;&#x2F;strong&gt; The shell splits your input into tokens: &lt;code&gt;[&quot;ls&quot;, &quot;-l&quot;]&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Alias expansion:&lt;&#x2F;strong&gt; Most systems alias &lt;code&gt;ls&lt;&#x2F;code&gt; to &lt;code&gt;ls --color=auto&lt;&#x2F;code&gt; by default, so your command actually becomes &lt;code&gt;[&quot;ls&quot;, &quot;--color=auto&quot;, &quot;-l&quot;]&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Built-in check:&lt;&#x2F;strong&gt; The shell looks through its list of built-in commands (&lt;code&gt;cd&lt;&#x2F;code&gt;, &lt;code&gt;echo&lt;&#x2F;code&gt;, &lt;code&gt;source&lt;&#x2F;code&gt;, etc.) to see if &lt;code&gt;ls&lt;&#x2F;code&gt; is one of them. It&#x27;s not, so we keep going.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-finding-the-executable&quot;&gt;Step 2: Finding the Executable&lt;&#x2F;h2&gt;
&lt;p&gt;Since &lt;code&gt;ls&lt;&#x2F;code&gt; isn&#x27;t a built-in, the shell needs to find the actual binary on disk. It doesn&#x27;t search randomly; it walks through each directory in your &lt;code&gt;$PATH&lt;&#x2F;code&gt; environment variable, in order.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s say your &lt;code&gt;$PATH&lt;&#x2F;code&gt; is &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;bin:&#x2F;usr&#x2F;bin:&#x2F;bin&lt;&#x2F;code&gt;. The shell checks:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&#x2F;usr&#x2F;local&#x2F;bin&#x2F;ls&lt;&#x2F;code&gt; (not there)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;ls&lt;&#x2F;code&gt; (found it)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;It uses system calls like &lt;code&gt;stat()&lt;&#x2F;code&gt; or &lt;code&gt;access()&lt;&#x2F;code&gt; to check if the file exists and is executable.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Optimization Note:&lt;&#x2F;strong&gt; Modern shells don&#x27;t walk the &lt;code&gt;$PATH&lt;&#x2F;code&gt; every time. They maintain a hash table of previously found commands. You can see this by running the &lt;code&gt;hash&lt;&#x2F;code&gt; command in Bash. If you move a binary, you might see a &quot;file not found&quot; error because the shell is still looking at the old hashed path.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-3-the-fork-exec-dance&quot;&gt;Step 3: The Fork-Exec Dance&lt;&#x2F;h2&gt;
&lt;p&gt;The shell cannot simply &quot;become&quot; &lt;code&gt;ls&lt;&#x2F;code&gt;. If it did, you would lose your shell session entirely. Instead, it creates a copy of itself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-fork&quot;&gt;The Fork&lt;&#x2F;h3&gt;
&lt;p&gt;The shell calls &lt;code&gt;fork()&lt;&#x2F;code&gt;, which creates a new process that is an exact duplicate of the parent. Now you have two processes running the same shell code:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parent process (your shell):&lt;&#x2F;strong&gt; Calls &lt;code&gt;wait()&lt;&#x2F;code&gt; and blocks until the child finishes.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Child process:&lt;&#x2F;strong&gt; Currently running shell code, but preparing to switch.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;the-exec&quot;&gt;The Exec&lt;&#x2F;h3&gt;
&lt;p&gt;The child immediately calls &lt;code&gt;execve(&quot;&#x2F;usr&#x2F;bin&#x2F;ls&quot;, argv, envp)&lt;&#x2F;code&gt; where:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;argv&lt;&#x2F;code&gt; is &lt;code&gt;[&quot;ls&quot;, &quot;-l&quot;]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;envp&lt;&#x2F;code&gt; contains your environment variables (HOME, PATH, USER, etc.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When &lt;code&gt;execve&lt;&#x2F;code&gt; is called, it instructs the kernel to:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Destroy&lt;&#x2F;strong&gt; the child&#x27;s current memory space.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Load&lt;&#x2F;strong&gt; the &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;ls&lt;&#x2F;code&gt; binary from disk into memory.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Jump&lt;&#x2F;strong&gt; to the entry point of the new program.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If you run &lt;code&gt;strace&lt;&#x2F;code&gt; to track system calls, you can see this transition clearly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;# strace -f -e trace=execve bash -c &amp;quot;ls -l&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;execve(&amp;quot;&#x2F;usr&#x2F;bin&#x2F;bash&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;&amp;quot;bash&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;-c&amp;quot;, &amp;quot;ls -l&amp;quot;], ...&lt;&#x2F;span&gt;&lt;span&gt;) = 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;pid 1234&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; execve(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;&amp;quot;&#x2F;usr&#x2F;bin&#x2F;ls&amp;quot;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;&amp;quot;ls&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;-l&amp;quot;], ...&lt;&#x2F;span&gt;&lt;span&gt;) = 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The child process retains the same process ID but runs completely different code. This fork-exec model is an elegant way to handle process creation in Unix-like systems.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-4-the-dynamic-linker&quot;&gt;Step 4: The Dynamic Linker&lt;&#x2F;h2&gt;
&lt;p&gt;Most modern programs are not statically compiled; they depend on shared libraries like &lt;code&gt;libc.so&lt;&#x2F;code&gt;. Before &lt;code&gt;ls&lt;&#x2F;code&gt; can run, the kernel loads the dynamic linker (usually &lt;code&gt;&#x2F;lib64&#x2F;ld-linux-x86-64.so.2&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The linker&#x27;s job is to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Parse the ELF binary to find its dependencies.&lt;&#x2F;li&gt;
&lt;li&gt;Load shared libraries like &lt;code&gt;libc.so&lt;&#x2F;code&gt; and &lt;code&gt;libselinux.so&lt;&#x2F;code&gt; into memory.&lt;&#x2F;li&gt;
&lt;li&gt;Resolve symbols, connecting &lt;code&gt;printf&lt;&#x2F;code&gt; calls in &lt;code&gt;ls&lt;&#x2F;code&gt; to the actual &lt;code&gt;printf&lt;&#x2F;code&gt; implementation in &lt;code&gt;libc&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Only after this setup is the &lt;code&gt;main()&lt;&#x2F;code&gt; function of &lt;code&gt;ls&lt;&#x2F;code&gt; called.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-5-reading-the-directory&quot;&gt;Step 5: Reading the Directory&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, &lt;code&gt;ls&lt;&#x2F;code&gt; performs its primary task. It cannot read the disk directly, as that is the kernel&#x27;s domain. Instead, it makes a series of system calls:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;openat()&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; opens the directory (typically &lt;code&gt;.&lt;&#x2F;code&gt;, your current working directory).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;getdents64()&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; reads directory entries from the kernel. This provides a list of filenames and their associated inodes (filesystem metadata).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;stat()&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; gets called on every file. Because you used &lt;code&gt;-l&lt;&#x2F;code&gt;, &lt;code&gt;ls&lt;&#x2F;code&gt; needs detailed information:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Permissions (&lt;code&gt;rwxr-xr-x&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Owner and group (UID&#x2F;GID)&lt;&#x2F;li&gt;
&lt;li&gt;File size&lt;&#x2F;li&gt;
&lt;li&gt;Modification timestamp&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can see this &quot;stat storm&quot; in &lt;code&gt;strace&lt;&#x2F;code&gt; output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;openat(AT_FDCWD, &amp;quot;.&amp;quot;, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;getdents64(3, &#x2F;* 10 entries *&#x2F;, 32768)  = 312&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;newfstatat(AT_FDCWD, &amp;quot;file1.txt&amp;quot;, {st_mode=S_IFREG|0644, st_size=1024, ...}, 0) = 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;newfstatat(AT_FDCWD, &amp;quot;file2.md&amp;quot;, {st_mode=S_IFREG|0644, st_size=2048, ...}, 0) = 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;step-6-writing-the-output&quot;&gt;Step 6: Writing the Output&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;ls&lt;&#x2F;code&gt; formats this information and calls &lt;code&gt;write()&lt;&#x2F;code&gt; on file descriptor 1 (stdout). The kernel takes those bytes and sends them to your terminal emulator for rendering.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;TTY Detection:&lt;&#x2F;strong&gt; &lt;code&gt;ls&lt;&#x2F;code&gt; uses the &lt;code&gt;isatty()&lt;&#x2F;code&gt; library function to see if its output is a terminal. If it is, it provides colors and columns. If the output is a pipe (like &lt;code&gt;ls | cat&lt;&#x2F;code&gt;), it strips colors and switches to one-file-per-line for better script compatibility.&lt;&#x2F;p&gt;
&lt;p&gt;When the child process exits with status 0 (success), the parent shell is woken up from its &lt;code&gt;wait()&lt;&#x2F;code&gt; call. The shell prints a new prompt, and the cycle repeats.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;&#x2F;h2&gt;
&lt;p&gt;This entire process, from keystroke to output, happens in milliseconds. Understanding it is important for several reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Debugging:&lt;&#x2F;strong&gt; When a command fails, knowing the sequence helps identify the cause. Is it a &lt;code&gt;$PATH&lt;&#x2F;code&gt; issue, a permission problem, or a missing shared library?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Performance:&lt;&#x2F;strong&gt; Every &lt;code&gt;stat()&lt;&#x2F;code&gt; call is expensive. This explains why &lt;code&gt;ls&lt;&#x2F;code&gt; on large directories can be slow. Furthermore, every system call involves a context switch from user-space to kernel-space. In a post-Spectre&#x2F;Meltdown environment (with KPTI enabled), these transitions are even more costly, making syscall minimization a key optimization.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Security:&lt;&#x2F;strong&gt; The fork-exec model creates clear boundaries. Child processes inherit file descriptors, environment variables, and security contexts. Understanding this is essential for writing secure software.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Next time someone asks you what happens when you type &lt;code&gt;ls&lt;&#x2F;code&gt;, you&#x27;ll have a more complete answer.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>De-Magicking AI: Writing a Neural Network in Pure C</title>
          <pubDate>Sun, 08 Feb 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/111-post/</link>
          <guid>https://gawindlin.com/blog/111-post/</guid>
          <description xml:base="https://gawindlin.com/blog/111-post/">&lt;p&gt;Everyone is talking about AI right now. But for most developers, &quot;doing AI&quot; just means importing a Python library and calling a function. It feels like magic.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t like magic. I like logic.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to understand &lt;em&gt;why&lt;&#x2F;em&gt; it works—not just theoretically, but at the instruction level. So, I decided to do something slightly masochistic: I built a fully functional, configurable neural network in pure C. No PyTorch, no TensorFlow, just me and &lt;code&gt;math.h&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-why-logic-vs-magic&quot;&gt;The &quot;Why&quot;: Logic vs. Magic&lt;&#x2F;h2&gt;
&lt;p&gt;The motivation was simple: pure curiosity mixed with a challenge. When you use a high-level library, you take the gradient descent and backpropagation for granted. You trust the &quot;black box.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;By forcing myself to implement it in C, I had to confront the reality of what a neural network actually is: a massive pile of linear algebra and calculus. There is no garbage collector to save you, and there is no &lt;code&gt;tensor.backward()&lt;&#x2F;code&gt; to do the math for you.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-architecture&quot;&gt;The Architecture&lt;&#x2F;h2&gt;
&lt;p&gt;I didn&#x27;t want to hardcode a simple XOR solver. I wanted a generic engine. My implementation supports:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dynamic Topology:&lt;&#x2F;strong&gt; Configurable input, hidden, and output sizes.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Activations:&lt;&#x2F;strong&gt; Sigmoid, ReLU, Leaky ReLU, Tanh, and Linear.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Loss Functions:&lt;&#x2F;strong&gt; MSE, Binary Cross-Entropy, and MAE.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Optimizers:&lt;&#x2F;strong&gt; Momentum and Learning Rate Decay.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Here is how the network looks in memory. Notice the heavy use of double pointers (&lt;code&gt;double**&lt;&#x2F;code&gt;)—this was necessary to create dynamic 2D arrays for the weights and gradients.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;typedef struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span&gt; input_size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span&gt; hidden_size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span&gt; output_size&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;    &#x2F;&#x2F; Weights and biases&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span&gt; w1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; b1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span&gt; w2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; b2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;    &#x2F;&#x2F; Activations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; hidden&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; output&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;    &#x2F;&#x2F; Gradients &amp;amp; Momentum&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span&gt; dw1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span&gt; dw2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    double&lt;&#x2F;span&gt;&lt;span&gt; momentum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt; NeuralNetwork&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;the-math-no-libraries-just-calculus&quot;&gt;The Math: No Libraries, Just Calculus&lt;&#x2F;h2&gt;
&lt;p&gt;Implementing this required translating vector calculus directly into C loops. The code isn&#x27;t just moving data; it&#x27;s physically calculating the Chain Rule step-by-step.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. The Forward Pass (Prediction)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;First, we compute the weighted sum of inputs plus a bias (the linear step), and then &quot;squash&quot; it using an activation function like Sigmoid to introduce non-linearity:&lt;&#x2F;p&gt;
&lt;p&gt;$$
Z = W \cdot X + b
$$&lt;&#x2F;p&gt;
&lt;p&gt;$$
A = \sigma(Z) = \frac{1}{1 + e^{-Z}}
$$&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2. The Backward Pass (Learning)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is where the magic (or pain) happens. We calculate how much each specific weight contributed to the total error using the Chain Rule:&lt;&#x2F;p&gt;
&lt;p&gt;$$
\frac{\partial L}{\partial W} = \frac{\partial L}{\partial A} \cdot \frac{\partial A}{\partial Z} \cdot \frac{\partial Z}{\partial W}
$$&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;3. The Update (Gradient Descent)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we nudge the weights in the opposite direction of the gradient to minimize error, scaled by a learning rate ($\eta$):&lt;&#x2F;p&gt;
&lt;p&gt;$$
W_{new} = W_{old} - \eta \cdot \nabla L
$$&lt;&#x2F;p&gt;
&lt;p&gt;In Python, this is one line of code. In C, that single update equation becomes a carefully managed nested &lt;code&gt;for&lt;&#x2F;code&gt; loop handling pointers to gradient arrays.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-friction-pointers-and-memory&quot;&gt;The Friction: Pointers and Memory&lt;&#x2F;h2&gt;
&lt;p&gt;The hardest part wasn&#x27;t the math itself; it was the memory management. In Python, you create a list and move on. In C, if you want a dataset, you have to &lt;code&gt;malloc&lt;&#x2F;code&gt; every single row.&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t plan your memory usage ahead of time, you end up with segmentation faults or memory leaks. You have to be intentional. Every time I calculate a gradient, I have to know exactly where that &lt;code&gt;double&lt;&#x2F;code&gt; is going to live.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-aha-moment&quot;&gt;The &quot;Aha!&quot; Moment&lt;&#x2F;h2&gt;
&lt;p&gt;The most satisfying part of this project wasn&#x27;t writing the code—it was running it. Because it&#x27;s C, it is blazing fast. I built a CLI interface that lets me tweak hyperparameters via flags (&lt;code&gt;-h&lt;&#x2F;code&gt; for hidden size, &lt;code&gt;-lr&lt;&#x2F;code&gt; for learning rate) and watch the training in real-time.&lt;&#x2F;p&gt;
&lt;p&gt;Here is what it looks like when the network learns the &lt;strong&gt;XOR&lt;&#x2F;strong&gt; function (a classic non-linear problem) in milliseconds:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; .&#x2F;nn -e&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 5000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -l&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -d xor -ha relu -oa sigmoid -loss bce -wd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.001&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -v&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;dataset&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; info:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;: xor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;  total&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; samples:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;  training&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; samples:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 80&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;  test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; samples:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 20&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;starting&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; training...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;epoch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; 0&#x2F;5000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; train&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; loss:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.697071&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt; test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; loss:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.713845&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;epoch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; 100: learning rate decayed to&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.099500&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; results:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;  training&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; loss:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.000590&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;  test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; loss:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.001088&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;  training&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; accuracy: 100.00%&lt;&#x2F;span&gt;&lt;span&gt; (80&#x2F;80)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;  test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; accuracy: 100.00%&lt;&#x2F;span&gt;&lt;span&gt; (20&#x2F;20)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;sample&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; predictions:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;input:&lt;&#x2F;span&gt;&lt;span&gt; [0.071,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; 0.926]&lt;&#x2F;span&gt;&lt;span&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; output:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.9940&lt;&#x2F;span&gt;&lt;span&gt; (expected:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 1.0000&lt;&#x2F;span&gt;&lt;span&gt;) -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; class: 1 (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;expected:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;) ✓&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;input:&lt;&#x2F;span&gt;&lt;span&gt; [-0.045,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; 0.077]&lt;&#x2F;span&gt;&lt;span&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; output:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.0000&lt;&#x2F;span&gt;&lt;span&gt; (expected:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.0000&lt;&#x2F;span&gt;&lt;span&gt;) -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; class: 0 (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;expected:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;) ✓&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Seeing the loss drop from &lt;code&gt;0.69&lt;&#x2F;code&gt; to &lt;code&gt;0.0005&lt;&#x2F;code&gt; in the terminal proves that the math I wrote by hand is actually working. The network &quot;learned&quot; that &lt;code&gt;[0, 1]&lt;&#x2F;code&gt; outputs &lt;code&gt;1&lt;&#x2F;code&gt; and &lt;code&gt;[0, 0]&lt;&#x2F;code&gt; outputs &lt;code&gt;0&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dealing-with-complexity-the-circle-problem&quot;&gt;Dealing with Complexity: The Circle Problem&lt;&#x2F;h2&gt;
&lt;p&gt;XOR is cool, but I wanted to see if my C engine could handle messy classification data. I generated a dataset of points inside and outside a circle (non-linearly separable) and added noise.&lt;&#x2F;p&gt;
&lt;p&gt;The network struggled initially, but after tuning the hidden layer size and switching to &lt;code&gt;ReLU&lt;&#x2F;code&gt;, it converged.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; .&#x2F;nn -d circle_enhanced -n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 2000&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -ha relu -oa sigmoid -loss bce&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;sample&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; predictions:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;input:&lt;&#x2F;span&gt;&lt;span&gt; [-0.281,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -0.996&lt;&#x2F;span&gt;&lt;span&gt;] -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; output: 0.4922 (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;expected:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.0000&lt;&#x2F;span&gt;&lt;span&gt;) -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; class: 0 (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;expected:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;) ✓&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;input:&lt;&#x2F;span&gt;&lt;span&gt; [-0.192,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; 0.158]&lt;&#x2F;span&gt;&lt;span&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; output:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0.4922&lt;&#x2F;span&gt;&lt;span&gt; (expected:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 1.0000&lt;&#x2F;span&gt;&lt;span&gt;) -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; class: 0 (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;expected:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;) ✗&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can see it&#x27;s not perfect (it misses edge cases), but that&#x27;s the reality of ML. It’s probabilistic, not deterministic.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Building this taught me that there is no magic in AI. It&#x27;s just:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Forward Pass:&lt;&#x2F;strong&gt; Dot products and activation functions.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Loss Calculation:&lt;&#x2F;strong&gt; How wrong were we?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Backward Pass:&lt;&#x2F;strong&gt; Using derivatives to nudge weights in the opposite direction of the error.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Check out the full source code on my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;neural_network&quot;&gt;Codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>C vs Node.js: I fought the Event Loop, and the Loop (almost) won</title>
          <pubDate>Tue, 03 Feb 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/110-post/</link>
          <guid>https://gawindlin.com/blog/110-post/</guid>
          <description xml:base="https://gawindlin.com/blog/110-post/">&lt;h2 id=&quot;the-hypothesis&quot;&gt;The Hypothesis&lt;&#x2F;h2&gt;
&lt;p&gt;I love C. It’s raw, it’s low-level, and it forces you to understand memory. Naturally, I assumed that a raw TCP server written in C would obliterate a server written in JavaScript (Node.js).&lt;&#x2F;p&gt;
&lt;p&gt;Node.js runs on V8 (a JIT engine) and carries the weight of a runtime. C runs directly on the metal. The winner seemed obvious.&lt;&#x2F;p&gt;
&lt;p&gt;I set up a benchmark between my &lt;strong&gt;Surface Pro 9&lt;&#x2F;strong&gt; (Client) and my &lt;strong&gt;Ubuntu Homelab&lt;&#x2F;strong&gt; (Server) over a Tailscale VPN.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;attempt-1-node-js-the-baseline&quot;&gt;Attempt 1: Node.js (The Baseline)&lt;&#x2F;h2&gt;
&lt;p&gt;I ran a standard &quot;Hello World&quot; in Node.js, which uses the &lt;strong&gt;Event Loop&lt;&#x2F;strong&gt; (Non-blocking I&#x2F;O) by default.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Requests&#x2F;Sec:&lt;&#x2F;strong&gt; ~15,860&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Errors:&lt;&#x2F;strong&gt; 0&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;The Reality Check:&lt;&#x2F;strong&gt; Node.js didn&#x27;t just win; it destroyed my C server. It handled concurrency effortlessly because it never waits for I&#x2F;O.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;attempt-2-the-naive-c-server&quot;&gt;Attempt 2: The Naive C Server&lt;&#x2F;h2&gt;
&lt;p&gt;My first attempt was a standard, blocking C server. It used &lt;code&gt;accept()&lt;&#x2F;code&gt;, &lt;code&gt;write()&lt;&#x2F;code&gt;, and &lt;code&gt;close()&lt;&#x2F;code&gt; in a simple &lt;code&gt;while&lt;&#x2F;code&gt; loop.&lt;&#x2F;p&gt;
&lt;p&gt;I hit it with &lt;code&gt;wrk&lt;&#x2F;code&gt; (12 threads, 400 connections).&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Requests&#x2F;Sec:&lt;&#x2F;strong&gt; ~6,500&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Errors:&lt;&#x2F;strong&gt; 160,000+ (Socket Read Errors)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;The Failure:&lt;&#x2F;strong&gt; It was a disaster. Because the server processed requests one by one (Blocking I&#x2F;O), the OS connection queue filled up instantly, and packets were dropped.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;attempt-3-the-redemption-single-threaded-epoll&quot;&gt;Attempt 3: The Redemption (Single-Threaded Epoll)&lt;&#x2F;h2&gt;
&lt;p&gt;I wasn&#x27;t going to let C lose. I rewrote the server using &lt;strong&gt;&lt;code&gt;epoll&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;, the Linux kernel&#x27;s high-performance notification mechanism. I also implemented &lt;strong&gt;HTTP Keep-Alive&lt;&#x2F;strong&gt; to stop closing sockets unnecessarily.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Requests&#x2F;Sec:&lt;&#x2F;strong&gt; ~13,580&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Errors:&lt;&#x2F;strong&gt; 0&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;The Frustration:&lt;&#x2F;strong&gt; I fixed the stability, but I was &lt;em&gt;still&lt;&#x2F;em&gt; slower than Node.js. Why? Because &lt;code&gt;libuv&lt;&#x2F;code&gt; (Node&#x27;s C core) and V8 are hyper-optimized. My single-threaded C code just couldn&#x27;t keep up with 15 years of Google engineering.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;attempt-4-the-nuclear-option-multi-threading&quot;&gt;Attempt 4: The Nuclear Option (Multi-Threading)&lt;&#x2F;h2&gt;
&lt;p&gt;Node.js is single-threaded. My server has 4 cores. It was time to cheat (or rather, use my hardware).&lt;&#x2F;p&gt;
&lt;p&gt;I used &lt;code&gt;pthread&lt;&#x2F;code&gt; to spin up 4 worker threads. But how do you share a port? I used &lt;code&gt;SO_REUSEPORT&lt;&#x2F;code&gt;, a Linux kernel flag that allows multiple sockets to bind to port 8080 simultaneously. The kernel handles the load balancing.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Requests&#x2F;Sec:&lt;&#x2F;strong&gt; &lt;strong&gt;~17,060&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Errors:&lt;&#x2F;strong&gt; 0&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;the-verdict&quot;&gt;The Verdict&lt;&#x2F;h2&gt;
&lt;p&gt;We finally did it. We beat JavaScript.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Implementation&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Req&#x2F;Sec&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Notes&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Node.js&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;15,859&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;The Gold Standard&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Blocking C&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;6,529&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;100% Fail rate&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Epoll C (1 Thread)&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;13,578&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Faster, but not fast enough&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Epoll C (4 Threads)&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;17,059&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Victory&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;I started this experiment to prove C was faster. Instead, I learned that &lt;strong&gt;Architecture &amp;gt; Language&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A badly written C program will always lose to a well-optimized Runtime. Even a &lt;em&gt;well-written&lt;&#x2F;em&gt; single-threaded C program might lose to the V8 engine. But if you dig deep enough into the OS using &lt;code&gt;epoll&lt;&#x2F;code&gt;, &lt;code&gt;SO_REUSEPORT&lt;&#x2F;code&gt;, and threads you can eventually reclaim the crown.&lt;&#x2F;p&gt;
&lt;p&gt;Check out the source code for the final server on my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;homelab&#x2F;src&#x2F;branch&#x2F;main&#x2F;node%20vs%20C&quot;&gt;Codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>The Invisible Navigator: Why I Revisit A* in Java</title>
          <pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/101-post/</link>
          <guid>https://gawindlin.com/blog/101-post/</guid>
          <description xml:base="https://gawindlin.com/blog/101-post/">&lt;p&gt;If you drove somewhere today, you likely trusted a blue line on your phone screen. You didn&#x27;t question it. You just turned left when it said turn left.&lt;&#x2F;p&gt;
&lt;p&gt;But how did it know? How did it sift through millions of road segments, traffic lights, and speed limits to find the &lt;em&gt;single&lt;&#x2F;em&gt; optimal route in milliseconds?&lt;&#x2F;p&gt;
&lt;p&gt;It wasn&#x27;t magic. It was a graph traversal algorithm. specifically, a heuristic search likely derived from &lt;strong&gt;A* (A-Star)&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We will be doing this in &lt;strong&gt;Java&lt;&#x2F;strong&gt;, which was my first programming language, and despite the hate it gets, its strict Object-Oriented nature makes it the perfect vessel for complex algorithms.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-logic-why-dijkstra-wasn-t-enough&quot;&gt;The Logic: Why Dijkstra Wasn&#x27;t Enough&lt;&#x2F;h2&gt;
&lt;p&gt;The classic algorithm for finding the shortest path is &lt;strong&gt;Dijkstra&#x27;s Algorithm&lt;&#x2F;strong&gt;. It works by exploring &lt;em&gt;every&lt;&#x2F;em&gt; possible direction equally, expanding outward like a ripple in a pond until it hits the target.&lt;&#x2F;p&gt;
&lt;p&gt;This is guaranteed to find the shortest path, but it&#x27;s wasteful. If you are in Zurich and want to go to Paris (West), there is no point in checking roads leading to Vienna (East).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Enter A* (A-Star).&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A* introduces a &quot;brain&quot; to the process. It doesn&#x27;t just look at the distance traveled ($g$); it also estimates the distance remaining ($h$, or the &lt;strong&gt;heuristic&lt;&#x2F;strong&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The core math is elegant in its simplicity:&lt;&#x2F;p&gt;
&lt;p&gt;$$f(n) = g(n) + h(n)$$&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$g(n)$&lt;&#x2F;strong&gt;: The actual cost to get from the start to node $n$ (traffic, distance).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;$h(n)$&lt;&#x2F;strong&gt;: The estimated &quot;crow flies&quot; distance from $n$ to the goal.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;By minimizing $f(n)$, the algorithm prioritizes paths that are both short &lt;em&gt;and&lt;&#x2F;em&gt; moving in the right direction.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-code-java-s-oop-power&quot;&gt;The Code: Java&#x27;s OOP Power&lt;&#x2F;h2&gt;
&lt;p&gt;I implemented this in Java because the &lt;code&gt;PriorityQueue&lt;&#x2F;code&gt; and &lt;code&gt;Class&lt;&#x2F;code&gt; structures handle the complexity cleanly. Here is the core &lt;code&gt;Node&lt;&#x2F;code&gt; class that implements &lt;code&gt;Comparable&lt;&#x2F;code&gt;—this is what allows Java to instantly sort the most promising paths to the top of the pile.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;static class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F9E2AF;font-style: italic;&quot;&gt; Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; implements&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F9E2AF;font-style: italic;&quot;&gt; Comparable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span&gt; g&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt; &#x2F;&#x2F; Cost from start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span&gt; h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt; &#x2F;&#x2F; Heuristic (guess) to end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    int&lt;&#x2F;span&gt;&lt;span&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt; &#x2F;&#x2F; Total score (g + h)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;    Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; g&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt;        this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt;        this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;g&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; g&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt;        this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt;        this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; g &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;    &#x2F;&#x2F; This is the magic: Java automatically sorts by &amp;#39;f&amp;#39; score&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt;    @Override&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    public int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; compareTo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; other&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; Integer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;compare&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; other&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And here is the search loop. Unlike the raw pointer arithmetic of my C projects, this reads almost like English.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;while&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;pq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;isEmpty&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;()) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    Node&lt;&#x2F;span&gt;&lt;span&gt; current&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;poll&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;();&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt; &#x2F;&#x2F; Get the most promising node&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;current&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span&gt; goalNode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; reconstructPath&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;parent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; goalNode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;    &#x2F;&#x2F; If we found a shorter path to a neighbor, record it&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;newG &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; newG&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        parent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; u&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        pq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; newG&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;the-visualization-watching-it-think&quot;&gt;The Visualization: Watching it Think&lt;&#x2F;h2&gt;
&lt;p&gt;The real beauty of A* emerges when you visualize its decision-making. Unlike Dijkstra&#x27;s uniform expansion, A* is drawn toward the goal like a magnet. Let&#x27;s trace through a simple grid.&lt;&#x2F;p&gt;
&lt;p&gt;Consider this 4x4 world. &lt;code&gt;S&lt;&#x2F;code&gt; is start, &lt;code&gt;G&lt;&#x2F;code&gt; is goal, &lt;code&gt;#&lt;&#x2F;code&gt; is a wall. The heuristic $h$ is the Manhattan distance (sum of horizontal and vertical steps remaining).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Initial State:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;S . . #&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. # . .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. . # .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# . . G&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Our Java &lt;code&gt;PriorityQueue&lt;&#x2F;code&gt; starts with the &lt;code&gt;S&lt;&#x2F;code&gt; node at (0,0). Let&#x27;s label each explored node with its &lt;code&gt;[f = g + h]&lt;&#x2F;code&gt; score.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Step 1:&lt;&#x2F;strong&gt; Expand &lt;code&gt;S&lt;&#x2F;code&gt; (0,0). Its neighbors are (0,1) and (1,0).&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;(0,1): &lt;code&gt;g=1&lt;&#x2F;code&gt; (one step from S), &lt;code&gt;h=4&lt;&#x2F;code&gt; (to G at (3,3)), so &lt;code&gt;f=5&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;(1,0): &lt;code&gt;g=1&lt;&#x2F;code&gt;, &lt;code&gt;h=5&lt;&#x2F;code&gt;, so &lt;code&gt;f=6&lt;&#x2F;code&gt;
PriorityQueue now: &lt;code&gt;[(0,1):5, (1,0):6]&lt;&#x2F;code&gt; ← Lowest &lt;code&gt;f&lt;&#x2F;code&gt; is first.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Step 2:&lt;&#x2F;strong&gt; Expand the most promising node, (0,1) &lt;code&gt;f=5&lt;&#x2F;code&gt;. Its open neighbor is (0,2).&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;(0,2): &lt;code&gt;g=2&lt;&#x2F;code&gt;, &lt;code&gt;h=3&lt;&#x2F;code&gt;, so &lt;code&gt;f=5&lt;&#x2F;code&gt;
Queue: &lt;code&gt;[(0,2):5, (1,0):6]&lt;&#x2F;code&gt; ← A tie! Our &lt;code&gt;compareTo&lt;&#x2F;code&gt; method will take (0,2), but both have equal priority.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Step 3:&lt;&#x2F;strong&gt; Expand (0,2) &lt;code&gt;f=5&lt;&#x2F;code&gt;. Its open neighbor is (1,2).&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;(1,2): &lt;code&gt;g=3&lt;&#x2F;code&gt;, &lt;code&gt;h=2&lt;&#x2F;code&gt;, so &lt;code&gt;f=5&lt;&#x2F;code&gt;
Queue: &lt;code&gt;[(1,2):5, (1,0):6]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Step 4:&lt;&#x2F;strong&gt; Expand (1,2) &lt;code&gt;f=5&lt;&#x2F;code&gt;. Critical choice! Neighbors: (1,3) is a wall, (2,2) is a wall, (0,2) is visited. Only (2,1) is open.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;(2,1): &lt;code&gt;g=4&lt;&#x2F;code&gt;, &lt;code&gt;h=2&lt;&#x2F;code&gt;, so &lt;code&gt;f=6&lt;&#x2F;code&gt;
Queue: &lt;code&gt;[(1,0):6, (2,1):6]&lt;&#x2F;code&gt; ← Notice the &quot;backtrack&quot;: (1,0) from Step 1 is now just as promising. The algorithm isn&#x27;t stubborn; it reconsiders.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Step 5:&lt;&#x2F;strong&gt; Expand (1,0) &lt;code&gt;f=6&lt;&#x2F;code&gt;. Its neighbor (2,0) opens a new frontier.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;(2,0): &lt;code&gt;g=2&lt;&#x2F;code&gt;, &lt;code&gt;h=3&lt;&#x2F;code&gt;, so &lt;code&gt;f=5&lt;&#x2F;code&gt; ← A better path is found!
Queue: &lt;code&gt;[(2,0):5, (2,1):6]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Step 6:&lt;&#x2F;strong&gt; Expand (2,0) &lt;code&gt;f=5&lt;&#x2F;code&gt;. This leads to (3,0) which is a wall, and (2,1) which is already queued. The algorithm proceeds, navigating around the obstacles until...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Final Path:&lt;&#x2F;strong&gt; The algorithm will find this optimal 6-step path, having explored far fewer nodes than Dijkstra would have:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;S 1 2 #&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1 # 3 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2 5 # 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# 6 7 G&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;a-note-on-greed-vs-structure&quot;&gt;A Note on &quot;Greed&quot; vs &quot;Structure&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;A* is often called a &quot;Best-First&quot; search, which can feel greedy, it always grabs the shiny, promising node next. But sometimes, being greedy isn&#x27;t the answer.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s important to mention algorithms like &lt;strong&gt;Prim&#x27;s&lt;&#x2F;strong&gt; or &lt;strong&gt;Kruskal&#x27;s&lt;&#x2F;strong&gt; (used for Minimum Spanning Trees). Unlike A*, which finds a single path for a single agent, MSTs are about infrastructure. They connect *all* nodes with the minimum total wire&#x2F;road length.&lt;&#x2F;p&gt;
&lt;p&gt;If A* is the GPS in your car, MST is the civil engineer who decided where to pave the roads in the first place.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-java&quot;&gt;Why Java?&lt;&#x2F;h2&gt;
&lt;p&gt;There is a reason Big Tech still runs on Java. When you are processing graph data with millions of nodes (think social networks or street maps), you need the strictness of strong typing and the reliability of the JVM.&lt;&#x2F;p&gt;
&lt;p&gt;Writing this in C would have required manually implementing a Priority Queue and managing heap memory for every node expansion. Writing it in Python would have been slow. Java hits that sweet spot: high-level abstractions with near-native performance.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>The Only Linux Course You Need: OverTheWire Bandit (0-10)</title>
          <pubDate>Wed, 28 Jan 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/100-post/</link>
          <guid>https://gawindlin.com/blog/100-post/</guid>
          <description xml:base="https://gawindlin.com/blog/100-post/">&lt;p&gt;If you ask a beginner how to start hacking, they usually ask &quot;which tool should I download?&quot; They want the &quot;Magic Button.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;But relying on automated tools without understanding the underlying system is like being a pilot who only knows how to use autopilot. If the system fails, or if the tool doesn&#x27;t work, you crash. You need to know how to fly manually.&lt;&#x2F;p&gt;
&lt;p&gt;That is why I recommend &lt;strong&gt;OverTheWire: Bandit&lt;&#x2F;strong&gt; to everyone. It isn&#x27;t just a security game; it is a trial-by-fire Linux administration course. It forces you to understand:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Filesystem Hierarchy:&lt;&#x2F;strong&gt; Why are things in &lt;code&gt;&#x2F;var&lt;&#x2F;code&gt; vs &lt;code&gt;&#x2F;tmp&lt;&#x2F;code&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Permissions:&lt;&#x2F;strong&gt; Who owns what, and why does it matter?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Piping:&lt;&#x2F;strong&gt; How to chain small tools to solve big problems.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Below is my walkthrough for Levels 0–10. I have blurred the flags so you can&#x27;t just copy-paste them. You have to run the commands yourself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;The Setup&lt;&#x2F;h2&gt;
&lt;p&gt;I keep it simple:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Terminal:&lt;&#x2F;strong&gt; On the right half of my screen.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Notes:&lt;&#x2F;strong&gt; A simple &lt;code&gt;.txt&lt;&#x2F;code&gt; file on the left where I paste the passwords as I find them.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Scripting:&lt;&#x2F;strong&gt; If &lt;code&gt;bash&lt;&#x2F;code&gt; gets too messy, I switch to Python. For most of the first 10 levels, the shell is king, but I&#x27;ll show you where Python becomes cleaner.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;levels-0-3-learning-to-walk&quot;&gt;Levels 0-3: Learning to Walk&lt;&#x2F;h2&gt;
&lt;p&gt;The first few levels are about muscle memory: &lt;code&gt;ls&lt;&#x2F;code&gt;, &lt;code&gt;cd&lt;&#x2F;code&gt;, and &lt;code&gt;cat&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-0-1&quot;&gt;Level 0 → 1&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; Log in via SSH.
We connect to &lt;code&gt;bandit.labs.overthewire.org&lt;&#x2F;code&gt; on port &lt;code&gt;2220&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;ssh&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; bandit0@bandit.labs.overthewire.org -p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 2220&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;# Password: bandit0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once in, we check the files.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;ls&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; readme&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;NH2SXQwcBdpmTEzi3bvBHMMtH66vVXjL&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-1-2-the-dash-problem&quot;&gt;Level 1 → 2 (The &quot;Dash&quot; Problem)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; Read a file named &lt;code&gt;-&lt;&#x2F;code&gt;.
If you try &lt;code&gt;cat -&lt;&#x2F;code&gt;, the program thinks you are trying to read from &lt;code&gt;stdin&lt;&#x2F;code&gt; (standard input). You have to explicitly tell the shell &quot;this is a file path&quot; using &lt;code&gt;.&#x2F;&lt;&#x2F;code&gt; (which means &quot;current directory&quot;).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; .&#x2F;-&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;rRGizSaX8Mk1RTb1CNQoXTcYZWU6lgzi&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-2-3-spaces-in-filenames&quot;&gt;Level 2 → 3 (Spaces in Filenames)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; Read a file named &lt;code&gt;spaces in this filename&lt;&#x2F;code&gt;.
Spaces separate arguments in Bash. To tell Bash this is one single argument, we use quotes or escape characters.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;spaces in this filename&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;# OR&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; spaces&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F5C2E7;&quot;&gt;\ &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F5C2E7;&quot;&gt;\ &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F5C2E7;&quot;&gt;\ &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;filename&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;aBZ0W5EmUfAf7k76VKKJ7px84k5ePPRa&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;levels-4-6-the-power-of-find&quot;&gt;Levels 4-6: The Power of &lt;code&gt;find&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now we stop looking at files and start &lt;em&gt;searching&lt;&#x2F;em&gt; for them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-4-5&quot;&gt;Level 4 → 5&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; Find the only human-readable file in the &lt;code&gt;inhere&lt;&#x2F;code&gt; directory.
There are multiple files named &lt;code&gt;-file00&lt;&#x2F;code&gt;, &lt;code&gt;-file01&lt;&#x2F;code&gt;, etc. Most are binary garbage. We can use the &lt;code&gt;file&lt;&#x2F;code&gt; command to see the data type of every file at once.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; .&#x2F;inhere&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Look for the one that says &lt;code&gt;ASCII text&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;4oQYVPkxZOOEOO5pTW81FB8j8lxXGUQw&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-5-6&quot;&gt;Level 5 → 6&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; Find a file that is &lt;strong&gt;1033 bytes&lt;&#x2F;strong&gt;, &lt;strong&gt;not executable&lt;&#x2F;strong&gt;, and &lt;strong&gt;owned by group bandit6&lt;&#x2F;strong&gt;.
This is where &lt;code&gt;find&lt;&#x2F;code&gt; shines. We can filter by properties, not just names.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; . -type f -size 1033c ! -executable -group bandit6&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;(Note: &lt;code&gt;1033c&lt;&#x2F;code&gt; means bytes. &lt;code&gt;1033k&lt;&#x2F;code&gt; would be kilobytes!)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;HWasnPhtq9AVKe0dmk45nxy20cvUa6Cu&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-6-7-the-filter&quot;&gt;Level 6 → 7 (The Filter)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; The file is somewhere on the server (not just in the current folder), owned by &lt;strong&gt;user bandit7&lt;&#x2F;strong&gt;, &lt;strong&gt;group bandit6&lt;&#x2F;strong&gt;, and &lt;strong&gt;33 bytes&lt;&#x2F;strong&gt; large.&lt;&#x2F;p&gt;
&lt;p&gt;This is the first real hurdle for many. You have to search the &lt;em&gt;entire&lt;&#x2F;em&gt; drive (&lt;code&gt;&#x2F;&lt;&#x2F;code&gt;), which means you are going to get thousands of &quot;Permission Denied&quot; errors from looking in folders you don&#x27;t have access to.&lt;&#x2F;p&gt;
&lt;p&gt;If you just run the command, the flag will be buried in error messages. We need to redirect those errors (&lt;code&gt;stderr&lt;&#x2F;code&gt;, file descriptor 2) into the void (&lt;code&gt;&#x2F;dev&#x2F;null&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &#x2F; -user bandit7 -group bandit6 -size 33c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; 2&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;&#x2F;dev&#x2F;null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This command is a synthesis of three concepts:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Scope:&lt;&#x2F;strong&gt; Searching from root &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Filtering:&lt;&#x2F;strong&gt; Combining user, group, and size flags.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Stream Redirection:&lt;&#x2F;strong&gt; Cleaning the output so only the success is visible.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;morbNTDkSW6jIlUc0ymOdMaLnOlFVAaj&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;levels-7-10-text-wrangling&quot;&gt;Levels 7-10: Text Wrangling&lt;&#x2F;h2&gt;
&lt;p&gt;We found the files; now we need to process the text &lt;em&gt;inside&lt;&#x2F;em&gt; them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-7-8&quot;&gt;Level 7 → 8&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; The password is next to the word &quot;millionth&quot;.
We use &lt;code&gt;grep&lt;&#x2F;code&gt; (Global Regular Expression Print).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;grep&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;millionth&amp;quot; data.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;dfwvzFQi4mU0wfNbFOe9RoWskMLg7eEc&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-8-9&quot;&gt;Level 8 → 9&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; The password is the only line of text that occurs only once.
To find a unique line, we first need to sort them. Why? Because &lt;code&gt;uniq&lt;&#x2F;code&gt; only compares adjacent lines. It can&#x27;t detect duplicates that are separated. Sorting brings all identical lines together.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; data.txt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; uniq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -u&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Python Alternative:&lt;&#x2F;strong&gt; If you want to see where Python becomes cleaner, here&#x27;s how you could solve this with a one-liner:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;python3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;from collections import Counter; print([k for k,v in Counter(open(&amp;#39;data.txt&amp;#39;).readlines()).items() if v==1][0].strip())&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This uses Python&#x27;s &lt;code&gt;Counter&lt;&#x2F;code&gt; to count occurrences without needing to sort first. For simple shell tasks, &lt;code&gt;sort | uniq&lt;&#x2F;code&gt; is faster to type, but when dealing with more complex data transformations, Python&#x27;s data structures give you more power.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;4CKMh1JI91bUIZZPXDqGanal4xvAg0JM&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-9-10&quot;&gt;Level 9 → 10&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; One of the few human-readable strings in a binary file, preceded by several &lt;code&gt;=&lt;&#x2F;code&gt; characters.
If you &lt;code&gt;cat&lt;&#x2F;code&gt; a binary file, you might crash your terminal. Use &lt;code&gt;strings&lt;&#x2F;code&gt; to pull out the text, then pipe it to grep.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;strings&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; data.txt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; grep&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;===&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;FGUW5il8JNE7q19ksvT6E3RhhE5gQnwj&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-10-11&quot;&gt;Level 10 → 11&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;&#x2F;strong&gt; The file contains base64 encoded data.
Base64 is not encryption; it&#x27;s encoding. It&#x27;s easily reversible.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;base64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -d data.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Flag:&lt;&#x2F;strong&gt; &lt;span class=&quot;spoiler&quot;&gt;dtR173fZKb0RRsDFSGsg2RWnpNVj3qRr&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;At this point, you aren&#x27;t &quot;hacking&quot; yet. You&#x27;re just administrating. But these skills are the foundation. If you can&#x27;t filter a text stream or find a config file by its permissions, you can&#x27;t escalate privileges.&lt;&#x2F;p&gt;
&lt;p&gt;Next time, we&#x27;ll dive into &lt;strong&gt;Level 11-20&lt;&#x2F;strong&gt;, where we start dealing with networking, compression, and SSH keys.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>Smart Cracking: Teaching Python to &#x27;Guess&#x27; Like a Human</title>
          <pubDate>Sat, 24 Jan 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/11-post/</link>
          <guid>https://gawindlin.com/blog/11-post/</guid>
          <description xml:base="https://gawindlin.com/blog/11-post/">&lt;h2 id=&quot;the-problem-with-brute-force&quot;&gt;The Problem with Brute Force&lt;&#x2F;h2&gt;
&lt;p&gt;If you try to crack a password by guessing &lt;code&gt;aaaaa&lt;&#x2F;code&gt;, then &lt;code&gt;aaaab&lt;&#x2F;code&gt;, you are fighting against entropy. This is inefficient because humans don&#x27;t type random characters; we follow predictable patterns like &lt;code&gt;Password123&lt;&#x2F;code&gt;, &lt;code&gt;Welcome2026&lt;&#x2F;code&gt;, or &lt;code&gt;ilovecoffee&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If I know you typed &lt;code&gt;P-a-s-s&lt;&#x2F;code&gt;, there is a statistical near-certainty that the next letter is &lt;code&gt;w&lt;&#x2F;code&gt;. A &quot;dumb&quot; brute-forcer would try &lt;code&gt;P-a-s-s-a&lt;&#x2F;code&gt;, &lt;code&gt;P-a-s-s-b&lt;&#x2F;code&gt;, and waste thousands of cycles before hitting the correct character. I wanted to build a tool that &quot;understands&quot; these patterns, so I implemented a Markov Chain engine in my security toolkit, &lt;strong&gt;Sec-Suite&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-theory-markov-chains&quot;&gt;The Theory: Markov Chains&lt;&#x2F;h2&gt;
&lt;p&gt;At its core, a Markov Chain is a stochastic model where the probability of each event depends only on the state attained in the previous event. For password cracking, we look at a sequence of characters to predict the most likely successor.&lt;&#x2F;p&gt;
&lt;p&gt;Mathematically, the probability of the next character occurring is based on the previous characters:&lt;&#x2F;p&gt;
&lt;p&gt;$$P(X_{n} = x | X_{n-3}, X_{n-2}, X_{n-1})$$&lt;&#x2F;p&gt;
&lt;p&gt;In my implementation, I defined a &quot;state&quot; as a sequence of 3 characters, known as an &lt;strong&gt;Order-3&lt;&#x2F;strong&gt; chain. This effectively creates a &lt;strong&gt;4-gram&lt;&#x2F;strong&gt; model: it looks at a 3-character &quot;window&quot; to guess the 4th.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-logic-a-sliding-window-of-probability&quot;&gt;The Logic: A &quot;Sliding Window&quot; of Probability&lt;&#x2F;h2&gt;
&lt;p&gt;The engine works in two distinct phases: &lt;strong&gt;Training&lt;&#x2F;strong&gt; and &lt;strong&gt;Generation&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-training-building-the-brain&quot;&gt;1. Training (Building the Brain)&lt;&#x2F;h3&gt;
&lt;p&gt;I feed the model a wordlist, such as &lt;code&gt;rockyou.txt&lt;&#x2F;code&gt;. It slides a window of size 3 over every password, recording what character comes next.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; train&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; passwords&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; List&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;font-style: italic;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; passwords&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;        for&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;font-style: italic;&quot;&gt; range&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;font-style: italic;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;order&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;            # The &amp;quot;State&amp;quot; (e.g., &amp;quot;Pas&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;            # The &amp;quot;Next&amp;quot; (e.g., &amp;quot;s&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            next_char&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; not in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;                self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;            # Record that &amp;quot;s&amp;quot; can follow &amp;quot;Pas&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;            self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;next_char&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;2-generation-the-statistical-guess&quot;&gt;2. Generation (The Statistical Guess)&lt;&#x2F;h3&gt;
&lt;p&gt;The beauty of this approach is that I don&#x27;t need complex math libraries to calculate weights. By storing every occurrence in a list, &lt;code&gt;random.choice()&lt;&#x2F;code&gt; handles the probability distribution automatically.&lt;&#x2F;p&gt;
&lt;p&gt;If the model sees &lt;code&gt;Password&lt;&#x2F;code&gt; five times and &lt;code&gt;Passcode&lt;&#x2F;code&gt; twice, the list for the state &lt;code&gt;ass&lt;&#x2F;code&gt; looks like this: &lt;code&gt;[&#x27;w&#x27;, &#x27;w&#x27;, &#x27;w&#x27;, &#x27;w&#x27;, &#x27;w&#x27;, &#x27;c&#x27;, &#x27;c&#x27;]&lt;&#x2F;code&gt;. By using a list rather than a dictionary of percentages, we avoid manual math—&lt;code&gt;random.choice()&lt;&#x2F;code&gt; simply has a &lt;strong&gt;71.4%&lt;&#x2F;strong&gt; chance of picking &lt;code&gt;w&lt;&#x2F;code&gt; based on its frequency.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; generate_password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; max_length&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;font-style: italic;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 20&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;) -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;font-style: italic;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    while&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;font-style: italic;&quot;&gt; len&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; max_length&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; :]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;        # Frequency in the list = Probability of selection&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        next_char&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; random&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;choice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;font-style: italic;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span&gt; next_char&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;the-bottleneck-python-s-gil&quot;&gt;The Bottleneck: Python&#x27;s GIL&lt;&#x2F;h2&gt;
&lt;p&gt;I initially included a multi-threaded cracker to generate and test passwords in parallel. However, I hit a wall called the &lt;strong&gt;Global Interpreter Lock (GIL)&lt;&#x2F;strong&gt;. In Python, the GIL prevents multiple native threads from executing bytecodes at once For CPU-bound tasks like password hashing, threading is essentially an &quot;illusion of parallelism&quot;—my script was barely faster than a single-threaded version.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-path-forward-multiprocessing-vs-c&quot;&gt;The Path Forward: Multiprocessing vs. C&lt;&#x2F;h3&gt;
&lt;p&gt;To truly scale, I have two options:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;multiprocessing&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: Use Python’s &lt;code&gt;multiprocessing&lt;&#x2F;code&gt; module to side-step the GIL by spawning separate memory spaces for each CPU core.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The C Switch&lt;&#x2F;strong&gt;: Port the hashing and generation logic to C&#x2F;C++.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;While &lt;code&gt;multiprocessing&lt;&#x2F;code&gt; is a great intermediate step, &lt;strong&gt;Python is for logic; C is for speed&lt;&#x2F;strong&gt;. To build a production-grade cracker, the engine needs to live closer to the bare metal.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;why-serialize-pkl&quot;&gt;Why Serialize? (&lt;code&gt;.pkl&lt;&#x2F;code&gt;)&lt;&#x2F;h2&gt;
&lt;p&gt;Training on &lt;code&gt;rockyou.txt&lt;&#x2F;code&gt; (14 million passwords) takes significant time. I used Python&#x27;s &lt;code&gt;pickle&lt;&#x2F;code&gt; module to dump the memory state into a &lt;code&gt;.pkl&lt;&#x2F;code&gt; file so the &quot;brain&quot; loads instantly on startup.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;h3 id=&quot;warning-a-note-on-security&quot;&gt;⚠️ A Note on Security&lt;&#x2F;h3&gt;
&lt;p&gt;While &lt;code&gt;pickle&lt;&#x2F;code&gt; is convenient, it is &lt;strong&gt;insecure&lt;&#x2F;strong&gt; against erroneous or malicious data. Unpickling a crafted file can lead to &lt;strong&gt;Remote Code Execution (RCE)&lt;&#x2F;strong&gt;. For a public security tool, a safer alternative like JSON or a custom binary format is preferred.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Building this taught me that &quot;hacking&quot; is often just applied statistics. By analyzing the &lt;em&gt;structure&lt;&#x2F;em&gt; of passwords rather than just guessing blindly, we can reduce the search space by orders of magnitude. The next version of this engine will likely be written in C, but Python proved to be the perfect laboratory for proving the concept.&lt;&#x2F;p&gt;
&lt;p&gt;You can view the full source code on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;sec-suite&quot;&gt;Codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>Demystifying Non-Blocking I&#x2F;O in C</title>
          <pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/10-post/</link>
          <guid>https://gawindlin.com/blog/10-post/</guid>
          <description xml:base="https://gawindlin.com/blog/10-post/">&lt;h2 id=&quot;the-problem-with-blocking&quot;&gt;The Problem with Blocking&lt;&#x2F;h2&gt;
&lt;p&gt;When I decided to write a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;port-scanner&quot;&gt;TCP Port Scanner&lt;&#x2F;a&gt; in C, I initially looked at the standard &lt;code&gt;connect()&lt;&#x2F;code&gt; system call. The problem with the default behavior (blocking mode) is that if you try to connect to a firewall that simply drops packets (a &quot;filtered&quot; port), your program hangs. It waits for the OS timeout, which can be anywhere from 30 seconds to several minutes.&lt;&#x2F;p&gt;
&lt;p&gt;For a scanner checking 1,000 ports, that is unacceptable. I needed a way to control the timeout precisely, say, 1 second per port—and keep the application responsive to &lt;code&gt;Ctrl+C&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution-select&quot;&gt;The Solution: &lt;code&gt;select()&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The solution is to set the socket to &lt;strong&gt;non-blocking mode&lt;&#x2F;strong&gt; using &lt;code&gt;fcntl&lt;&#x2F;code&gt;. When you call &lt;code&gt;connect()&lt;&#x2F;code&gt; on a non-blocking socket, it returns immediately. Usually, it returns &lt;code&gt;-1&lt;&#x2F;code&gt; with the error &lt;code&gt;EINPROGRESS&lt;&#x2F;code&gt;. This isn&#x27;t a failure; it just means the TCP handshake is happening in the background.&lt;&#x2F;p&gt;
&lt;p&gt;To wait for that handshake to finish (or fail) with a custom timeout, I used &lt;code&gt;select()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-critical-trap-so-error&quot;&gt;The Critical Trap: &lt;code&gt;SO_ERROR&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Here is where many implementations fail. When &lt;code&gt;select()&lt;&#x2F;code&gt; tells you the socket is &quot;writable,&quot; that doesn&#x27;t necessarily mean the connection succeeded. It just means the handshake is &lt;em&gt;done&lt;&#x2F;em&gt;. It could have been a success (ACK) or a failure (RST).&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t check the actual socket error, you will report closed or filtered ports as &quot;Open.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Here is the core logic from my implementation:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;&#x2F;&#x2F; Inside scan_port()...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;&#x2F;&#x2F; Initiate non-blocking connect&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; res &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; connect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;sock&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;, (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; sockaddr &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;server_addr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; sizeof&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;server_addr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;res &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt; errno &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;!=&lt;&#x2F;span&gt;&lt;span&gt; EINPROGRESS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;    close&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;sock&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;&#x2F;&#x2F; Use select() to wait for writeability with a timeout&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fd_set writefds&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;FD_ZERO&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;writefds&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;FD_SET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;sock&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;writefds&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; timeval timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;timeout.tv_sec &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; timeout_sec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;timeout.tv_usec &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; select_result &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; select&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;sock &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt; NULL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;writefds&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F38BA8;&quot;&gt; NULL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;select_result &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt; &#x2F;&#x2F; Timeout&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;select_result &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt; &#x2F;&#x2F; Error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;&#x2F;&#x2F; THE CRITICAL STEP: Check SO_ERROR&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt; so_error &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F9E2AF;font-style: italic;&quot;&gt;socklen_t&lt;&#x2F;span&gt;&lt;span&gt; len &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;= sizeof&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;so_error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;&#x2F;&#x2F; If we skip this, we get false positives!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;getsockopt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;sock&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; SOL_SOCKET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; SO_ERROR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;so_error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;    close&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;sock&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;&#x2F;&#x2F; If so_error is 0, the port is truly open.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;so_error &lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; : -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;why-this-matters&quot;&gt;Why this matters:&lt;&#x2F;h3&gt;
&lt;p&gt;Writing this scanner wasn&#x27;t just about finding ports. It was more about understanding the lifecycle of a TCP connection. By forcing myself to handle &lt;code&gt;EINPROGRESS&lt;&#x2F;code&gt; ad &lt;code&gt;SO_ERROR&lt;&#x2F;code&gt; manually, I gained a much clearer picture of how the OS handles networking than i ever would have using a Python lib.&lt;&#x2F;p&gt;
&lt;p&gt;You can view the full source code on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;port-scanner&quot;&gt;Codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>My Homelab is a Surface Pro 9</title>
          <pubDate>Sat, 17 Jan 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/1-post/</link>
          <guid>https://gawindlin.com/blog/1-post/</guid>
          <description xml:base="https://gawindlin.com/blog/1-post/">&lt;h2 id=&quot;the-hardware-constraint&quot;&gt;The Hardware Constraint&lt;&#x2F;h2&gt;
&lt;p&gt;Most homelabs start with a Raspberry Pi cluster or an old desktop tower tucked away in a closet. Mine is a &lt;strong&gt;Microsoft Surface Pro 9&lt;&#x2F;strong&gt; (i5-1235U, 8GB RAM).&lt;&#x2F;p&gt;
&lt;p&gt;It served me well as a tablet during high school, but when I moved to ETH, it became my first Linux machine. Now, it runs my entire digital life. Because I only have 8GB of RAM, I can&#x27;t afford to be wasteful. I can&#x27;t just spin up a separate database container for every single service. I needed a &quot;converged&quot; architecture.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-architecture&quot;&gt;The Architecture&lt;&#x2F;h2&gt;
&lt;p&gt;I use &lt;strong&gt;Docker Compose&lt;&#x2F;strong&gt; to orchestrate everything. The core philosophy is &lt;strong&gt;Shared Resources&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reverse Proxy:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;caddyserver.com&#x2F;&quot;&gt;Caddy&lt;&#x2F;a&gt; handles SSL and ingress automation.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Database:&lt;&#x2F;strong&gt; A single PostgreSQL 16 instance acts as the central brain, serving Vikunja, OwnCloud, and Linkwarden simultaneously.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Cache:&lt;&#x2F;strong&gt; A single Redis instance shared across services.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Security:&lt;&#x2F;strong&gt; CrowdSec reading Caddy logs in real-time.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;the-performance-stats&quot;&gt;The Performance Stats&lt;&#x2F;h2&gt;
&lt;p&gt;The proof is in the numbers. By sharing the database and cache, the efficiency is incredibly high.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;btop.jpg&quot; alt=&quot;btop screenshot of my surface running linux&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here is a live snapshot of the memory usage:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt; ❯ free -h&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;              total&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;        used        free      shared  buff&#x2F;cache   available&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;Mem:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;          7.6Gi       3.3Gi       335Mi        61Mi       4.4Gi       4.3Gi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I am running 10 active containers and using only &lt;strong&gt;3.3Gi of RAM&lt;&#x2F;strong&gt;, leaving more than half the system resources free for other tasks.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the breakdown by container:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Service&lt;&#x2F;th&gt;&lt;th&gt;Memory Usage&lt;&#x2F;th&gt;&lt;th&gt;Technology&lt;&#x2F;th&gt;&lt;th&gt;Note&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Linkwarden&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;~1.54 GiB&lt;&#x2F;td&gt;&lt;td&gt;Node.js&lt;&#x2F;td&gt;&lt;td&gt;The heaviest container by far (Next.js app).&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Uptime Kuma&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;130 MiB&lt;&#x2F;td&gt;&lt;td&gt;Node.js&lt;&#x2F;td&gt;&lt;td&gt;Monitoring dashboard.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;AdGuard Home&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;115 MiB&lt;&#x2F;td&gt;&lt;td&gt;Go&lt;&#x2F;td&gt;&lt;td&gt;DNS filtering.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;OwnCloud&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;78 MiB&lt;&#x2F;td&gt;&lt;td&gt;PHP&#x2F;C++&lt;&#x2F;td&gt;&lt;td&gt;File server.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Postgres&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;77 MiB&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;C&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;Hosting DBs for 3 different apps.&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Wallos&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;49 MiB&lt;&#x2F;td&gt;&lt;td&gt;PHP&lt;&#x2F;td&gt;&lt;td&gt;Finance tracking.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CrowdSec&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;30 MiB&lt;&#x2F;td&gt;&lt;td&gt;Go&lt;&#x2F;td&gt;&lt;td&gt;Intrusion prevention.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Caddy&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;25 MiB&lt;&#x2F;td&gt;&lt;td&gt;Go&lt;&#x2F;td&gt;&lt;td&gt;Reverse Proxy.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Vikunja&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;20 MiB&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Go&lt;&#x2F;td&gt;&lt;td&gt;To-Do list (backend).&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Redis&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;3 MiB&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;C&lt;&#x2F;td&gt;&lt;td&gt;Shared Cache.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;em&gt;Note how lightweight the Go and C-based services are compared to the Node.js ones. A single Postgres instance serving multiple apps for just 77MB is a huge win.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-split-horizon-dns&quot;&gt;The &quot;Split-Horizon&quot; DNS&lt;&#x2F;h2&gt;
&lt;p&gt;One of my main requirements was accessing my services (like &lt;code&gt;tasks.lab.gawindlin.com&lt;&#x2F;code&gt;) seamlessly, whether I am at home or at university.&lt;&#x2F;p&gt;
&lt;p&gt;I achieved this using &lt;strong&gt;Split-Horizon DNS&lt;&#x2F;strong&gt; and &lt;strong&gt;Tailscale&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Publicly:&lt;&#x2F;strong&gt; The domains point to my Tailscale IP (or are blocked entirely if not on VPN).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Internally:&lt;&#x2F;strong&gt; I run &lt;strong&gt;AdGuard Home&lt;&#x2F;strong&gt; on port 53. It rewrites DNS requests for &lt;code&gt;*.lab.gawindlin.com&lt;&#x2F;code&gt; to the local LAN IP of the Surface Pro.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This setup ensures that I never have to toggle Wi-Fi or change URLs. It just works.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;security-caddy-crowdsec&quot;&gt;Security: Caddy + CrowdSec&lt;&#x2F;h2&gt;
&lt;p&gt;Exposing services is risky, so I automated intrusion prevention. I configured Caddy to output JSON logs, which CrowdSec watches in real-time. If an IP scans too many non-existent URLs (404s) or tries to brute-force a password, CrowdSec bans them at the container level.&lt;&#x2F;p&gt;
&lt;p&gt;Here is how the integration looks in my &lt;code&gt;docker-compose.yml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;  # Caddy: The Gateway&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;  caddy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    image&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; caddy:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    container_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; caddy&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    restart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; unless-stopped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    ports&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;80:80&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;443:443&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    environment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; CADDY_INGRESS_NETWORKS=lab_net&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    networks&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; lab_net&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    volumes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; .&#x2F;Caddyfile:&#x2F;etc&#x2F;caddy&#x2F;Caddyfile&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; caddy_logs:&#x2F;var&#x2F;log&#x2F;caddy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt; # Shared volume for logs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;  # CrowdSec: The Bouncer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;  crowdsec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    image&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; crowdsecurity&#x2F;crowdsec:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    container_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; crowdsec&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    restart&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; unless-stopped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    environment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;      Collections&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;crowdsecurity&#x2F;caddy&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    volumes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &#x2F;var&#x2F;run&#x2F;docker.sock:&#x2F;var&#x2F;run&#x2F;docker.sock:ro&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; caddy_logs:&#x2F;var&#x2F;log&#x2F;caddy:ro&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt; # Read-only access to logs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; .&#x2F;crowdsec&#x2F;acquis.yaml:&#x2F;etc&#x2F;crowdsec&#x2F;acquis.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    networks&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; lab_net&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;volumes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;  caddy_logs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Running a homelab doesn&#x27;t require a rack server. With a bit of optimization—sharing your database, using lightweight containers like Caddy, and carefully monitoring resources—you can run a professional-grade infrastructure on a tablet. I will document more about this project here and on my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;homelab&#x2F;&quot;&gt;Codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
      <item>
          <title>Hello World: The Terminal is My Home</title>
          <pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://gawindlin.com/blog/0-post/</link>
          <guid>https://gawindlin.com/blog/0-post/</guid>
          <description xml:base="https://gawindlin.com/blog/0-post/">&lt;h2 id=&quot;hello-world&quot;&gt;Hello World&lt;&#x2F;h2&gt;
&lt;p&gt;Welcome to &lt;code&gt;gawindlin.com&lt;&#x2F;code&gt;. If you are reading this, you’ve found my corner of the internet.&lt;&#x2F;p&gt;
&lt;p&gt;I built this site to serve as a digital archive, a place to document my work, my studies at ETH Zürich, and the projects that keep me up at night. While the site itself is built with Astro and styled to also function like my beloved Neovim, the content you&#x27;ll find here going forward will likely dig much deeper than HTML and CSS.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-aha-moment&quot;&gt;The &quot;Aha&quot; Moment&lt;&#x2F;h2&gt;
&lt;p&gt;For a long time, I built things for the web. I have projects like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;screen-savor&quot;&gt;Screen Savor&lt;&#x2F;a&gt; in my history, and they taught me a lot. But recently, I realized my interests have shifted.&lt;&#x2F;p&gt;
&lt;p&gt;There was a specific day recently that defined this shift for me. I spent the morning reinstalling Arch Linux and configuring Hyprland (because, of course, kernel panic). I spent the afternoon debugging my Homelab infrastructure because an ad-blocker config took down the internet for the whole house. I ended the day grinding through Data Structures and Algorithms assignments for university.&lt;&#x2F;p&gt;
&lt;p&gt;Exhausted but satisfied, I realized: &lt;strong&gt;This is it.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not just a developer; I&#x27;m a systems enthusiast. I love the grit of low-level programming, the &#x27;logic&#x27; of C, and the endless rabbit holes of Linux administration.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-to-expect-here&quot;&gt;What to Expect Here&lt;&#x2F;h2&gt;
&lt;p&gt;This blog won&#x27;t just be about polished finish lines; it will be about the process. I plan to write about:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure &amp;amp; Homelab:&lt;&#x2F;strong&gt; Documenting my setups, like the Kubernetes cluster I help manage for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vis.ethz.ch&quot;&gt;VIS ETHZ&lt;&#x2F;a&gt; or my personal microservices architecture.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Low-Level Learning:&lt;&#x2F;strong&gt; Deep dives into C, POSIX sockets, and things I learned building tools like my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&#x2F;port-scanner&quot;&gt;TCP Port Scanner&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Cybersecurity:&lt;&#x2F;strong&gt; Write-ups on CTFs (Capture The Flags), security tools I&#x27;m building (like &lt;code&gt;sec-suite&lt;&#x2F;code&gt;), and vulnerabilities I&#x27;m studying.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Uni Life:&lt;&#x2F;strong&gt; The occasional proof from my studies at ETH that was too interesting (or painful) not to share.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;let-s-connect&quot;&gt;Let&#x27;s Connect&lt;&#x2F;h2&gt;
&lt;p&gt;Whether you are a recruiter, a fellow student, or just someone who stumbled here via Codeberg: welcome.&lt;&#x2F;p&gt;
&lt;p&gt;Feel free to check out my code on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;0xShred&quot;&gt;Codeberg&lt;&#x2F;a&gt; or browse my &lt;a href=&quot;&#x2F;cv.pdf&quot;&gt;CV&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
</description>
      </item>
    </channel>
</rss>
