{"title":"Bruno Croci","author":{"name":"Bruno Croci","uri":"https:\/\/bruno.croci.me\/"},"link":{"@attributes":{"href":"https:\/\/crocidb.com\/\/index.xml"}},"id":"https:\/\/crocidb.com\/","updated":"2026-03-31T22:11:05Z","entry":[{"id":"https:\/\/crocidb.com\/post\/llm-is-the-horse\/","title":"LLM is the Horse","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/llm-is-the-horse\/"}},"updated":"2026-03-31T00:00:00Z","summary":"<p>Recently someone posted on <a href=\"https:\/\/news.ycombinator.com\/item?id=47584540\">Hacker News<\/a> about the <strong>Claude Code<\/strong> source code that got leaked, and the first comment was pointing out how they have a <a href=\"https:\/\/github.com\/chatgptprojects\/claude-code\/blob\/642c7f944bbe5f7e57c05d756ab7fa7c9c5035cc\/src\/utils\/userPromptKeywords.ts#L8\">regex to detect a negative sentiment<\/a> in the code. Then one comment got me thinking:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/Pastedimage20260331143833.png\" alt=\"Claude Code uses regex to analyze the sentiment of the user prompt\"  \/>\n    <p class=\"image-description\">Claude Code uses regex to analyze the sentiment of the user prompt<\/p>\n<\/div>\n<\/p>\n<p>More specifically that last one, reproducing here:<\/p>\n<blockquote>\n<p>An LLM company using <em>regexes<\/em> for sentiment analysis? That&rsquo;s like a truck company using horses to transport parts. Weird choice.<\/p>\n<\/blockquote>\n<p>I think the analogy couldn&rsquo;t be more wrong. In fact, it makes more sense inverted. <strong>The LLM is the horse<\/strong>, and the regex is the truck. In the sense that regex are more <em>efficient<\/em> in parsing text than an LLM is, but it requires a person driving it, like the truck. While the horse can do it by itself, it&rsquo;s a more complicated system that is slower and more expensive (well, not in terms of money, but horses are animals that have needs and can&rsquo;t work all the time).<\/p>\n<p>That reminds me of a similar discussion I saw years ago when autonomous cars became a thing. So much research and engineering into a car to make it drive by itself and avoid obstacles, when the very previous <em>thing<\/em> we used for transportation already did all of this! Yes, the same horses!<\/p>\n<p>Of course, in the case of autonomous cars, we&rsquo;re talking about speed and conformation to the current transportation infrastructure we have in our cities. And in the LLM analogy, needless to say that it can perform sentiment analysis very well, but I guess everything has their own use case. Or as <a href=\"https:\/\/news.ycombinator.com\/item?id=47585709\">Hacker News user draxil posted<\/a>: <em>&ldquo;Good to have more than a hammer in your toolbox!&rdquo;<\/em><\/p>\n<p>What are your thoughts on it?<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/my-journey-trying-to-get-rid-of-caps-lock\/","title":"My journey trying to get rid of Caps Lock","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/my-journey-trying-to-get-rid-of-caps-lock\/"}},"updated":"2026-03-13T00:00:00Z","summary":"<p>The first time I ever used a typewriter, it was already a vintage device. It was my aunt&rsquo;s old machine, and I suspect it was already pretty much gimmicky by her time too. For a kid who had no computer in the late 90s, it was really cool to sit down and bash my tiny fingers to get some barely visible text on an A4 paper.<\/p>\n<p>Sorry <em>mechanical keyboards<\/em>, none of your fancy colorful switches could even scratch the difficulty of typing on one of those machines. Holding shift while pressing any other key was really a struggle. That&rsquo;s why the <strong>shift lock<\/strong> key was useful. I remember, being a very bored kid, investigating all those mechanisms and how that magical button would lock the shift key, which would itself move the carriage down, so the big caps on the typebar would hit the tape and the paper. You could feel the whole table move when you pressed shift again to release it quickly. Since the very next day we ditched the heavy machinery, the <strong>caps lock<\/strong> key was deprecated.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/Pastedimage20260313144708.png\" alt=\"my fianc\u00e9 playing with her old typewriter just so I could take a photo for this post\"  \/>\n    <p class=\"image-description\">my fianc\u00e9 playing with her old typewriter just so I could take a photo for this post<\/p>\n<\/div>\n<\/p>\n<p>If I EVER need to type all caps like that, I always hold shift all the way and I imagine you do that too. For that reason, I&rsquo;ve been either ignoring that key or remapping it to something else. For quite a long time I remapped it to <strong>Ctrl<\/strong>, but I honestly never used it very much. It wasn&rsquo;t until a few years ago, when I started using Neovim as my main code editor, that remapping it to <strong>Esc<\/strong> was the greatest change I made to my keyboard ever. And it didn&rsquo;t stick to only Neovim, I actually don&rsquo;t even have muscle memory to reach the top-left original <strong>Esc<\/strong> key anymore, even in games.<\/p>\n<p>The problem is that remapping keys is a surprisingly difficult problem if you consider different systems and different keyboards. I have three computers, and a Steam Deck, that I use on my day-to-day. Two laptops and one desktop. Every laptop has an external keyboard, but also the embedded one, that&rsquo;s a total of 5 keyboards that I type on regularly. That&rsquo;s not all, I do have two other laptops that I use sparingly. I also run different distros of Linux and Windows on these computers.<\/p>\n<p>For the longest time, I think I&rsquo;ve been remapping the keys with the system&rsquo;s configuration tools. Powertoys on Windows and the GNOME settings. More recently, I think after Wayland adoption, it&rsquo;s not available in the settings anymore, so last few times I had to set that up, I just copy-pasted something I found online. On Windows, Powertoys doesn&rsquo;t work with fullscreen applications. On my wayland-gnome, I had problems even with windowed games.<\/p>\n<p>I&rsquo;ve had so much frustration with this issue that I decided to actually stop and research carefully what my options are. This post covers my attempts, findings, and how I settled with a firmware solution for some keyboards, and software solutions in Windows and Linux.<\/p>\n<h2 id=\"custom-keyboard-firmware\">Custom keyboard firmware<\/h2>\n<p>I can hear someone screaming QMK since the very title of this post. And yes, that&rsquo;s part of my solution. QMK is a custom firmware and toolchain for flashing it that run in some of the most common mechanical keyboards out there.<\/p>\n<p>I said I have three keyboards I use daily, they&rsquo;re all mechanical with fancy modern firmwares, at least I thought. Two of those are <strong>Durgod K320<\/strong> and one is a <strong>Keychron K2<\/strong>. They all are technically supported by QMK. My Keychron not officially, but I was happy when I found this <a href=\"https:\/\/github.com\/rajumakantham\/QMK-on-K2V2\">repository<\/a> explaining how to flash QMK on it. But it turns out only version 2 and 3 support QMK, and I have the first one. So ruled out.<\/p>\n<h3 id=\"flashing-qmk-on-the-k320\">Flashing QMK on the K320<\/h3>\n<p><div class=\"image-div\">\n    <img src=\"images\/Pastedimage20260306125008.png\" alt=\"the top one has blue switches and the bottom, brown\"  \/>\n    <p class=\"image-description\">the top one has blue switches and the bottom, brown<\/p>\n<\/div>\n<\/p>\n<p>The K320, though, is officially supported, but in order to flash the QMK firmware, I need to open the keyboard, <a href=\"https:\/\/github.com\/qmk\/qmk_firmware\/blob\/master\/keyboards\/durgod\/k3x0\/readme.md\">short some contacts<\/a> to enable boot mode, then flash the image.<\/p>\n<p>So I started by installing the necessary QMK tools:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># installing QMK<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>curl -fsSL https:\/\/install.qmk.fm | sh\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># setting up environment<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>qmk setup\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"># compiling K320<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>qmk compile -kb durgod\/k320 -km default \n<\/span><\/span><\/code><\/pre><\/div><p>That generated <code>durgod_k320_base_default.bin<\/code> in the <code>qmk_firmware<\/code> folder. That was checked out in <code>$HOME<\/code> by the qmk installer.<\/p>\n<h4 id=\"opening-up-the-keyboard\">Opening up the Keyboard<\/h4>\n<p>One thing that the <a href=\"https:\/\/github.com\/qmk\/qmk_firmware\/blob\/master\/keyboards\/durgod\/k3x0\/readme.md\">article explaining how to flash it<\/a> is very clear about is that, although there are <strong>Durgod K320<\/strong> keyboards with different microcontrollers, it will only work in the one with an <strong>STM32<\/strong>. Which means that I have to open the keyboard and check it and there&rsquo;s a risk of no success.<\/p>\n<p>Opening this keyboard is quite difficult. There are <a href=\"https:\/\/www.youtube.com\/results?search_query=opening+durgod+k320\">several videos<\/a> explaining it, and I tried to follow them with my tools. First I started by removing the keys, and I was surprised with the amount of hair and fingernails that this collects. I took a photo, but I won&rsquo;t show it and disgust you with this. :)<\/p>\n<p>When I removed the board to check on the micro-controller, I realized that it was not a <strong>STM32<\/strong>, but an <strong>APM32<\/strong> instead:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/Pastedimage20260302233147.png\" alt=\"Geehy APM32 instead of the expected STM32\"  \/>\n    <p class=\"image-description\">Geehy APM32 instead of the expected STM32<\/p>\n<\/div>\n<\/p>\n<p>This worried me at first. Such work for nothing. So I researched a bit and it seems like the <strong>Geehy APM32 F072RBT6<\/strong> is <em>almost<\/em> a plug-in replacement for the <strong>STM32<\/strong>. So I decided to try anyway. At least I was going to backup the original firmware and if something didn&rsquo;t work, I&rsquo;d just flash it back.<\/p>\n<p>I shorted <code>boot0<\/code> to <code>vdd<\/code>, or <code>C27<\/code> and <code>R21<\/code>:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/Pastedimage20260302233401.png\" alt=\"If it&rsquo;s not clear, that&rsquo;s really really tiny and way more difficult to do that it seems\"  \/>\n    <p class=\"image-description\">If it&rsquo;s not clear, that&rsquo;s really really tiny and way more difficult to do that it seems<\/p>\n<\/div>\n<\/p>\n<p>And connected the USB cable at the same time, so the keyboard enters boot mode, in DFU. One way to test it: the LEDs near the contacts should not light up, and the device should show up in DFU:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>dfu-util -l\n<\/span><\/span><\/code><\/pre><\/div><p>But it didn&rsquo;t work. After many attempts, I thought that maybe I was shorting something else with my skin, so I made a lot of effort not to touch anything with my hands. Couple more tries and this showed up:<\/p>\n<pre tabindex=\"0\"><code>dfu-util 0.11\n\nCopyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.\nCopyright 2010-2021 Tormod Volden and Stefan Schmidt\nThis program is Free Software and has ABSOLUTELY NO WARRANTY\nPlease report bugs to http:\/\/sourceforge.net\/p\/dfu-util\/tickets\/\n\nFound DFU: [314b:0106] ver=0010, devnum=18, cfg=1, intf=0, path=&#34;1-3&#34;, alt=1, name=&#34;@Option Bytes  \/0x1FFFF800\/01*016 e&#34;, serial=&#34;0448&#34;\nFound DFU: [314b:0106] ver=0010, devnum=18, cfg=1, intf=0, path=&#34;1-3&#34;, alt=0, name=&#34;@Internal Flash  \/0x8000000\/06*002Kg,058*002Kg&#34;, serial=&#34;0448&#34;\n<\/code><\/pre><p>That&rsquo;s good news! So I backed up the stock firmware with:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>dfu-util -a <span style=\"color:#ae81ff\">0<\/span> -d 0483:DF11 -s 0x08000000 -U k320_original.bin\n<\/span><\/span><\/code><\/pre><\/div><p>And then I flashed the compiled qmk firmware I built earlier:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>qmk flash -kb durgod\/k320 -km default\n<\/span><\/span><\/code><\/pre><\/div><p>And it worked! I mean, the keyboard was on and it was working as expected.<\/p>\n<h4 id=\"some-more-customization\">Some more customization<\/h4>\n<p>Since I had it already opened, I decided to try something else. In many of the videos I saw, people were casting some silicone on the bottom shell to try and dampen the sound of the keyboard a bit. Since that keyboard of mine has the clicky blue switches, that I might not be that excited about anymore, I decided to try it with the tools I have: some paper towel:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/Pastedimage20260302235106.png\" alt=\"not as fancy as silicone or foam, but I like to improvise\"  \/>\n    <p class=\"image-description\">not as fancy as silicone or foam, but I like to improvise<\/p>\n<\/div>\n<\/p>\n<p>I might be biased, but I <em>think<\/em> it did improve the sound a bit. Maybe just a bit.<\/p>\n<h4 id=\"customizing-qmk\">Customizing QMK<\/h4>\n<p>Once QMK is installed, it&rsquo;s easier to flash new firmwares. To enter boot mode, I just need to disconnect the USB, press <strong>Esc + Space<\/strong> and then connect it back again. I tried many times and it didn&rsquo;t work. And I still don&rsquo;t know why, but it doesn&rsquo;t work very reliably, maybe like 1 every 4 or 5 times it actually enters boot mode. Not sure if there&rsquo;s anything wrong with my specific keyboard. Should research later.<\/p>\n<p>Then I went to the <a href=\"https:\/\/config.qmk.fm\/#\/durgod\/k320\/base\/LAYOUT_all\">QMK Configurator<\/a> and switched the <strong>Caps Lock<\/strong> key for <strong>Esc<\/strong>. That generates a <code>json<\/code> file with the config, but we need to build another firmware with this config file. So the configurator itself can compile and you just download the <code>bin<\/code> file.<\/p>\n<p>Once in bootmode:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>dfu-util -a <span style=\"color:#ae81ff\">0<\/span> -d 0483:df11 -s 0x08000000:leave -D durgod_k320_base_durgod_k320_base_layout_noesc.bin\n<\/span><\/span><\/code><\/pre><\/div><p>And it works as expected! Hardware level <strong>Caps Lock<\/strong> extermination!<\/p>\n<p>But I found the process a bit too complicated. Of course I&rsquo;m not gonna be changing the scheme all the time, but it&rsquo;s not that friendly to build a new firmware, get the keyboard into <em>bootmode<\/em> and flash it.<\/p>\n<h3 id=\"via\">VIA<\/h3>\n<p>Via is another solution with QMK that makes applying changes to the keyboard SO MUCH easier. You configure everything directly from a <a href=\"https:\/\/www.usevia.app\/\">webpage<\/a> and it flashes instantly to the keyboard:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/Pastedimage20260306124042.png\" alt=\"I love the fact that I can just open their website and change anything about my keyboard\"  \/>\n    <p class=\"image-description\">I love the fact that I can just open their website and change anything about my keyboard<\/p>\n<\/div>\n<\/p>\n<p>All I had to do was download the VIA firmware for my keyboard on their website and flash it. So much that I actually went much further and implemented a whole new layer of keys that I can reach while <em>holding<\/em> <strong>Caps Lock<\/strong>. So now I overloaded it to have two functions in one: it&rsquo;s <strong>Esc<\/strong> when tapped, reach <code>layer1<\/code> when held.<\/p>\n<h3 id=\"flashed-keyboards\">Flashed Keyboards<\/h3>\n<p>I did the process for both of my K320 and that solved part of my problem. Two of my keyboards now can be customized, but there&rsquo;s still all the laptop keyboards and the ones that just don&rsquo;t support QMK. And although I&rsquo;m always interested in getting a new keyboard, two-hundred-euros is not something I&rsquo;d like to spend if it&rsquo;s not a really good plasticky point-n-shoot camera from the 90s, amiright?<\/p>\n<h2 id=\"software-solution\">Software Solution<\/h2>\n<p>I always used the stock OS keyboard remap tool. On Linux it was whatever graphic environment&rsquo;s settings offered. The main problem with this approach is that it only works in that specific environment. No TTY or login screen supported. That&rsquo;s pretty lame, because I see myself using alternate TTY more than I should.<\/p>\n<p>After some research, I found out about <a href=\"https:\/\/github.com\/rvaiya\/keyd\">keyd<\/a>. It&rsquo;s a daemon-based solution that is heavily inspired by QMK and supports most of the same features. I don&rsquo;t really understand on which level the keyboard interception happens, but I&rsquo;m interested in exploring that at some point later.<\/p>\n<p>I appreciate the simplicity of <code>keyd<\/code>&rsquo;s configurations. To setup the basic Caps Lock=Esc, all I needed to do in my most recent Arch setup, from installing to having it working, was:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>sudo pacman -S keyd\n<\/span><\/span><\/code><\/pre><\/div><p>Then setup the config in <code>\/etc\/keyd\/default.conf<\/code>:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-ini\" data-lang=\"ini\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">[ids]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">*<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">[main]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">capslock<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">escape<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>And enabling the service:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>sudo systemctl enable --now keyd\n<\/span><\/span><\/code><\/pre><\/div><p>Boom. It works beautifully. By far the easiest possible solution to the <strong>Caps Lock<\/strong> problem.<\/p>\n<p>Since <code>keyd<\/code> also supports lots of other features similar to QMK\/VIA such as layers, I replicated my whole VIA config in it and the whole config is:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-ini\" data-lang=\"ini\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">[ids]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">*<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">[main]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">capslock<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">overload(layer1, escape)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">[layer1]<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">h<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">left<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">j<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">down<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">k<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">up<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">l<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">right<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">b<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">macro(leftcontrol+left)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">w<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">macro(leftcontrol+right)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">u<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">macro(leftcontrol+z)<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Explaining: I have my caps-lock key that acts as ESC when tapped, and change the layer when held. The other layer will have the <code>hjkl<\/code> vim-movement keys act as the arrows, allowing me to use them anywhere on my system, the same with <code>bw<\/code> for back and forward one word and <code>u<\/code> to undo stuff.<\/p>\n<p>This is especially good because I can now enjoy even more typing on my Thinkpad laptops, internationally known to have the best laptop keyboards.<\/p>\n<h3 id=\"windows\">Windows<\/h3>\n<p>I also need to replicate the software remapping to Windows. I&rsquo;ve been using <strong>PowerToys<\/strong>, and I genuinely think it&rsquo;s a good general collection of tools, but the keyboard remap is not really quite there. First it only supports mapping one key to another (no fancy layers, macros, etc), second it doesn&rsquo;t really work all the time, like in fullscreen programs.<\/p>\n<p>I found out that intercepting keys gets way more complicated on Windows. <code>keyd<\/code> itself suggests <a href=\"https:\/\/github.com\/kmonad\/kmonad\">kmonad<\/a> for more advanced customization, and it supports the Microsoft system. The configuration, though, is a bit more complex. Getting my Caps Lock\/layer config in it looks like this:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-lisp\" data-lang=\"lisp\"><span style=\"display:flex;\"><span>(defcfg\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0input\u00a0 (low-level-hook)\n<\/span><\/span><span style=\"display:flex;\"><span>  output (send-event-sink <span style=\"color:#ae81ff\">200<\/span> <span style=\"color:#ae81ff\">10<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0fallthrough true\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0allow-cmd false\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><span style=\"display:flex;\"><span>  \n<\/span><\/span><span style=\"display:flex;\"><span>(defsrc\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0grv\u00a0 1\u00a0 \u00a0 2\u00a0 \u00a0 3\u00a0 \u00a0 4\u00a0 \u00a0 5\u00a0 \u00a0 6\u00a0 \u00a0 7\u00a0 \u00a0 8\u00a0 \u00a0 9\u00a0 \u00a0 0\u00a0 \u00a0 <span style=\"color:#a6e22e\">-<\/span>\u00a0 \u00a0 <span style=\"color:#a6e22e\">=<\/span>\u00a0 \u00a0 bspc\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0tab\u00a0 q\u00a0 \u00a0 w\u00a0 \u00a0 e\u00a0 \u00a0 r\u00a0 \u00a0 <span style=\"color:#a6e22e\">t<\/span>\u00a0 \u00a0 y\u00a0 \u00a0 u\u00a0 \u00a0 i\u00a0 \u00a0 o\u00a0 \u00a0 p\u00a0 \u00a0 [\u00a0 \u00a0 ]\u00a0 \u00a0 <span style=\"color:#960050;background-color:#1e0010\">\\<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0caps a\u00a0 \u00a0 s\u00a0 \u00a0 d\u00a0 \u00a0 f\u00a0 \u00a0 g\u00a0 \u00a0 h\u00a0 \u00a0 j\u00a0 \u00a0 k\u00a0 \u00a0 l\u00a0 \u00a0 <span style=\"color:#75715e\">;\u00a0 \u00a0 &#39;\u00a0 \u00a0 ret<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0lsft z\u00a0 \u00a0 x\u00a0 \u00a0 c\u00a0 \u00a0 v\u00a0 \u00a0 b\u00a0 \u00a0 n\u00a0 \u00a0 m\u00a0 \u00a0 <span style=\"color:#f92672\">,<\/span>\u00a0 \u00a0 <span style=\"color:#f92672\">.<\/span>\u00a0 \u00a0 <span style=\"color:#a6e22e\">\/<\/span>\u00a0 \u00a0 rsft\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0lctl lmet lalt \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 spc\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ralt rmet cmp\u00a0 rctl\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>(defalias\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0cap (tap-hold-next <span style=\"color:#ae81ff\">200<\/span> esc (layer-toggle layer1))\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><span style=\"display:flex;\"><span>  \n<\/span><\/span><span style=\"display:flex;\"><span>(deflayer base\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0grv\u00a0 1\u00a0 \u00a0 2\u00a0 \u00a0 3\u00a0 \u00a0 4\u00a0 \u00a0 5\u00a0 \u00a0 6\u00a0 \u00a0 7\u00a0 \u00a0 8\u00a0 \u00a0 9\u00a0 \u00a0 0\u00a0 \u00a0 <span style=\"color:#a6e22e\">-<\/span>\u00a0 \u00a0 <span style=\"color:#a6e22e\">=<\/span>\u00a0 \u00a0 bspc\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0tab\u00a0 q\u00a0 \u00a0 w\u00a0 \u00a0 e\u00a0 \u00a0 r\u00a0 \u00a0 <span style=\"color:#a6e22e\">t<\/span>\u00a0 \u00a0 y\u00a0 \u00a0 u\u00a0 \u00a0 i\u00a0 \u00a0 o\u00a0 \u00a0 p\u00a0 \u00a0 [\u00a0 \u00a0 ]\u00a0 \u00a0 <span style=\"color:#960050;background-color:#1e0010\">\\<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0@cap a\u00a0 \u00a0 s\u00a0 \u00a0 d\u00a0 \u00a0 f\u00a0 \u00a0 g\u00a0 \u00a0 h\u00a0 \u00a0 j\u00a0 \u00a0 k\u00a0 \u00a0 l\u00a0 \u00a0 <span style=\"color:#75715e\">;\u00a0 \u00a0 &#39;\u00a0 \u00a0 ret<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0lsft z\u00a0 \u00a0 x\u00a0 \u00a0 c\u00a0 \u00a0 v\u00a0 \u00a0 b\u00a0 \u00a0 n\u00a0 \u00a0 m\u00a0 \u00a0 <span style=\"color:#f92672\">,<\/span>\u00a0 \u00a0 <span style=\"color:#f92672\">.<\/span>\u00a0 \u00a0 <span style=\"color:#a6e22e\">\/<\/span>\u00a0 \u00a0 rsft\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0lctl lmet lalt \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 spc\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ralt rmet cmp\u00a0 rctl\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><span style=\"display:flex;\"><span>  \n<\/span><\/span><span style=\"display:flex;\"><span>(deflayer layer1\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0_\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0_\u00a0 \u00a0 _\u00a0 \u00a0 C-rght _\u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 C-z\u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0_\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 left down up \u00a0 rght _\u00a0 \u00a0 _\u00a0 \u00a0 _\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0_\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 C-left _\u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\n<\/span><\/span><span style=\"display:flex;\"><span>\u00a0\u00a0_\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 _\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\u00a0 \u00a0 _\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><\/code><\/pre><\/div><p>Although I appreciate Lisp, I&rsquo;ll go with the INI style config any day.<\/p>\n<p><code>kmonad<\/code> is a cli tool, not a first-class citizen on <em>winland<\/em>. The program accepts the config file as a parameter, so you need to run as:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-bat\" data-lang=\"bat\"><span style=\"display:flex;\"><span>kmonad.exe .\\config.kbd\n<\/span><\/span><\/code><\/pre><\/div><p>&hellip; and you have to keep the empty terminal window open during all the execution of the program!<\/p>\n<p>You&rsquo;d think that having a program like this run as a background service would be a no-brainer, but it turns out Windows isolates its services for security reasons. While it&rsquo;s <em>technically<\/em> possible for a service to intercept keystrokes, it&rsquo;s just as simple as a Linux deamon. So what&rsquo;s the alternative?<\/p>\n<h4 id=\"kanata\">Kanata<\/h4>\n<p>Someone online recommended using <a href=\"https:\/\/github.com\/jtroo\/kanata\">kanata<\/a>, which started basically as a rewrite of <code>kmonad<\/code> in Rust, <em>by the way<\/em>. It has a better support for Windows, because it can be executed as a tray application.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/Pastedimage20260312170459.png\" alt=\"it&rsquo;s supposed to reload the config automatically though\"  \/>\n    <p class=\"image-description\">it&rsquo;s supposed to reload the config automatically though<\/p>\n<\/div>\n<\/p>\n<p>The config is slightly different, though:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-lisp\" data-lang=\"lisp\"><span style=\"display:flex;\"><span>(defcfg\n<\/span><\/span><span style=\"display:flex;\"><span>  process-unmapped-keys yes\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>(defalias\n<\/span><\/span><span style=\"display:flex;\"><span>  cap (tap-hold <span style=\"color:#ae81ff\">200<\/span> <span style=\"color:#ae81ff\">200<\/span> esc (layer-toggle layer1))\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>(defsrc\n<\/span><\/span><span style=\"display:flex;\"><span>  grv  <span style=\"color:#ae81ff\">1<\/span>    <span style=\"color:#ae81ff\">2<\/span>    <span style=\"color:#ae81ff\">3<\/span>    <span style=\"color:#ae81ff\">4<\/span>    <span style=\"color:#ae81ff\">5<\/span>    <span style=\"color:#ae81ff\">6<\/span>    <span style=\"color:#ae81ff\">7<\/span>    <span style=\"color:#ae81ff\">8<\/span>    <span style=\"color:#ae81ff\">9<\/span>    <span style=\"color:#ae81ff\">0<\/span>    <span style=\"color:#a6e22e\">-<\/span>    <span style=\"color:#a6e22e\">=<\/span>    bspc\n<\/span><\/span><span style=\"display:flex;\"><span>  tab  q    w    e    r    <span style=\"color:#66d9ef\">t<\/span>    y    u    i    o    p    [    ]    <span style=\"color:#960050;background-color:#1e0010\">\\<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  caps a    s    d    f    g    h    j    k    l    <span style=\"color:#75715e\">;    &#39;    ret<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  lsft z    x    c    v    b    n    m    <span style=\"color:#f92672\">,<\/span>    <span style=\"color:#f92672\">.<\/span>    <span style=\"color:#a6e22e\">\/<\/span>    rsft\n<\/span><\/span><span style=\"display:flex;\"><span>  lctl lmet lalt           spc            ralt rmet cmp  rctl\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>(deflayer base\n<\/span><\/span><span style=\"display:flex;\"><span>  grv  <span style=\"color:#ae81ff\">1<\/span>    <span style=\"color:#ae81ff\">2<\/span>    <span style=\"color:#ae81ff\">3<\/span>    <span style=\"color:#ae81ff\">4<\/span>    <span style=\"color:#ae81ff\">5<\/span>    <span style=\"color:#ae81ff\">6<\/span>    <span style=\"color:#ae81ff\">7<\/span>    <span style=\"color:#ae81ff\">8<\/span>    <span style=\"color:#ae81ff\">9<\/span>    <span style=\"color:#ae81ff\">0<\/span>    <span style=\"color:#a6e22e\">-<\/span>    <span style=\"color:#a6e22e\">=<\/span>    bspc\n<\/span><\/span><span style=\"display:flex;\"><span>  tab  q    w    e    r    <span style=\"color:#66d9ef\">t<\/span>    y    u    i    o    p    [    ]    <span style=\"color:#960050;background-color:#1e0010\">\\<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  @cap a    s    d    f    g    h    j    k    l    <span style=\"color:#75715e\">;    &#39;    ret<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>  lsft z    x    c    v    b    n    m    <span style=\"color:#f92672\">,<\/span>    <span style=\"color:#f92672\">.<\/span>    <span style=\"color:#a6e22e\">\/<\/span>    rsft\n<\/span><\/span><span style=\"display:flex;\"><span>  lctl lmet lalt           spc            ralt rmet cmp  rctl\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>(deflayer layer1\n<\/span><\/span><span style=\"display:flex;\"><span>  _    _    _    _    _    _    _    _    _    _    _    _    _    _\n<\/span><\/span><span style=\"display:flex;\"><span>  _    _    C-rght _  _    _    _    C-z  _    _    _    _    _    _\n<\/span><\/span><span style=\"display:flex;\"><span>  _    _    _    _    _    _    left down up   rght _    _    _\n<\/span><\/span><span style=\"display:flex;\"><span>  _    _    _    _    _    C-left _  _    _    _    _    _\n<\/span><\/span><span style=\"display:flex;\"><span>  _    _    _              _              _    _    _    _\n<\/span><\/span><span style=\"display:flex;\"><span>)\n<\/span><\/span><\/code><\/pre><\/div><p>Unfortunately, this solution is not as good as having a daemon running in the background, and it can only run in user-space. To make it start on logon, I created a shortcut in <code>shell:startup<\/code> with:<\/p>\n<pre tabindex=\"0\"><code>C:\\Users\\crocidb\\Kanata\\kanata_windows_gui_winIOv2_x64.exe -c C:\\Users\\crocidb\\dotfiles\\keyboards\\kanata_windows.kbd\n<\/code><\/pre><p>It does take longer to start on logon than I would like, but it works and is invisible. I set that up in my windows laptop and even the embedded keyboard now is remapped to my scheme.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>I can now have the same scheme on all my keyboards in all the computers I use. Even the Steam Deck and my less-used laptops are configured with these, so switching from one to another is completely seamless. I added all the keyboard configs to my <a href=\"https:\/\/github.com\/crocidb\/dotfiles\/tree\/master\/keyboards\">dotfiles<\/a>, so whenever I change one thing somewhere, all I need to do is <code>git pull<\/code> and get the latest version. It&rsquo;s still a bummer I have three different config systems (VIA, keyd and kanata), but such is life.<\/p>\n<p>This started as a quest to eliminate <strong>Caps Lock<\/strong> and eventually turned into a rabbit hole of customizing the whole keyboard experience. I got to learn many things about systems, drivers, firmwares and keyboards in general. So much that I want to keep researching this field. I already have so many notes and loose ends leading me to actually try to understand how <code>keyd<\/code> works and setup some tests on my own. But that&rsquo;s for another post.<\/p>\n<p>At the moment, I&rsquo;m looking to replace my <strong>Keychron<\/strong> keyboard that doesn&rsquo;t support qmk for a brand new one that already has it flashed. And I think I&rsquo;m falling out of love with the clicky blue switches. Let me know if you have any recommendations.<\/p>\n"},{"id":"https:\/\/crocidb.com\/now\/","title":"\u231b Now","link":{"@attributes":{"href":"https:\/\/crocidb.com\/now\/"}},"updated":"2026-03-06T00:00:00Z","summary":"<p>I don\u2019t know if it\u2019s ADHD or another neurodivergent condition, but I have a lot of different interests and I\u2019m constantly starting new projects and lines of research. Since I rarely finish a project, I keep coming back to them from time to time. So here I try to summarize my latest activities. This means that if a certain project disappears from this list, it\u2019s not because I\u2019ve finished or abandoned it, I\u2019ve just been focusing on other things for the moment.<\/p>\n<h3 id=\"tech-and-development\">Tech and Development<\/h3>\n <!-- - Working on a full version of Spacebar Clicker -->\n<ul>\n<li>Working on <a href=\"https:\/\/github.com\/CrociDB\/bulletty\">bulletty<\/a>, a TUI RSS\/ATOM feed reader app for the terminal, in Rust<\/li>\n<li>Working on <a href=\"https:\/\/github.com\/crocidb\/clutterlog\">clutterlog<\/a>, an SSG to make beautiful project WIP galleries, <a href=\"https:\/\/crocidb.github.io\/crocidb-clutterlog\/\">example<\/a>; also my first experience <em>vibe-coding<\/em> something. I&rsquo;ll write about it<\/li>\n<li>Designing a new keyboard experience using <code>QMK\/VIA<\/code> on hardware and <code>keyd<\/code> and <code>kmonad<\/code> on software<\/li>\n<li>Studying audio plugins and implementing a prototype for an old idea<\/li>\n<\/ul>\n <!-- - Building some guitar effects for my custom build of GuitarML's [FunBox](https:\/\/github.com\/GuitarML\/FunBox) -->\n<!--### Art\n\n\n<!-- ### Music -->\n<!---->\n<!-- Finishing a music project. It's an instrumental psychedelic rock with some influences from brazilian music. I composed, recorded all instruments, produced and mixed everything. It's been a really interesting journey. I'll blog about it when I'm done. -->\n<!---->\n<hr>\n<p>Last edit: <strong>2026-03-06<\/strong><\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/retrospective-2025\/","title":"Retrospective 2025","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/retrospective-2025\/"}},"updated":"2026-01-02T00:00:00Z","summary":"<p>The first quarter of the century is gone, and it&rsquo;s time for recapping this last cycle. With the passing of time feeling quicker and quicker, this is the moment I like to reflect on how my career and hobbies are going.<\/p>\n<h1 id=\"note-taking\">Note Taking<\/h1>\n<p>Just like in the last years, I like to start this text with my note taking habit. Since 2023, when I started my note-taking habits more seriously, I&rsquo;ve been trying to improve it and experiment with different techniques and mediums. This year, I started a notebook. A notebook where I write random notes, journaling, doodling and sketches. I took this notebook to all the trips I did this year and I tried to write as much as I could about anything that came to mind in those moments.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/notebook.gif\" alt=\"my notebook\"  \/>\n    <p class=\"image-description\">my notebook<\/p>\n<\/div>\n<\/p>\n<p>I feel like I can develop my ideas more fully while writing them on the notebook. Also, I created the habit of reading back, which makes me see how interesting those ideas really are. Very commonly I&rsquo;ll cringe out of something I wrote; other times, I&rsquo;ll expand it.<\/p>\n<p>I used it for literally everything. Here are some studies I did when I wanted to create the logo for <strong>bulletty<\/strong>:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/bulletty-logo-study.jpg\" alt=\"bulletty logo studies\"  \/>\n    <p class=\"image-description\">bulletty logo studies<\/p>\n<\/div>\n<\/p>\n<p>Writing every day is bringing back the muscle resistance I had back in University, where I actually had to write a lot. Along with that, I&rsquo;ve been drawing as much as I can, trying to develop a style to maybe in the future be able to write some comics:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/bobo.jpg\" alt=\"a small commentary on the Brazilian political landscape\"  \/>\n    <p class=\"image-description\">a small commentary on the Brazilian political landscape<\/p>\n<\/div>\n<\/p>\n<h2 id=\"dev-journal\">Dev Journal<\/h2>\n<p>Probably one of the most important things I started doing this year was journaling my personal projects. I started this habit at work a while back: I&rsquo;d have a project-wide public directory of articles, usually using Confluence, then I&rsquo;d simply journal about the current issue I was working on. Either a bug or a feature, I&rsquo;d just dump all the information I could with a hint of <em>storytelling<\/em>, basically guiding through my thought process on the issue, very well illustrated with screenshots and code snippets. That was very important so I could remember well what I was working on, and be able to return to previous subjects quickly. But it was also very useful to explain what was done when requesting code reviews.<\/p>\n<p>For my personal projects, I don&rsquo;t know how I never did it before, especially considering how many different projects I work at the same time. So I set up an Obsidian vault with a folder <code>Journal<\/code>, where I actually create the articles, a folder <code>Projects<\/code> where I store all the centralized information about a project, and another folder <code>Kanban<\/code> for kanban boards in these projects. <em>Adieu<\/em> Trello! With a template, I can organize my articles in a way that it&rsquo;s easier to access the projects it&rsquo;s assigned to:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/devjournal.png\" alt=\"the last article from 2025\"  \/>\n    <p class=\"image-description\">the last article from 2025<\/p>\n<\/div>\n<\/p>\n<p>I&rsquo;m not the best at keeping this board updated, but it&rsquo;s nice to be able to drag tasks here too.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/bulletty-kanban.png\" alt=\"bulletty kanban board\"  \/>\n    <p class=\"image-description\">bulletty kanban board<\/p>\n<\/div>\n<\/p>\n<p>And whenever I need to find all the texts relative to a project, I can go to the main project page and look for the pages that mention it:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/bulletty-out.png\" alt=\"all entries for bulletty\"  \/>\n    <p class=\"image-description\">all entries for bulletty<\/p>\n<\/div>\n<\/p>\n<p>It got also pretty easier to write texts for this blog, since I&rsquo;m already documenting a big part of the tech stuff I&rsquo;m doing.<\/p>\n<h1 id=\"blogging\">Blogging<\/h1>\n<p>There were fewer articles published this year, but I think I probably wrote way more. I also think that this year&rsquo;s articles are better researched and better written than those from previous years.<\/p>\n<ul>\n<li><a href=\"post\/on-programming-with-ai-assistants-vibe-coding\/\">Treat Your LLM Like a Junior Dev, Not an Oracle<\/a><\/li>\n<li><a href=\"post\/kernel-adventures\/demystifying-the-shebang\/\">Demystifying the #! (shebang): Kernel Adventures<\/a><\/li>\n<li><a href=\"post\/rant-internet-is-a-scam\/\">Rant: Internet is a Scam<\/a><\/li>\n<li><a href=\"post\/how-i-built-bulletty-tui-feed-reader-written-in-rust\/\">How I Built bulletty, a TUI Feed Reader in Rust<\/a><\/li>\n<li><a href=\"post\/rust-after-six-months-my-impressions\/\">Rust after six months: my impressions on language features, tooling and ecosystem<\/a><\/li>\n<li><a href=\"post\/generating-fantasy-book-titles-the-old-way\/\">Generating Fantasy book titles the old way<\/a><\/li>\n<li><a href=\"post\/maintaining-an-oss-during-hacktoberfest\/\">Maintaining an open source software during Hacktoberfest<\/a><\/li>\n<\/ul>\n<p>Most of them are about <strong>bulletty<\/strong> or Rust, which is because it was probably the personal project I dedicated myself the most this year. The post about the shebang made it to first page of HackerNews with a lot of interesting discussions.<\/p>\n<h1 id=\"projects\">Projects<\/h1>\n<p>A compilation of the status of the projects I worked on in 2025 and I can disclose.<\/p>\n<h2 id=\"bulletty\">bulletty<\/h2>\n<p>The TUI feed reader that I started working on by May. It was my last attempt to learn Rust and it paid off. I really learned a lot from working on this project, and apparently I&rsquo;m doing a good job, since I&rsquo;m seeing it get some traction. With 300 stars on GitHub and many downloads on crates.io, I feel this is genuinely a good tool. I&rsquo;ll keep working on it for the time being, since there&rsquo;s a lot of improvements that need to be done.<\/p>\n<p><a href=\"https:\/\/github.com\/CrociDB\/bulletty\"><div class=\"image-div\">\n    <img src=\"https:\/\/github.com\/CrociDB\/bulletty\/raw\/main\/img\/screenshot.gif\" alt=\"bulletty\"  \/>\n    <p class=\"image-description\">bulletty<\/p>\n<\/div>\n<\/a><\/p>\n<h2 id=\"flingern\">flingern<\/h2>\n<p>A static website generator, focused on creating a virtual art gallery. I started this project a few years ago, but I feel it&rsquo;s finally getting to the first release, along with my own art website.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/flingern.png\" alt=\"a preview of what flingern can do\"  \/>\n    <p class=\"image-description\">a preview of what flingern can do<\/p>\n<\/div>\n<\/p>\n<h2 id=\"guitar-pedal\">Guitar Pedal<\/h2>\n<p>Another project I spent a considerable amount of time on this year was a custom build of the <a href=\"https:\/\/github.com\/GuitarML\/FunBox\/\">FunBox<\/a> guitar pedal, by <a href=\"https:\/\/github.com\/GuitarML\">GuitarML<\/a>. It uses the <a href=\"https:\/\/daisy.audio\/hardware\/Seed\/\">Daisy Seed<\/a> chip, and has a bunch of knobs and switches for creating custom effects. It was my first time making a big project like that, with so many different components to solder and the case to assemble. I&rsquo;ll be writing more about it this year, as I&rsquo;m currently writing a procedural harmony synthesizer for it.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/funbox-build.jpg\" alt=\"my custom build of the FunBox pedal\"  \/>\n    <p class=\"image-description\">my custom build of the FunBox pedal<\/p>\n<\/div>\n<\/p>\n<h1 id=\"travel--photography\">Travel &amp; Photography<\/h1>\n<p>I was fortunate enough to take many trips this year. I took holidays in <em>Salvador, Brazil<\/em> and <em>Torreira, Portugal<\/em>, but also took shorter trips to <em>London<\/em>, <em>Amsterdam<\/em>, <em>S\u00e3o Paulo<\/em>, <em>Milan<\/em>, <em>Berlin<\/em>, <em>Munich<\/em> and more. Always walking around with at least one camera in my hand, I think I did a good job experimenting with film, infra-red camera, and decade old point-and-shoot digicam, while documenting simple day-to-day moments from those places.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/photos.jpg\" alt=\"some photos\"  \/>\n    <p class=\"image-description\">some photos<\/p>\n<\/div>\n<\/p>\n<p>For the first time I was also lucky enough to shoot a friend&rsquo;s wedding. A big responsibility that comes with a really good sense of accomplishment. Not to mention how much I had to learn before and during the wedding. Wedding photography is difficult!<\/p>\n<h1 id=\"misc\">Misc<\/h1>\n<p>Other interesting points from this year:<\/p>\n<ul>\n<li>I got engaged! :)<\/li>\n<li>I got back to play Chess! Back when I was visiting my family in S\u00e3o Paulo, I played a bit with my sister (who totally destroyed me), then when she came to visit me, we played a lot on a real physical board set. The tactile sensation of moving the pieces really gives extra pleasure, and reminds me of the time I used to play some local tournaments in my city.<\/li>\n<li>I did therapy for the most part of the year. That&rsquo;s been really interesting and I don&rsquo;t know if I ever learned so much about myself and the world in such a short amount of time. Totally recommend.<\/li>\n<li>With some friends recently converting to a new religion, I started listening a lot to their experiences, which made me learn more about religion and spirituality in general. I was raised a Catholic, but became an atheist since I was 17 years old. After that, I just didn&rsquo;t like religion at all. Can&rsquo;t say I like it more now, but I think I&rsquo;m finally free of the ick, so I can properly learn more about the history and its effects on a person and society.<\/li>\n<li>I played <em>Clair Obscur: Expedition 33<\/em> and it&rsquo;s safely one of the best games I&rsquo;ve ever played. Never thought JRPGs were for me, but I&rsquo;m glad I tried this one.<\/li>\n<li>I ate so much good food this year, street food, high-class restaurants, random pubs, homemade food, all-inclusive:<\/li>\n<\/ul>\n<p><div class=\"image-div\">\n    <img src=\"images\/food.jpg\" alt=\"I&rsquo;m a foodie, what can I do?\"  \/>\n    <p class=\"image-description\">I&rsquo;m a foodie, what can I do?<\/p>\n<\/div>\n<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/maintaining-an-oss-during-hacktoberfest\/","title":"Maintaining an open source software during Hacktoberfest","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/maintaining-an-oss-during-hacktoberfest\/"}},"updated":"2025-12-17T00:00:00Z","summary":"<p>I was finalizing the first working version of <a href=\"https:\/\/github.com\/CrociDB\/bulletty\"><strong>bulletty<\/strong><\/a> when I realized it was close to <a href=\"https:\/\/hacktoberfest.com\/\">Hacktoberfest<\/a>, the event created by DigitalOcean where they give t-shirts for people who contribute to open source software (more specifically, when you complete 6 successful PRs during the month of October). So I thought it could be interesting to have people contribute and help me make the feed reader better, also bringing more visibility to the project.<\/p>\n<p>I started by organizing the Issues in the project: one for each feature from the project roadmap and for bugs, with many details and images. Then I added appropriate tags, such as &ldquo;Improvements&rdquo;, &ldquo;Bugs&rdquo;, &ldquo;Good First Issue&rdquo;, and of course the &ldquo;Hacktoberfest&rdquo; tag. I also wrote a pretty generic <strong>CONTRIBUTING<\/strong> file, mostly explaining how to proceed when suggesting changes, reporting bugs or submitting a PR.<\/p>\n<p>To make it public, a few days before October, I posted on my personal accounts on X, Bluesky and Mastodon, then posted on the <code>\/r\/hacktoberfest<\/code> subreddit. Then I figured out there&rsquo;s an official Hacktoberfest discord server with a channel for people to recruit contributors, so I did it too. I tried Hacker News, but honestly was the channel where I got the fewest views.<\/p>\n<p>In just a few days, I saw the star counter on GitHub go to almost a hundred. Got many replies on all the platforms and I considered it an absolute success. Then October started&hellip;<\/p>\n<p>On the very first day, I started getting multiple comments on issues of people asking me to assign the issue to them. Without any type of discussion first:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/asking-to-assign-issue.png\" alt=\"People asking to be assigned to Issues\"  \/>\n    <p class=\"image-description\">People asking to be assigned to Issues<\/p>\n<\/div>\n<\/p>\n<p>I thought that was a bit too quick and I realized that I didn&rsquo;t want to assign issues to people without discussing what they&rsquo;re planning to implement in the first place. Maybe if someone already came with a pull request, I could consider their implementation and would review the code and discuss if that&rsquo;s the best way. But just assign them the issue and wait for whatever they came up with felt a bit too risky, because that meant that other people would not even check on that issue if someone is already on it.<\/p>\n<p>So I updated the <strong>CONTRIBUTING<\/strong> file with some more information on how to work on the issues:<\/p>\n<blockquote>\n<p><strong>Working on Issues<\/strong><\/p>\n<p>First requirement: use the program. I&rsquo;ve seen people wanting to contribute without using it. Issues will only be assigned to users when enough discussion about their implementation has taken place. It&rsquo;s important that nobody keeps an issue assigned without making progress, as this prevents others from contributing. So, if you want to write code for an existing issue, start by discussing the issue and your proposed solution first.<\/p>\n<p>I do think it&rsquo;s fine if you submit a PR for a bugfix you made without prior discussion, as long as you take the time to explain the why and the how. In that case, the issue won&rsquo;t be assigned to you until the merge is complete.<\/p>\n<\/blockquote>\n<p>So I replied that user:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/i-have-a-knowledge.png\" alt=\"I have a knowledge!\"  \/>\n    <p class=\"image-description\">I have a knowledge!<\/p>\n<\/div>\n<\/p>\n<p>I instantly suspected the user would be vibe-coding due to the way they described their proposal for the issue. So I asked, and they confirmed some sort of vibe-coding workflow. So I had to go and update the <strong>CONTRIBUTING<\/strong> file once again:<\/p>\n<blockquote>\n<p><strong>Generative AI use<\/strong><\/p>\n<p>I don&rsquo;t want to go as far as prohibiting anyone from using AI. After all, at this point, some AI use is inevitable. However, purely vibe-coded PRs are not going to be approved. If you&rsquo;re using AI to generate code, you must make it very clear. And you&rsquo;ll have to own it and maintain it. I will review and ask as many questions as necessary about the code, and I reserve the right to judge whether I think the contribution is worth it or not.<\/p>\n<p>Also, not properly communicating that you&rsquo;re using generated code in your PR is considered dishonest. If I find out, I&rsquo;ll have to close the PR.<\/p>\n<\/blockquote>\n<p>I use some AI myself, it&rsquo;s not the problem. However I see people simply delegating to an LLM to write all the code for them, and I think it&rsquo;s just bad.<\/p>\n<p>That user eventually submitted a PR for that:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/broken-pr.png\" alt=\"Looks too detailed explanation for an implementation that didn&rsquo;t even build\"  \/>\n    <p class=\"image-description\">Looks too detailed explanation for an implementation that didn&rsquo;t even build<\/p>\n<\/div>\n<\/p>\n<p>The PR didn&rsquo;t even build; the code wasn&rsquo;t formatted (despite me explicitly saying it&rsquo;s important in the CONTRIBUTING file); the two commits were titled <em>&ldquo;Update readerscreen.rs&rdquo;<\/em>, without any logical division; there was even an instance of a method&rsquo;s name that was changed, but without changing the name in the Trait it inherited from:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/method-name-change.png\" alt=\"handle_events was changed to handle_event for no apparent reason\"  \/>\n    <p class=\"image-description\"><code>handle_events<\/code> was changed to <code>handle_event<\/code> for no apparent reason<\/p>\n<\/div>\n<\/p>\n<p>So many indications that the user just asked their own agentic copilot for the changes and just tried to submit the PR. I closed the issue with some harsh words, something like &ldquo;please avoid wasting both our times&rdquo;.<\/p>\n<hr>\n<p>Some of the issues got people adding <em>&ldquo;attack plans&rdquo;<\/em> that looked very clearly LLM-generated, because they didn&rsquo;t make much sense to begin with. For example, one of the issues is caching the feed library, something that needs to be done but can be as simple as keeping a local variable of the feed entries per feed source, instead of querying the disk on every interaction (which is still happening to this day). One user suggested very vague and nonsensical <em>cache infrastructure<\/em> right after affirming they were not vibe-coding:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/cached-library.png\" alt=\"interesting cache infrastructure\"  \/>\n    <p class=\"image-description\">interesting cache <em>infrastructure<\/em><\/p>\n<\/div>\n<\/p>\n<p>I remember reading from other maintainers that during Hacktoberfest, it was really common to get spammy PRs with mostly typo fixing, simple comments being added or removed, or plainly useless changes. Now, with AI coding and advanced agentic models where you just show your codebase and ask for modifications and it does it all in place, maintaining open source software is getting more complex. For simple tasks, it might not even be a problem, but I can see users contributing with big refactors that were completely machine-made, possibly introducing not only a series of bugs, but eventually creating a code that nobody fully understands. Simply because <a href=\"https:\/\/ratfactor.com\/cards\/naur-vs-llms\">LLMs have no &ldquo;theory&rdquo;<\/a>, as in <a href=\"https:\/\/ratfactor.com\/papers\/naur1\">Peter Naur&rsquo;s &ldquo;Programming as Theory Building&rdquo;<\/a>.<\/p>\n<p>Plus, everyone seems to have their own conviction on what has been created with AI or not. This whole post is about how I dealt with <em>possible<\/em> AI generated contribution mostly based on the behaviour and messages of the user proposing them. But these could be just mistakes, maybe they were really just made by humans. For example, I see some people judging texts that are <em>overly<\/em> formatted with italics, bold and underline as something that LLMs could have done, but I myself do it quite a lot since forever. AI could be creating noise in the open source software community.<\/p>\n<h2 id=\"there-were-good-contributions\">There were good contributions<\/h2>\n<p>Nevertheless, there were good contributions. After the first wave of weird PRs, some more well-intentioned people got interested and started contributing to the project. Highlight to these PRs:<\/p>\n<h3 id=\"add-nextprev-navigation-to-the-reader\"><a href=\"https:\/\/github.com\/CrociDB\/bulletty\/pull\/54\">Add next\/prev navigation to the reader<\/a><\/h3>\n<p>A very important feature that was missing, the ability to navigate previous\/next posts from the reader screen itself. The implementation taught me about <code>Rc&lt;RefCell&lt;&gt;&gt;<\/code> in Rust.<\/p>\n<h3 id=\"feature-read-later\"><a href=\"https:\/\/github.com\/CrociDB\/bulletty\/pull\/73\">Feature: Read Later<\/a><\/h3>\n<p>The ability to store articles in a separate category to be read later. This was an extremely nice PR made by <a href=\"https:\/\/github.com\/alfaarghya\">alfaarghya<\/a>, after some very nice discussion on the best ways to implement it. He also contributed with other nice PRs during the time.<\/p>\n<hr>\n<p>Some other PRs that I&rsquo;ve reviewed and merged into the project were:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/CrociDB\/bulletty\/pull\/61\">feature: implement delete command for bulletty cli<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/CrociDB\/bulletty\/pull\/57\">feature: add new &lsquo;dirs&rsquo; command to display important directories<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/CrociDB\/bulletty\/pull\/55\">Fix integer underflow in macOS debug builds<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/CrociDB\/bulletty\/pull\/49\">fix: remove flawed if blocks in feedparser<\/a><\/li>\n<\/ul>\n<p>These inspired me so much that I ended up implementing many features during this time too. Since I was submitting PRs for myself to approve too, Hacktoberfest considered these and also sent me a t-shirt (that hasn&rsquo;t arrived yet).<\/p>\n<hr>\n<p>In the end it was a good idea. <strong>bulletty<\/strong> got some visibility, code contributions, many people also posting bugs and suggestions, even after the event. The project has now <strong>250<\/strong> stars on GitHub:<\/p>\n<p><a href=\"https:\/\/github.com\/CrociDB\/bulletty\"><div class=\"image-div\">\n    <img src=\"https:\/\/github.com\/CrociDB\/bulletty\/blob\/main\/img\/screenshot.gif?raw=true\" alt=\"bulletty\"  \/>\n    <p class=\"image-description\">bulletty<\/p>\n<\/div>\n<\/a><\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/generating-fantasy-book-titles-the-old-way\/","title":"Generating Fantasy book titles the old way","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/generating-fantasy-book-titles-the-old-way\/"}},"updated":"2025-12-04T00:00:00Z","summary":"<p>Back in 2012, I was really into fantasy and historical fiction books: <strong>The Name of the Wind<\/strong> (Patrick Rothfuss), <strong>A Magic of Twilight<\/strong> (S. L. Farrell), <strong>Armageddon<\/strong> (Eduardo Spohr), <strong>The Warlord Chronicles<\/strong> and <strong>The Saxon Stories<\/strong> (Bernard Cornwell), etc. I was also listening to a lot of podcasts on the subject, and following most of the latest releases in these genres in Brazil. I eventually noticed a pattern in the book titles.<\/p>\n<p>I think it&rsquo;s much more obvious in the translated Portuguese titles than it is in English, but I&rsquo;ll explain it with the English titles, because I can find enough examples that follow the rule. Most of the books are titled something like:<\/p>\n<pre tabindex=\"0\"><code>[Article] Noun _of_ [Article] Noun\n<\/code><\/pre><p>Examples:<\/p>\n<ul>\n<li>The Name of the Wind<\/li>\n<li>A Magic of Twilight<\/li>\n<li>Enemy of God<\/li>\n<li>Lords of the North<\/li>\n<li>A Game of Thrones<\/li>\n<li>The Fellowship of the Ring<\/li>\n<li>The Wheel of Time<\/li>\n<\/ul>\n<p>In English, you&rsquo;ll sometimes find a slightly different structure, with an adjective coming before the noun, like <strong>The Pale Horseman<\/strong>. In Portuguese, since the adjective always comes after the noun, I think it&rsquo;s much more common to use <strong>adjective phrases<\/strong> instead of just a single adjective. Plus, in English there are two ways of forming adjective phrases: <code>Noun Noun<\/code> and <code>Noun of Noun<\/code>, and they often mean different things. In Portuguese, this difference is not as clear.<\/p>\n<p>Based on that, I created a &ldquo;fantasy book title generator&rdquo;, and it&rsquo;s been online for about 13 years now, at the same address: <a href=\"https:\/\/labs.crocidb.com\/fantasy-generator\/\">https:\/\/labs.crocidb.com\/fantasy-generator\/<\/a>. Somehow it&rsquo;s still popular.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/fantasy-generator.gif\" alt=\"Fantasy Generator\"  \/>\n    <p class=\"image-description\">Fantasy Generator<\/p>\n<\/div>\n<\/p>\n<p>Even in the age of LLMs, it still gets around 100 visits per month. I&rsquo;ve had people tell me they&rsquo;ve used it for their DnD campaigns, including people who didn&rsquo;t even know I was the one who made it in the first place.<\/p>\n<hr>\n<p>The implementation is really basic. All I have is a list of nouns for the subject and another one for the adjective phrase:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;O&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Anjo&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;Os&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Anjos&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;O&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Rei&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;Os&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Reis&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;O&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Deus&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;Os&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Deuses&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;O&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Pante\u00e3o&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;A&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Irmandade&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>...\n<\/span><\/span><\/code><\/pre><\/div><p>Articles are separated because sometimes they can be omitted, which multiplies the possibilities even further.<\/p>\n<p>For the adjectives, Portuguese helped me even more, because we always combine the preposition and the article that comes after it. For example, in &ldquo;Lords of the North&rdquo;, the &ldquo;of the&rdquo; would be &ldquo;de o Norte&rdquo;, but &ldquo;de o&rdquo; becomes &ldquo;do&rdquo;, so it&rsquo;s shorter and easier:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;do&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Trono&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;do&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Reino&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;da&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Coroa&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;do&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Castelo&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;da&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Guerra&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;da&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Paz&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;da&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Coragem&#34;<\/span>],\n<\/span><\/span><span style=\"display:flex;\"><span>[<span style=\"color:#e6db74\">&#34;da&#34;<\/span>, <span style=\"color:#e6db74\">&#34;Justi\u00e7a&#34;<\/span>]\n<\/span><\/span><\/code><\/pre><\/div><p>I kept them separated because at some point I was going to introduce indefinite articles to the mix, but I just never did.<\/p>\n<p>Those nouns and adjectives are divided into different categories, things like &ldquo;Angels&rdquo;, &ldquo;Kings&rdquo;, &ldquo;Gods&rdquo;, &ldquo;Vampires&rdquo;, &ldquo;Magic&rdquo;, etc., from which the user can pick their own combination. The code to generate a title is really simple, and the last change was 13 years ago:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">generate<\/span>()\n<\/span><\/span><span style=\"display:flex;\"><span>{\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">nouns<\/span> <span style=\"color:#f92672\">=<\/span> [];\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">for<\/span> (<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">i<\/span> <span style=\"color:#66d9ef\">in<\/span> <span style=\"color:#a6e22e\">noun<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\t{\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">element<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">$<\/span>(<span style=\"color:#e6db74\">&#34;#&#34;<\/span> <span style=\"color:#f92672\">+<\/span> <span style=\"color:#a6e22e\">i<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">element<\/span>.<span style=\"color:#a6e22e\">length<\/span> <span style=\"color:#f92672\">==<\/span> <span style=\"color:#ae81ff\">0<\/span> <span style=\"color:#f92672\">||<\/span> <span style=\"color:#a6e22e\">element<\/span>.<span style=\"color:#a6e22e\">attr<\/span>(<span style=\"color:#e6db74\">&#34;checked&#34;<\/span>))\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t{\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t\t<span style=\"color:#a6e22e\">nouns<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">nouns<\/span>.<span style=\"color:#a6e22e\">concat<\/span>(<span style=\"color:#a6e22e\">noun<\/span>[<span style=\"color:#a6e22e\">i<\/span>]);\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t}\n<\/span><\/span><span style=\"display:flex;\"><span>\t}\n<\/span><\/span><span style=\"display:flex;\"><span>\t\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">index<\/span> <span style=\"color:#f92672\">=<\/span> Math.<span style=\"color:#a6e22e\">floor<\/span>(Math.<span style=\"color:#a6e22e\">random<\/span>() <span style=\"color:#f92672\">*<\/span> <span style=\"color:#a6e22e\">nouns<\/span>.<span style=\"color:#a6e22e\">length<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">selectedNoun<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">nouns<\/span>[<span style=\"color:#a6e22e\">index<\/span>];\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">article<\/span> <span style=\"color:#f92672\">=<\/span> Math.<span style=\"color:#a6e22e\">floor<\/span>(Math.<span style=\"color:#a6e22e\">random<\/span>() <span style=\"color:#f92672\">*<\/span> <span style=\"color:#ae81ff\">3<\/span>) <span style=\"color:#f92672\">==<\/span> <span style=\"color:#ae81ff\">1<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\t\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">generatedName<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;&#34;<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#f92672\">!<\/span><span style=\"color:#a6e22e\">article<\/span>) <span style=\"color:#a6e22e\">generatedName<\/span> <span style=\"color:#f92672\">+=<\/span> <span style=\"color:#a6e22e\">selectedNoun<\/span>[<span style=\"color:#ae81ff\">0<\/span>] <span style=\"color:#f92672\">+<\/span> <span style=\"color:#e6db74\">&#34; &#34;<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">generatedName<\/span> <span style=\"color:#f92672\">+=<\/span> <span style=\"color:#a6e22e\">selectedNoun<\/span>[<span style=\"color:#ae81ff\">1<\/span>];\n<\/span><\/span><span style=\"display:flex;\"><span>\t\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">adjs<\/span> <span style=\"color:#f92672\">=<\/span> [];\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">for<\/span> (<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">i<\/span> <span style=\"color:#66d9ef\">in<\/span> <span style=\"color:#a6e22e\">adj<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\t{\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">element<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">$<\/span>(<span style=\"color:#e6db74\">&#34;#&#34;<\/span> <span style=\"color:#f92672\">+<\/span> <span style=\"color:#a6e22e\">i<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">element<\/span>.<span style=\"color:#a6e22e\">length<\/span> <span style=\"color:#f92672\">==<\/span> <span style=\"color:#ae81ff\">0<\/span> <span style=\"color:#f92672\">||<\/span> <span style=\"color:#a6e22e\">element<\/span>.<span style=\"color:#a6e22e\">attr<\/span>(<span style=\"color:#e6db74\">&#34;checked&#34;<\/span>))\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t{\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t\t<span style=\"color:#a6e22e\">adjs<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">adjs<\/span>.<span style=\"color:#a6e22e\">concat<\/span>(<span style=\"color:#a6e22e\">adj<\/span>[<span style=\"color:#a6e22e\">i<\/span>]);\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t}\n<\/span><\/span><span style=\"display:flex;\"><span>\t}\n<\/span><\/span><span style=\"display:flex;\"><span>\t\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">index<\/span> <span style=\"color:#f92672\">=<\/span> Math.<span style=\"color:#a6e22e\">floor<\/span>(Math.<span style=\"color:#a6e22e\">random<\/span>() <span style=\"color:#f92672\">*<\/span> <span style=\"color:#a6e22e\">adjs<\/span>.<span style=\"color:#a6e22e\">length<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">var<\/span> <span style=\"color:#a6e22e\">selectedAdj<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">adjs<\/span>[<span style=\"color:#a6e22e\">index<\/span>];\n<\/span><\/span><span style=\"display:flex;\"><span>\t\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">generatedName<\/span> <span style=\"color:#f92672\">+=<\/span> <span style=\"color:#e6db74\">&#34; &#34;<\/span> <span style=\"color:#f92672\">+<\/span> <span style=\"color:#a6e22e\">selectedAdj<\/span>[<span style=\"color:#ae81ff\">0<\/span>] <span style=\"color:#f92672\">+<\/span> <span style=\"color:#e6db74\">&#34; &#34;<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">generatedName<\/span> <span style=\"color:#f92672\">+=<\/span> <span style=\"color:#a6e22e\">selectedAdj<\/span>[<span style=\"color:#ae81ff\">1<\/span>];\n<\/span><\/span><span style=\"display:flex;\"><span>\t\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">$<\/span>(<span style=\"color:#e6db74\">&#34;h1&#34;<\/span>).<span style=\"color:#a6e22e\">text<\/span>(<span style=\"color:#a6e22e\">generatedName<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span>};\n<\/span><\/span><\/code><\/pre><\/div><p>An LLM nowadays can give you results that are so much better, more diverse and more complex than that, but I find some beauty in simple solutions like that. Before I tried implementing a <a href=\"https:\/\/labs.crocidb.com\/horoscope\/\">zodiac horoscope generator<\/a> too. It was a bit more complex, because I had to divide phrases in different parts to have full sentences that made sense, but it worked beautifully too.<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/rust-after-six-months-my-impressions\/","title":"Rust after six months: my impressions on language features, tooling and ecosystem","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/rust-after-six-months-my-impressions\/"}},"updated":"2025-11-05T00:00:00Z","summary":"<p>Rust is only around ten years old, but it already made a huge impact in the software community. It went as far as making it into the Linux Kernel! The meme &lsquo;written in Rust, btw&rsquo; is often joked about, but in reality, it&rsquo;s inspiring many people to create interesting innovations in established domains.<\/p>\n<p>Think of tools like <a href=\"https:\/\/github.com\/astral-sh\/uv\">uv<\/a> for Python; <a href=\"https:\/\/github.com\/denoland\/deno\">deno<\/a> for Javascript; modern terminal emulators like <a href=\"https:\/\/github.com\/alacritty\/alacritty\">alacritty<\/a> and <a href=\"https:\/\/github.com\/wezterm\/wezterm\">wezterm<\/a>; a modern shell, <a href=\"https:\/\/github.com\/fish-shell\/fish-shell\">fish<\/a>; text\/code editors like <a href=\"https:\/\/zed.dev\/\">zed<\/a> and <a href=\"https:\/\/github.com\/helix-editor\/helix\">helix<\/a>; a new browser engine <a href=\"https:\/\/github.com\/servo\/servo\">servo<\/a>;  even gamedev with the highly experimental 100% data-driven game engine <a href=\"https:\/\/github.com\/bevyengine\/bevy\">bevy<\/a>. The new wave of remake of classic unix tools, such as <a href=\"https:\/\/github.com\/sharkdp\/bat\">bat<\/a> (for cat), <a href=\"https:\/\/github.com\/BurntSushi\/ripgrep\">ripgrep<\/a> (grep), <a href=\"https:\/\/github.com\/sxyazi\/yazi\">yazi<\/a> (tui file manager), <a href=\"https:\/\/github.com\/eza-community\/eza\">eza<\/a> (ls), <a href=\"https:\/\/github.com\/zellij-org\/zellij\">zellij<\/a> (screen\/tmux), etc. There are a lot of implications of creating a modern version of these tools, because they&rsquo;re doing one thing very well (as the unix philosophy), but also they&rsquo;ve been doing the very same thing for half a century. Rust is bringing more diverse people to the realm of systems programming. People with fresh ideas and different concepts about what it means to <em>use a computer<\/em>. Not necessarily better, but different.<\/p>\n<p>I also joined the trend of <em>rewritten in Rust<\/em> and created <a href=\"https:\/\/crocidb.com\/post\/how-i-built-bulletty-tui-feed-reader-written-in-rust\/\">bulletty<\/a>, a TUI feed reader. And it&rsquo;s been six months since I&rsquo;ve been learning this language and this post is a collection of notes on parts of it that I find interesting.<\/p>\n<h2 id=\"my-first-experience-with-it\">My first experience with it<\/h2>\n<p>Back in 2019, I started doing the <a href=\"https:\/\/raytracing.github.io\/\">Ray Tracing in One Weekend<\/a> series of books in Rust, at the same time I was reading <a href=\"https:\/\/doc.rust-lang.org\/stable\/book\/\">The Rust Programming Language<\/a> book. I did get <a href=\"https:\/\/github.com\/CrociDB\/pathtracer\">something out of it<\/a>, I think I finished the first raytracing book entirely.<\/p>\n<p>It didn&rsquo;t get me really hooked, but I think it has more to do with what was happening in my life at the moment, I was going through some depression, I had just moved countries and it was my first European winter. Coming straight from a place where it&rsquo;s summer every single day of the year.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/raytracing-in-one-weekend.png\" alt=\"a basic raytracer written in Rust\"  \/>\n    <p class=\"image-description\">a basic raytracer written in Rust<\/p>\n<\/div>\n<\/p>\n<p>I moved on, but I wanted to try more at some point.<\/p>\n<h2 id=\"crate-ecosystem\">Crate ecosystem<\/h2>\n<p>I want to start off with the crate ecosystem. If you&rsquo;re not familiar with it, Rust has a very complete build system with a package\/dependency manager in it, <code>cargo<\/code>. With it, you can very easily include 3rd party libraries, or <code>crates<\/code>, into your project, something very similar to <code>npm<\/code> in Javascript, or <code>pip<\/code> in Python. But it&rsquo;s the first time I&rsquo;ve see a dependency manager work so well in a compiled language. For C++, there&rsquo;s <a href=\"https:\/\/conan.io\/\">conan<\/a>, <a href=\"https:\/\/vcpkg.io\/en\/\">vcpkg<\/a> and <a href=\"https:\/\/github.com\/cpm-cmake\/CPM.cmake\">cpm<\/a>, or even pure CMake&rsquo;s <em>FetchContent<\/em>, but honestly it&rsquo;s a pain to get it working seamlessly.<\/p>\n<p>It&rsquo;s really convenient to look for &ldquo;Perlin Noise&rdquo; and just include the first crate you find that will provide you with the needed functions. However, there are problems with it, such as dependencies that rely on other dependencies that may not fit your project for a variety of reasons such as license or simply because another of your dependency also require that dependency, but in another version. For example, <strong>bulletty<\/strong> explicitly declares <em>27<\/em> dependencies at the moment, but considering all the necessary dependencies to build it, it goes to an outstanding number of <em>366<\/em>!<\/p>\n<p>That means there&rsquo;s just so much code running when you fire up bulletty that I&rsquo;ll never be able to audit. On the other hand, it&rsquo;s thanks to them that bulletty was made possible. I honestly don&rsquo;t think I would have gotten that far if I had to parse HTML into Markdown myself, it&rsquo;s too big of a mountain to climb when you&rsquo;re starting a project. Now I&rsquo;m customizing parts of the <a href=\"https:\/\/gitlab.com\/CrociDB\/html2md\">crate that does that<\/a>. :)<\/p>\n<h2 id=\"algebraic-data-types--pattern-matching\">Algebraic Data Types &amp; Pattern Matching<\/h2>\n<p>Pattern Matching is one of my favorite language features. It&rsquo;s really expressive and convenient to describe exactly what you&rsquo;re trying to compare, for example:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-rust\" data-lang=\"rust\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">match<\/span> x { \n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#ae81ff\">1<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;one&#34;<\/span>),\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#ae81ff\">2<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;two&#34;<\/span>), \n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#ae81ff\">3<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;three&#34;<\/span>), \n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#ae81ff\">4<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;four&#34;<\/span>), \n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#ae81ff\">5<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;five&#34;<\/span>), \n<\/span><\/span><span style=\"display:flex;\"><span>\t_ <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;something else&#34;<\/span>), \n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p>That example of course is very similar to a <code>switch-case<\/code>  expression, but what now?<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-rust\" data-lang=\"rust\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">match<\/span> ph { \n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#ae81ff\">0<\/span><span style=\"color:#f92672\">..<\/span><span style=\"color:#ae81ff\">7<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;acid&#34;<\/span>), \n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#ae81ff\">7<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;neutral&#34;<\/span>), \n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#ae81ff\">8<\/span><span style=\"color:#f92672\">..=<\/span><span style=\"color:#ae81ff\">14<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;base&#34;<\/span>),\n<\/span><\/span><span style=\"display:flex;\"><span>\t_ <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">unreachable!<\/span>()\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p>Examples were taken from <a href=\"https:\/\/doc.rust-lang.org\/reference\/expressions\/match-expr.html\">Rust Reference<\/a>.<\/p>\n<p>But this gets way more exciting when you combine these with an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Algebraic_data_type\">Algebraic Data Type<\/a>, such as <a href=\"https:\/\/doc.rust-lang.org\/book\/ch06-01-defining-an-enum.html\">Rust Enums<\/a>:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-rust\" data-lang=\"rust\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">enum<\/span> <span style=\"color:#a6e22e\">IpAddr<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>\tV4(<span style=\"color:#66d9ef\">u8<\/span>, <span style=\"color:#66d9ef\">u8<\/span>, <span style=\"color:#66d9ef\">u8<\/span>, <span style=\"color:#66d9ef\">u8<\/span>),\n<\/span><\/span><span style=\"display:flex;\"><span>\tV6(String), \n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><span style=\"display:flex;\"><span> \n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">let<\/span> home <span style=\"color:#f92672\">=<\/span> IpAddr::V4(<span style=\"color:#ae81ff\">127<\/span>, <span style=\"color:#ae81ff\">0<\/span>, <span style=\"color:#ae81ff\">0<\/span>, <span style=\"color:#ae81ff\">1<\/span>); \n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">let<\/span> loopback <span style=\"color:#f92672\">=<\/span> IpAddr::V6(String::from(<span style=\"color:#e6db74\">&#34;::1&#34;<\/span>));\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\">\/\/...\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">match<\/span> ip {\n<\/span><\/span><span style=\"display:flex;\"><span>\tV4(ip1, ip2, ip3, ip4) <span style=\"color:#f92672\">=&gt;<\/span> println(<span style=\"color:#e6db74\">&#34;You IP is: {}.{}.{}.{}&#34;<\/span>, ip1, ip2, ip3, ip4),\n<\/span><\/span><span style=\"display:flex;\"><span>\tV6(ipv6) <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;Your IP is: <\/span><span style=\"color:#e6db74\">{}<\/span><span style=\"color:#e6db74\">&#34;<\/span>, ipv6);\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p>Since <code>IpAddr<\/code> can be either a <code>V4<\/code> or an <code>V6<\/code>, and they hold different information, you can match the type and bind variables for the data needed. If you ever did any Haskell, OCaml or Scala, you&rsquo;d be smiling now.<\/p>\n<h2 id=\"error-handling\">Error handling<\/h2>\n<p>I always hated <code>try...catch<\/code> constructs in C# and Java. First because the way it looks, several blocks of <code>try<\/code> with multiple lines within it, followed by lots of <code>catch<\/code> seemed very hard to read in my opinion. Not to mention that it&rsquo;s not always obvious some function will thrown an exception, completely breaking the flow of the program. In C++, I absolutely never used it.<\/p>\n<p>Rust offers very robust mechanisms to handle errors. Functions can return a <code>Result&lt;Type, Error&gt;<\/code> monad, similar to <code>Option&lt;Type&gt;<\/code>, containing either <code>Ok(T)<\/code> or <code>Err(E)<\/code>, which makes it instantly clear how to deal with the result. Plus, there&rsquo;s a lot of syntactic sugars that help the code get more readable while still safe. For example, the <code>?<\/code> operator:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-rust\" data-lang=\"rust\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">fn<\/span> <span style=\"color:#a6e22e\">do_something<\/span>() -&gt; Result<span style=\"color:#f92672\">&lt;<\/span>(), Error<span style=\"color:#f92672\">&gt;<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">let<\/span> test <span style=\"color:#f92672\">=<\/span> get_some_value()<span style=\"color:#f92672\">?<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">let<\/span> another_test <span style=\"color:#f92672\">=<\/span> test.get_something_else()<span style=\"color:#f92672\">?<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\tanother_test.do_something()\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p>The <code>?<\/code> operator redirects the result of a function call to the return of the current function, effectively delegating the obligation to check errors to the caller of <code>do_something()<\/code>. That way, you can call several functions that will themselves return a result and not worry for that at that moment.<\/p>\n<p>The good things about the <code>Result<\/code> type don&rsquo;t stop there. It includes functions for quick testing such as <code>is_error()<\/code>, or to transform into another error type with  <code>map_err<\/code>, and since it&rsquo;s just another value, you can pattern match it when you want to treat it. The easiest way to error checking that function above is:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-rust\" data-lang=\"rust\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">if<\/span> <span style=\"color:#66d9ef\">let<\/span> Err(my_error) <span style=\"color:#f92672\">=<\/span> do_something() {\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">println!<\/span>(<span style=\"color:#e6db74\">&#34;Oops, I got this error: <\/span><span style=\"color:#e6db74\">{:?}<\/span><span style=\"color:#e6db74\">&#34;<\/span>, my_error);\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><h2 id=\"returning\">Returning<\/h2>\n<p>In Rust, it will automatically return the last value in the block, so you can have a function like:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-rust\" data-lang=\"rust\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">fn<\/span> <span style=\"color:#a6e22e\">double<\/span>(x: <span style=\"color:#66d9ef\">i32<\/span>) -&gt; <span style=\"color:#66d9ef\">i32<\/span> { x <span style=\"color:#f92672\">*<\/span> <span style=\"color:#ae81ff\">2<\/span> }\n<\/span><\/span><\/code><\/pre><\/div><p>No need to add <code>;<\/code> at the end of a return value, which makes it instantly recognizable.<\/p>\n<p>Since any block can return, it&rsquo;s simpler to attribute conditional values to variables with <code>if<\/code> or <code>match<\/code>, for example:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-rust\" data-lang=\"rust\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\">\/\/ almost like a ternary operator\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">let<\/span> i: Option<span style=\"color:#f92672\">&lt;<\/span><span style=\"color:#66d9ef\">i32<\/span><span style=\"color:#f92672\">&gt;<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">if<\/span> b <span style=\"color:#f92672\">==<\/span> <span style=\"color:#ae81ff\">2<\/span> { \n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">let<\/span> z: <span style=\"color:#66d9ef\">i32<\/span> <span style=\"color:#f92672\">=<\/span> b <span style=\"color:#f92672\">*<\/span> <span style=\"color:#ae81ff\">20<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\tSome(z)\n<\/span><\/span><span style=\"display:flex;\"><span>} <span style=\"color:#66d9ef\">else<\/span> { \n<\/span><\/span><span style=\"display:flex;\"><span>\tNone \n<\/span><\/span><span style=\"display:flex;\"><span>};\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\">\/\/ pattern matching returns values!\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">let<\/span> z: <span style=\"color:#66d9ef\">i32<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">match<\/span> i {\n<\/span><\/span><span style=\"display:flex;\"><span>\tNone <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#ae81ff\">0<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>\tSome(v) <span style=\"color:#f92672\">=&gt;<\/span> v,\n<\/span><\/span><span style=\"display:flex;\"><span>};\n<\/span><\/span><\/code><\/pre><\/div><p>Or even cooler, just regular blocks:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-rust\" data-lang=\"rust\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">let<\/span> i: <span style=\"color:#66d9ef\">i32<\/span> <span style=\"color:#f92672\">=<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#66d9ef\">let<\/span> b <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">42<\/span> <span style=\"color:#f92672\">*<\/span> <span style=\"color:#ae81ff\">3<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>    b\n<\/span><\/span><span style=\"display:flex;\"><span>};\n<\/span><\/span><\/code><\/pre><\/div><p>And considering Rust objects only live during their own scope, creating limited blocks like this is more useful to keep your memory in control.<\/p>\n<h2 id=\"compiler-messages-are-fantastic\">Compiler messages are fantastic<\/h2>\n<p>Rust is a bureaucratic language, purposely. Its memory allocation\/deallocation model, binding heap memory to their scopes, creates a lot of friction to programmers used to either languages that deals with garbage, or languages that expects you to do your chores. But it does by shifting all that responsibility to the compiler. It&rsquo;s frustrating, but the messages the compiler and the linter give you are really detailed, often explaining the issue and suggesting fixes.<\/p>\n<h2 id=\"best-lsp\">Best LSP<\/h2>\n<p>As a professional C++ and C# programmer, I&rsquo;m very used to Visual Studio, which in general index really well the symbols in the projects and offers several functionalities to help you navigate in a large codebase. So far, I haven&rsquo;t found a single LSP that offered a similar experience to Visual Studio (or other big IDEs), something like detecting types well, finding definitions in other files, suggesting and applying changes in code, reliably renaming fields and functions, etc. But it changes with the Rust LSP. It&rsquo;s very complete and works incredibly well with Neovim, for example.<\/p>\n<p>Of course, it helps that the language has a static type system and a relatively simple build system. For example, C++ has types, but building and linking is a whole nightmare. So many possible ways to build a project in C++, with different compilers, different linkers, different runtimes, that I can understand if no LSPs will ever be really good. On the other hand there&rsquo;s Python or Javascript, where the runtime just need to make dependencies available and run it, but their type system is basically non-existent.<\/p>\n<p>Rust&rsquo;s LSP is aware of the whole project, it finds definitions really quick, it allows for symbols renaming, it resolves <code>use<\/code> directives, it displays API documentations within the editor (and even lets you open the API code directly) and it gives lots of suggestions to improve code. I see some people shitting on LSPs because &ldquo;you&rsquo;re supposed to know your codebase&rdquo;, as if auto-completions were even the most important thing in it.<\/p>\n<p>But beware the LSP consumes a lot of system resources. I was trying to program rust on a VPS with only 2GB of RAM and it was a bit challenging. Constantly crashing. Good thing I got a much beefier VPS now.<\/p>\n<h2 id=\"compile-times-are-long\">Compile times are long<\/h2>\n<p>It&rsquo;s a <a href=\"https:\/\/kobzol.github.io\/rust\/rustc\/2025\/06\/09\/why-doesnt-rust-care-more-about-compiler-performance.html\">common issue<\/a>, the compiler is slow. Building bulletty from scratch takes around a minute. LSP boot-up time when I open neovim is a bit longer that I would like. But honestly nothing that really annoys me. Just a consideration.<\/p>\n<h2 id=\"use-directives-can-get-a-bit-confusing-sometimes\">use directives can get a bit confusing sometimes<\/h2>\n<p>You open a Rust source code and first thing you see is this.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/use.png\" alt=\"use directive hell\"  \/>\n    <p class=\"image-description\">use directive hell<\/p>\n<\/div>\n<\/p>\n<p>Tell me if you don&rsquo;t find it a bit confusing. I&rsquo;m coming from the C++ realm where you can easily find 75 lines of <code>#include &quot;..\/..\/module\/test\/foo.h<\/code> in the beginning of the file, but at least it&rsquo;s instantly recognizable. All the nesting modules imports in Rust look almost like code, so it&rsquo;s hard to find where code actually starts. I&rsquo;m glad the LSP usually handles that for me.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>I feel like Rust is a perfect example of a modern language with very good tooling that can build fast, safe natives without a lot of hassle. Easy to start prototyping with 3rd party libraries, beautifully portable, fast and safe. It brought people from the C++-land and the Ruby-land together, it made it to the Linux kernel. Of course, it&rsquo;s not all flowers. The language can be a little complicated to get started with because of the borrow-checker, and it&rsquo;s a rather complicated language with lots of features, but for many domains, it can be almost like coding in python or javascript.<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/how-i-built-bulletty-tui-feed-reader-written-in-rust\/","title":"How I Built bulletty, a TUI Feed Reader in Rust","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/how-i-built-bulletty-tui-feed-reader-written-in-rust\/"}},"updated":"2025-09-16T00:00:00Z","summary":"<p>I&rsquo;m happy to officially announce one of the projects I&rsquo;ve been working on this summer. It&rsquo;s <a href=\"https:\/\/github.com\/CrociDB\/bulletty\"><strong>bulletty<\/strong><\/a>, a pretty, open-source TUI feed reader written in Rust that stores the articles locally as Markdown, giving the user total freedom over the articles.<\/p>\n<p><a href=\"https:\/\/github.com\/CrociDB\/bulletty\"><div class=\"image-div\">\n    <img src=\"https:\/\/github.com\/CrociDB\/bulletty\/raw\/refs\/heads\/main\/img\/screenshot.gif\" alt=\"bulletty\"  \/>\n    <p class=\"image-description\">bulletty<\/p>\n<\/div>\n<\/a><\/p>\n<blockquote>\n<p>For those unfamiliar, TUI means a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Text-based_user_interface\">Text-based user interface<\/a>, it means the program&rsquo;s interface is implemented in text in a terminal. That&rsquo;s also why <strong>bulletty<\/strong> has a <strong>TTY<\/strong> postfix, it&rsquo;s an old terminology for text terminals that tracks back to typewritters. Now you know why <strong>ghostty<\/strong>, <strong>kitty<\/strong>, <strong>mintty<\/strong>, or the linux <code>\/dev\/tty*<\/code> devices.<\/p>\n<\/blockquote>\n<p>More than a decade ago, <strong>Google Reader<\/strong> was one of my main applications when I was sitting in front of my computer. I had accumulated lots of good blogs that I liked to read by then, so opening the app to check what was new was my comfort thing. Eventually Google killed that reader, but it was a time where we were more interested in following the writers on Twitter. Easier to interact with them about their texts, but slightly harder to keep up with their published content, because you could easily miss the tweet.<\/p>\n<p>But it was like that for some time. Then came the programming article boards, such as <a href=\"https:\/\/news.ycombinator.com\/news\">Hacker News<\/a> and <a href=\"https:\/\/www.reddit.com\/r\/programming\/\">\/r\/programming<\/a>. Which for years were my daily source of information related to tech world, and still is. But more recently I felt like I wanted to go back to having my own feed subscriptions, as I discovered many great blogs using these platforms. Since the death of Google Reader, I had an account on <strong>feedly<\/strong>, so I used it. Although it didn&rsquo;t tick all the boxes for me. I was constantly shown ads, the UI didn&rsquo;t feel quite right, and I had zero desire to pay for yet another subscription I didn&rsquo;t truly need. So, I decided to explore other options.<\/p>\n<p>I thought of TUIs, since a lot of my personal project work I do through a VPS, it would be cool to just open my feed reader in another tmux pane while I work. I tested a few options, but honestly didn&rsquo;t get used to any of them.<\/p>\n<p>At the same time, I was fixing some issues with the feed for my blog, because I never really gave too much attention to my own feed generation before, I rely on <strong>Hugo<\/strong> for the blog generation and I just trusted it to generate the RSS\/ATOM well, but turns out it&rsquo;s the theme you&rsquo;re using the responsible to format the feed. And it was kind of broken, so I took some time to fix it and I wanted to promote it. I wanted more people to subscribe to my blog through the feed.<\/p>\n<p>So I thought of creating a small project that I could write about and then bring this subject up. First idea was a feed reader for the <strong>Flipper Zero<\/strong>, because why not? I didn&rsquo;t have the WiFi module for the flipper, but I had a <strong>ESP8266<\/strong> lying around, so&hellip; I&rsquo;d implement my own feed fetching and send it to the Flipper Zero through UART&hellip;<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/flipper_rss.jpeg\" alt=\"Flipper RSS application\"  \/>\n    <p class=\"image-description\">Flipper RSS application<\/p>\n<\/div>\n<\/p>\n<p>&hellip; well, I was on the right track. Wrote the flipper app first, started a communication with my own arduino program running on the ESP to exchange information, but then, when I was going to write the network part I realized that it only has around 80KB of RAM, and my whole feed xml file is over 200KB. Which means I&rsquo;d have to stream it. At the same time I&rsquo;d have to parse the XML, and parse the HTML within the content&hellip; That seemed like too much work for a small, week-long project, so I gave up on it.<\/p>\n<p>Back to nothing, I realized I&rsquo;d just go and implement my own feed reader the way I wanted it to be. Analyzing the options, I thought of a feature set that would make it my favorite feed reader:<\/p>\n<ul>\n<li>It must be a TUI, because I&rsquo;d like to read my subscriptions on my SSHd remote machine<\/li>\n<li>It must be easy to use, using a similar VIM-like keybindings that I&rsquo;m already used to<\/li>\n<li>It must look good and offer a good terminal reading experience<\/li>\n<li>It must save my files locally in a Obsidian vault-like file structure. I&rsquo;ve been using Obsidan a lot lately and I&rsquo;m very used to its simple structure. I can sync my whole feed library with git and have it on any device<\/li>\n<li>It should allow me to add articles to a read later section<\/li>\n<li>It should allow me make highlights of the text, and also add notes to it<\/li>\n<\/ul>\n<p>That sounded like a compelling piece of software to make. So I started looking for good TUI libraries to make it a really good experience. At first, Zig was going to be my language of choice, but didn&rsquo;t find any library for Zig that caught my eye. Then I read about <a href=\"https:\/\/ratatui.rs\/\">ratatui<\/a>, for Rust. I only ever wrote Rust before in 2019, when I started doing the <a href=\"https:\/\/github.com\/CrociDB\/pathtracer\">Ray Tracing in One Weekend<\/a>, by <strong>Peter Shirley<\/strong>. But since Rust was still on my list as a language to learn, I took the challenge. I&rsquo;ll write about this experience of writing Rust later.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/ratatui.jpeg\" alt=\"ratatui for Rust\"  \/>\n    <p class=\"image-description\">ratatui for Rust<\/p>\n<\/div>\n<\/p>\n<p>In only a few days, maybe a week, of course using many available <strong>crates<\/strong>, I had a raw sketch of a feed reader that would sync the sources, download the articles and have them available in my file system. So I went ahead and implemented all the base features I needed to start using my own feed reader.<\/p>\n<p>Right now, it&rsquo;s still far from being my dream reader, but it&rsquo;s already very usable. You can subscribe to sources into different categories, sync them, read them in the reader or open them externally, the vault-like disk structure of the files is there, so it&rsquo;s Bring Your Own Sync-method. That&rsquo;s the moment I thought I should put it our and show it to the world. Also, make sure I make the project open to contributions. It&rsquo;s <a href=\"https:\/\/hacktoberfest.com\/\">Hacktoberfest<\/a> time, and I already added its tag. So, if you know Rust and you&rsquo;re into contributing to open source software, here&rsquo;s your chance!<\/p>\n<p>I&rsquo;m creating <a href=\"https:\/\/github.com\/CrociDB\/bulletty\/issues\">Issues<\/a> in the GitHub page for bugs and features that are on the roadmap. But basically, the most important features to be implemented next are:<\/p>\n<ul>\n<li>\u00a0<strong>Themes<\/strong>: I want it to support multiple common themes like <strong>catppuccin<\/strong>, <strong>kanagawa<\/strong>, <strong>gruvbox<\/strong>, etc<\/li>\n<li>\u00a0<strong>Read later<\/strong>: an essential category for all the interesting articles I&rsquo;ll probably never get around to reading<\/li>\n<li>\u00a0<strong>Article Highlighting<\/strong>: I want to be able to highlight parts of the articles and keep it stored in a way that&rsquo;s easy to access alter<\/li>\n<li>\u00a0<strong>Notes on articles<\/strong>: similarly to the highlights, I think it&rsquo;s important for me to keep notes on interesting texts<\/li>\n<li>\u00a0<strong>Web View<\/strong>: that&rsquo;s an interesting idea, since all the articles are being stored as Markdown files, I could theoretically generate a static webpage from it and serve it to read on my phone and other devices<\/li>\n<li>\u00a0<strong>Mouse support<\/strong>: although it runs on the terminal, it&rsquo;s very handy to have it respond to mouse commands, just like any NeoVim or tmux<\/li>\n<li>\u00a0<strong>Image support<\/strong>: that&rsquo;s an important one, it should display the images when using a modern terminal emulator, using the <em>kitty image protocol<\/em>, or <em>sixel<\/em><\/li>\n<\/ul>\n<p>I&rsquo;ll be working on those features myself, without any deadline, just as I feel like doing. But I&rsquo;m very open for contributions. Whether it&rsquo;s a bug fix, a bug report, refactoring, writing tests to make it more robust, or just simply ideas to make this software better.<\/p>\n<p>If you&rsquo;re into the idea, give it a try. Install it with <code>cargo install bulletty<\/code>, subscribe to your feed sources, test it on different platforms (so far I&rsquo;ve only ran it on Linux), and don&rsquo;t forget to subscribe to this blog&rsquo;s <a href=\"https:\/\/crocidb.com\/index.xml\">feed<\/a>!<\/p>\n"},{"id":"https:\/\/crocidb.com\/about\/","title":"\ud83d\udc4b About","link":{"@attributes":{"href":"https:\/\/crocidb.com\/about\/"}},"updated":"2025-09-08T00:00:00Z","summary":"<p>Hello! I&rsquo;m a programmer, gamedev and hobbist artist, originally from <em>S\u00e3o Paulo, Brazil<\/em>, now based in <em>D\u00fcsseldorf, Germany<\/em>. I&rsquo;m a very curious and creative person, I feel pleasure by creating things, above everything. As a professional video game programmer, and currently working as a <strong>Senior Audio Programmer<\/strong> for <strong>Ubisoft D\u00fcsseldorf<\/strong>, my background spans gameplay, generalist, and audio programming. I&rsquo;m genuinely passionate about all aspects of video game development \u2013 diving into everything from engines and systems to graphics, audio, and of course, crafting engaging gameplay. I&rsquo;ve had the opportunity to contribute to titles such as <strong>Assassin&rsquo;s Creed Nexus VR<\/strong>, <strong>XDefiant<\/strong>, <strong>Angest<\/strong>, <strong>Rock&amp;Rails<\/strong>, <strong>Conflict0: Shattered<\/strong>, and <strong>Jake &rsquo;n Tess: Finding Monsters<\/strong>.<\/p>\n<p>Beyond my professional work, programming remains a significant hobby, providing a constant source of learning and experimentation. Some of my relevant personal projects are <a href=\"https:\/\/github.com\/CrociDB\/bulletty\">bulletty<\/a>, a TUI feed reader that stores the user data locally; <a href=\"https:\/\/github.com\/CrociDB\/flingern\">flingern<\/a>, a static art website generator; and my toy game engine, <a href=\"https:\/\/github.com\/CrociDB\/annileen\">annileen<\/a>. I also enjoy creating unique experiments, like <a href=\"https:\/\/github.com\/CrociDB\/doomoji\">rendering DOOM with emojis<\/a>, a <a href=\"https:\/\/crocidb.github.io\/retro2048\/\">2048 game that runs on the bootsector<\/a>, <a href=\"https:\/\/github.com\/CrociDB\/wordlos\">a Wordle clone for DOS written in x86 asm<\/a>, a <a href=\"https:\/\/github.com\/CrociDB\/mygbemu\">gameboy emulator<\/a>, and <a href=\"https:\/\/crocidb.itch.io\/phosphorus-dating\">several<\/a> <a href=\"https:\/\/crocidb.itch.io\/mitosis-project\">small<\/a> <a href=\"https:\/\/crocidb.itch.io\/spacelord-x\">games<\/a>.<\/p>\n<p>Photography has been one of my main interests throughout my whole life, I&rsquo;m always carrying a camera around and taking seamingly meaningless pictures. Music production is another big interest for me. I&rsquo;ve wrote some small soundtracks for games I made that can be seen on my <a href=\"https:\/\/soundcloud.com\/bruno-croci\">SoundCloud<\/a> and I&rsquo;m currently working on an EP. Recently, I started drawing and painting, and it&rsquo;s been such a good way to express thoughts and feelings.<\/p>\n<p>On this site, you can see what I&rsquo;m focused on right <a href=\"https:\/\/crocidb.com\/now\">\/now<\/a> or explore more <a href=\"https:\/\/crocidb.com\/things-ive-made\">Things I&rsquo;ve Made<\/a>. If you&rsquo;d like to see more of my work elsewhere, you can:<\/p>\n<ul>\n<li>Play my games on <a href=\"https:\/\/crocidb.itch.io\/\">itch.io<\/a><\/li>\n<li>Check my art website where I display everything artistic I do: <a href=\"https:\/\/bruno.croci.art\/\">Bruno Croci&rsquo;s Art<\/a><\/li>\n<li>Check out my open-source code on <a href=\"https:\/\/github.com\/CrociDB\">GitHub<\/a><\/li>\n<li>See generative art shaders on <a href=\"https:\/\/www.shadertoy.com\/user\/crocidb\">Shadertoy<\/a><\/li>\n<li>Listen to my music at <a href=\"https:\/\/soundcloud.com\/bruno-croci\">SoundCloud<\/a><\/li>\n<li>View my photos on <a href=\"https:\/\/pixelfed.de\/crocidb\">Pixelfed<\/a> or my <a href=\"https:\/\/bruno.croci.art\/\">Art Website<\/a><\/li>\n<li>Follow my updates and thoughts on <a href=\"https:\/\/bsky.app\/profile\/crocidb.bsky.social\">Bluesky<\/a> or <a href=\"https:\/\/mastodon.gamedev.place\/@crocidb\">Mastodon<\/a><\/li>\n<\/ul>\n<p>This blog is where I share insights on technology, programming, photography, art, and occasionally, my thoughts on other random topics. My main website, <a href=\"https:\/\/bruno.croci.me\/\">bruno.croci.me<\/a>, acts as a central hub for everything. For more details on my professional experience, please visit my <a href=\"http:\/\/linkedin.com\/in\/crocidb\">Linkedin profile<\/a>.<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/rant-internet-is-a-scam\/","title":"Rant: Internet is a Scam","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/rant-internet-is-a-scam\/"}},"updated":"2025-05-23T00:00:00Z","summary":"<p>At this point it&rsquo;s pretty clear that online search is broken. When googling anything you get ads, then more ads, then some organic websites that are abusing SEO tactics to sell you stuff. For example, try finding a good and fair comparison of VPN services. &lsquo;VPN&rsquo; is a hot keyword nowadays, google it and you&rsquo;ll get a full page of sponsored results. Next page is just mostly sponsored links, then a bunch of random websites with unimportant information just selling these services with affiliated links. Amazon is the same. Not only it&rsquo;s full of low-quality products, but it will also favor sellers who&rsquo;ll pay to appear in your search. I had a terrible time lately trying to find a good USB-C cable that would be high-speed transfer and fast-charging.<\/p>\n<p>Some argue that LLMs are the salvation of internet search, but honestly, how can you trust a system that was trained on what&rsquo;s already available online? Also, I&rsquo;m just counting the days until the companies providing LLM chats start serving ads <em>within<\/em> the text. Do you seriously think they&rsquo;re gonna be favoring an ad-less system? The whole point of capitalism is selling products; the day the AI will reply &ldquo;coke is really good for hangovers&rdquo;, when you ask what to do the morning after a night out is rather close. Due to regulations, it might even show a small disclaimer &ldquo;there are ads in this response&rdquo;, but who would care? Your best oracle friend says something, you believe, right? I mean, ChatGPT is already trying to <a href=\"https:\/\/www.businessinsider.com\/chatgpt-changes-nice-openai-overly-complimentary-model-tweak-supportive-personality-2025-4\">convince you it&rsquo;s your best friend<\/a>, it takes surprisingly little to create a trustworthy character.<\/p>\n<p>I always remember an <a href=\"https:\/\/www.youtube.com\/watch?v=Zib6OC_yJxk\">interview with Isaac Asimov in the late 80s<\/a> where he says he thinks that in the future, everyone will have a terminal that is connected to a huge web of knowledge, like a library, where you&rsquo;ll be able to ask about any subject and it will give you good answers that you could learn from. Well, that is already a reality, but he did not account for the fact that most of the information available in that library would be rubbish. Separating the good from the bad is exhausting and requires a lot of energy, which is why we often resort to trusting content creators or channels. But the main problem with trust is that most of those channels are just trying to get money from you.<\/p>\n<p>Social media doesn&rsquo;t show your friends updates anymore, they prioritize those <em>content creators<\/em>. These creators are often little more than sellers with an incredible reach. Their &ldquo;content&rdquo; is a mere piece of advertising that&rsquo;s filling the network with tons of low quality material\u2014that just assuming good will, because there&rsquo;s a lot of bad intentions out there too. In between all that, more ads. And when you post some personal update, like a nice photo of a sunset or the beautiful dish your just cooked, you get comments and interaction from bots, trying to <strong>scam<\/strong> you the dirty way.<\/p>\n<p>The whole internet experience is degrading. It&rsquo;s no longer the place to search for a specific subject or get informed about what&rsquo;s going on in the World <strong>reliably<\/strong>, it&rsquo;s barely a place for entertainment. Driven by consumerism, these platforms can consume your attention, draining your energy while you <em>believe<\/em> you&rsquo;re just relaxing. Or draining your pocket, making you buy low-quality products that solve problems you don&rsquo;t have while making the world a more inhospitable place. And I don&rsquo;t mean to call you out, the person spending a portion of your day scrolling\u2014it&rsquo;s not your fault, don&rsquo;t worry. But the fact is that there are people right now engineering ways to get your more addicted to their app and simplifying ways to sell your more things you absolutely don&rsquo;t need. It&rsquo;s incredibly frustrating because, at this point, there aren&rsquo;t many ways to escape it; we depend on it.<\/p>\n<p>I know there are interesting services like <a href=\"https:\/\/kagi.com\/\">Kagi<\/a>, that promises a better search engine with no ads; decentralized photo social network like <a href=\"https:\/\/pixelfed.org\/\">Pixelfed<\/a>;  <a href=\"https:\/\/en.wikipedia.org\/wiki\/Wikipedia:Wikipedians\">many dedicated people working tirelessly<\/a> for free to keep the internet useful, but their voices often aren&rsquo;t as loud.<\/p>\n<p>Maybe I&rsquo;m just too frustrated and looking on the negative side of things, but I genuinely feel we&rsquo;re going towards the wrong direction: one where there&rsquo;s no internet, no computers, only <em>terminals<\/em> to superior intelligence (AI) and quick entertainment, all controlled by big corporations. A future where everything we do is mediated by these agents, and everything we think is dictated by them.<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/kernel-adventures\/demystifying-the-shebang\/","title":"Demystifying the #! (shebang): Kernel Adventures","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/kernel-adventures\/demystifying-the-shebang\/"}},"updated":"2025-04-07T00:00:00Z","summary":"<p>From my first experience creating a shell script, I learned about the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Shebang_(Unix)\"><strong>shebang<\/strong><\/a> (<code>#!<\/code>), the special first line used to specify the interpreter for executing the script:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\">#! \/usr\/bin\/sh\n<\/span><\/span><\/span><span style=\"display:flex;\"><span>echo <span style=\"color:#e6db74\">&#34;Hello, World!&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>So that you can just invoke it with <code>.\/hello.sh<\/code> and it will run with the specified interpreter, assuming the file has execute permissions.<\/p>\n<p>Of course, the shebang isn&rsquo;t limited to shell scripts; you can use it for any script type:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-python\" data-lang=\"python\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\">#! \/usr\/bin\/python3<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>print(<span style=\"color:#e6db74\">&#34;Hello, World!&#34;<\/span>)\n<\/span><\/span><\/code><\/pre><\/div><p>This is particularly useful because many bundled Linux utilities are actually scripts. Thanks to the shebang, you don&rsquo;t need to explicitly invoke their interpreters. For example, there are two (very confusing) programs to create a user on Linux: <code>useradd<\/code> and <code>adduser<\/code>. One of them is the actual program that will create the user in the system, the other one is a utility that will create the user, the home directory and configure the user for you. Since I never remember which one is which, a good way to check is using the utility <code>file<\/code>:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>$ file <span style=\"color:#66d9ef\">$(<\/span>which useradd<span style=\"color:#66d9ef\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\/usr\/sbin\/useradd: ELF 64-bit LSB pie executable, x86-64, version <span style=\"color:#ae81ff\">1<\/span> <span style=\"color:#f92672\">(<\/span>SYSV<span style=\"color:#f92672\">)<\/span>, dynamically linked, interpreter \/lib64\/ld-linux-x86-64.so.2 <span style=\"color:#f92672\">(<\/span>...<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>$ file <span style=\"color:#66d9ef\">$(<\/span>which adduser<span style=\"color:#66d9ef\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\/usr\/sbin\/adduser: Perl script text executable\n<\/span><\/span><\/code><\/pre><\/div><p>Ok, we know that <code>adduser<\/code> is the tool we want to use, because it&rsquo;s more user-friendly and generally does what you&rsquo;d expect when adding a user. And yes, if you check how it starts:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>$ head -n <span style=\"color:#ae81ff\">1<\/span> \/usr\/sbin\/adduser\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\">#! \/usr\/bin\/perl<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>I had always assumed the <em>shell<\/em> used the shebang as a hint, but that&rsquo;s incorrect! <strong>This functionality is actually handled directly by the Linux Kernel.<\/strong><\/p>\n<h1 id=\"tracking-the-kernel-execution\">Tracking the kernel execution<\/h1>\n<p>One good way to track any executable in Linux is using <code>strace<\/code>, which traces all the system calls made by a process:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>$ strace .\/test.sh\n<\/span><\/span><span style=\"display:flex;\"><span>execve<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>, <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span><span style=\"color:#f92672\">]<\/span>, 0x7ffed15d9828 \/* <span style=\"color:#ae81ff\">33<\/span> vars *\/<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>brk<span style=\"color:#f92672\">(<\/span>NULL<span style=\"color:#f92672\">)<\/span>                               <span style=\"color:#f92672\">=<\/span> 0x59aea5a28000\n<\/span><\/span><span style=\"display:flex;\"><span>mmap<span style=\"color:#f92672\">(<\/span>NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> 0x78ee2be49000\n<\/span><\/span><span style=\"display:flex;\"><span>access<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;\/etc\/ld.so.preload&#34;<\/span>, R_OK<span style=\"color:#f92672\">)<\/span>      <span style=\"color:#f92672\">=<\/span> -1 ENOENT <span style=\"color:#f92672\">(<\/span>No such file or directory<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">(<\/span>...<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Interesting, the call to <code>test.sh<\/code> goes straight into <code>execve<\/code>, the syscall to start running a program from a file. This implies the kernel itself is <em>responsible for finding<\/em> the correct interpreter and executing it.<\/p>\n<p>If we start digging into the kernel code, we can see that the entry point for the <code>execve<\/code> syscall is in the function <code>do_execveat_common<\/code>, found in <a href=\"https:\/\/github.com\/torvalds\/linux\/blob\/master\/fs\/exec.c#L1967\"><code>fs\/exec.c<\/code><\/a>. It starts by creating a <code>struct linux_binprm *bprm;<\/code> which means &ldquo;binary program&rdquo;, then performing some checks, and eventually calling <code>bprm_execve<\/code>:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span>retval <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">bprm_execve<\/span>(bprm);\n<\/span><\/span><\/code><\/pre><\/div><p><code>bprm_execve<\/code> then proceeds to <code>exec_binprm<\/code>, which will then eventually invoke <code>search_binary_handler<\/code>. This function is responsible for identifying the file&rsquo;s executable format. It starts with <code>retval = prepare_binprm(bprm)<\/code> and following that function, we realize it&rsquo;s actually copying part of the contents of the file into the <code>bprm-&gt;buf<\/code>:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span><span style=\"color:#75715e\">\/*\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"> * Fill the binprm structure from the inode.\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"> * Read the first BINPRM_BUF_SIZE bytes\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"> *\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"> * This may be called multiple times for binary chains (scripts for example).\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\"> *\/<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">static<\/span> <span style=\"color:#66d9ef\">int<\/span> <span style=\"color:#a6e22e\">prepare_binprm<\/span>(<span style=\"color:#66d9ef\">struct<\/span> linux_binprm <span style=\"color:#f92672\">*<\/span>bprm)\n<\/span><\/span><span style=\"display:flex;\"><span>{\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">loff_t<\/span> pos <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">memset<\/span>(bprm<span style=\"color:#f92672\">-&gt;<\/span>buf, <span style=\"color:#ae81ff\">0<\/span>, BINPRM_BUF_SIZE);\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#a6e22e\">kernel_read<\/span>(bprm<span style=\"color:#f92672\">-&gt;<\/span>file, bprm<span style=\"color:#f92672\">-&gt;<\/span>buf, BINPRM_BUF_SIZE, <span style=\"color:#f92672\">&amp;<\/span>pos);\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p><code>BINPRM_BUF_SIZE<\/code> is <em>256<\/em> in <code>include\/linux\/binfmts.h<\/code><\/p>\n<p>Then it proceeds to look through a list of formats and checks which one the current program is:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">list_for_each_entry<\/span>(fmt, <span style=\"color:#f92672\">&amp;<\/span>formats, lh) {\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#f92672\">!<\/span><span style=\"color:#a6e22e\">try_module_get<\/span>(fmt<span style=\"color:#f92672\">-&gt;<\/span>module))\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">continue<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">read_unlock<\/span>(<span style=\"color:#f92672\">&amp;<\/span>binfmt_lock);\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>\tretval <span style=\"color:#f92672\">=<\/span> fmt<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">load_binary<\/span>(bprm);\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">read_lock<\/span>(<span style=\"color:#f92672\">&amp;<\/span>binfmt_lock);\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">put_binfmt<\/span>(fmt);\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">if<\/span> (bprm<span style=\"color:#f92672\">-&gt;<\/span>point_of_no_return <span style=\"color:#f92672\">||<\/span> (retval <span style=\"color:#f92672\">!=<\/span> <span style=\"color:#f92672\">-<\/span>ENOEXEC)) {\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#a6e22e\">read_unlock<\/span>(<span style=\"color:#f92672\">&amp;<\/span>binfmt_lock);\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">return<\/span> retval;\n<\/span><\/span><span style=\"display:flex;\"><span>\t}\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p>Those format modules are:<\/p>\n<ul>\n<li>binfmt_elf.c<\/li>\n<li>binfmt_elf_fdpic.c<\/li>\n<li>binfmt_flat.c<\/li>\n<li>binfmt_misc.c<\/li>\n<li>binfmt_script.c<\/li>\n<\/ul>\n<p>And they all are responsible for registering themselves so <code>search_binary_handler<\/code> test each one of them. We know that <a href=\"https:\/\/en.wikipedia.org\/wiki\/Executable_and_Linkable_Format\">ELF<\/a> is the regular binary format that Linux uses, <a href=\"https:\/\/cateee.net\/lkddb\/web-lkddb\/BINFMT_ELF_FDPIC.html\">ELF FDPIC<\/a> is an extension to ELF, FLAT binaries are just the instructions without any specific system configuration (<a href=\"https:\/\/stackoverflow.com\/questions\/1283342\/executing-a-flat-binary-file-under-linux\">this question<\/a> explains a bit), SCRIPT is the format that interprets our shebang, but what really caught my eye was MISC.<\/p>\n<p>According to the <a href=\"https:\/\/docs.kernel.org\/admin-guide\/binfmt-misc.html\">official Kernel Admin Guide<\/a>:<\/p>\n<blockquote>\n<p>This Kernel feature allows you to invoke almost (for restrictions see below) every program by simply typing its name in the shell. This includes for example compiled Java(TM), Python or Emacs programs. To achieve this you must tell binfmt_misc which interpreter has to be invoked with which binary. Binfmt_misc recognises the binary-type by matching some bytes at the beginning of the file with a magic byte sequence (masking out specified bits) you have supplied. Binfmt_misc can also recognise a filename extension aka\u00a0<code>.com<\/code>\u00a0or\u00a0<code>.exe<\/code>.<\/p>\n<\/blockquote>\n<p>It&rsquo;s another way to tell the Kernel what interpreter to run when invoking a program that&rsquo;s not native (ELF). For scripts (text files) we mostly use a shebang, but for byte-coded binaries, such as Java&rsquo;s JAR or Mono EXE files, it&rsquo;s the way to go!<\/p>\n<p>Returning to our shebang investigation, let&rsquo;s examine <code>fs\/binfmt_script.c<\/code>. Checking its registration mechanism near the end of the file reveals some key information:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">core_initcall<\/span>(init_script_binfmt);\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">module_exit<\/span>(exit_script_binfmt);\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">MODULE_DESCRIPTION<\/span>(<span style=\"color:#e6db74\">&#34;Kernel support for scripts starting with #!&#34;<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">MODULE_LICENSE<\/span>(<span style=\"color:#e6db74\">&#34;GPL&#34;<\/span>);\n<\/span><\/span><\/code><\/pre><\/div><p>There&rsquo;s the module description (yep, shebang is not an official term), then a <code>core_initcall<\/code> call pointing to <code>init_script_binfmt<\/code>:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">static<\/span> <span style=\"color:#66d9ef\">int<\/span> __init <span style=\"color:#a6e22e\">init_script_binfmt<\/span>(<span style=\"color:#66d9ef\">void<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>{\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">register_binfmt<\/span>(<span style=\"color:#f92672\">&amp;<\/span>script_format);\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#ae81ff\">0<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p>That registers the <code>script_format<\/code> object, which is defined like this:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">static<\/span> <span style=\"color:#66d9ef\">struct<\/span> linux_binfmt script_format <span style=\"color:#f92672\">=<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>\t.module\t\t<span style=\"color:#f92672\">=<\/span> THIS_MODULE,\n<\/span><\/span><span style=\"display:flex;\"><span>\t.load_binary\t<span style=\"color:#f92672\">=<\/span> load_script,\n<\/span><\/span><span style=\"display:flex;\"><span>};\n<\/span><\/span><\/code><\/pre><\/div><p>And when we examine the <code>load_script<\/code> function, boom:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">static<\/span> <span style=\"color:#66d9ef\">int<\/span> <span style=\"color:#a6e22e\">load_script<\/span>(<span style=\"color:#66d9ef\">struct<\/span> linux_binprm <span style=\"color:#f92672\">*<\/span>bprm)\n<\/span><\/span><span style=\"display:flex;\"><span>{\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">const<\/span> <span style=\"color:#66d9ef\">char<\/span> <span style=\"color:#f92672\">*<\/span>i_name, <span style=\"color:#f92672\">*<\/span>i_sep, <span style=\"color:#f92672\">*<\/span>i_arg, <span style=\"color:#f92672\">*<\/span>i_end, <span style=\"color:#f92672\">*<\/span>buf_end;\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">struct<\/span> file <span style=\"color:#f92672\">*<\/span>file;\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">int<\/span> retval;\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#75715e\">\/* Not ours to exec if we don&#39;t start with &#34;#!&#34;. *\/<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">if<\/span> ((bprm<span style=\"color:#f92672\">-&gt;<\/span>buf[<span style=\"color:#ae81ff\">0<\/span>] <span style=\"color:#f92672\">!=<\/span> <span style=\"color:#e6db74\">&#39;#&#39;<\/span>) <span style=\"color:#f92672\">||<\/span> (bprm<span style=\"color:#f92672\">-&gt;<\/span>buf[<span style=\"color:#ae81ff\">1<\/span>] <span style=\"color:#f92672\">!=<\/span> <span style=\"color:#e6db74\">&#39;!&#39;<\/span>))\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#f92672\">-<\/span>ENOEXEC;\n<\/span><\/span><span style=\"display:flex;\"><span>(...)\n<\/span><\/span><\/code><\/pre><\/div><p>There the check is!<\/p>\n<p>This function is very well-commented, detailing almost every step, so I recommend reading the source code <a href=\"https:\/\/github.com\/torvalds\/linux\/blob\/e48e99b6edf41c69c5528aa7ffb2daf3c59ee105\/fs\/binfmt_script.c#L34\">here<\/a>. Essentially, it reads the first line, parses the interpreter path (and any arguments), opens the interpreter&rsquo;s executable file, and assigns a reference to it to <code>bprm-&gt;interpreter<\/code>.<\/p>\n<p>Back in <code>exec_binprm<\/code>, it will check for if an interpreter (from script or misc binary formats) was found, then if so:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span>(...)\n<\/span><\/span><span style=\"display:flex;\"><span>ret <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">search_binary_handler<\/span>(bprm);\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">if<\/span> (ret <span style=\"color:#f92672\">&lt;<\/span> <span style=\"color:#ae81ff\">0<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">return<\/span> ret;\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#f92672\">!<\/span>bprm<span style=\"color:#f92672\">-&gt;<\/span>interpreter)\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">break<\/span>;\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>exec <span style=\"color:#f92672\">=<\/span> bprm<span style=\"color:#f92672\">-&gt;<\/span>file;\n<\/span><\/span><span style=\"display:flex;\"><span>bprm<span style=\"color:#f92672\">-&gt;<\/span>file <span style=\"color:#f92672\">=<\/span> bprm<span style=\"color:#f92672\">-&gt;<\/span>interpreter;\n<\/span><\/span><span style=\"display:flex;\"><span>bprm<span style=\"color:#f92672\">-&gt;<\/span>interpreter <span style=\"color:#f92672\">=<\/span> NULL;\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">exe_file_allow_write_access<\/span>(exec);\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">unlikely<\/span>(bprm<span style=\"color:#f92672\">-&gt;<\/span>have_execfd)) {\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">if<\/span> (bprm<span style=\"color:#f92672\">-&gt;<\/span>executable) {\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#a6e22e\">fput<\/span>(exec);\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#f92672\">-<\/span>ENOEXEC;\n<\/span><\/span><span style=\"display:flex;\"><span>\t}\n<\/span><\/span><span style=\"display:flex;\"><span>\tbprm<span style=\"color:#f92672\">-&gt;<\/span>executable <span style=\"color:#f92672\">=<\/span> exec;\n<\/span><\/span><span style=\"display:flex;\"><span>} <span style=\"color:#66d9ef\">else<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">fput<\/span>(exec);\n<\/span><\/span><span style=\"display:flex;\"><span>(...)\n<\/span><\/span><\/code><\/pre><\/div><p>If an interpreter <em>is<\/em> found, <code>bprm-&gt;file<\/code> is updated to point to the interpreter&rsquo;s file (replacing the script file), and the reference count for the original script file (<code>exec<\/code>) is decremented via <code>fput(exec)<\/code>.<\/p>\n<p>So, a single <code>execve<\/code> syscall on the script file triggers the kernel to: open the script, detect the <code>#!<\/code>, find and open the specified interpreter, and finally load and execute the <em>interpreter<\/em>, passing the script path as an argument. The kernel effectively replaces the process image with the interpreter&rsquo;s.<\/p>\n<h1 id=\"but-i-can-run-a-shellscript-without-\">But I can run a shellscript without #!<\/h1>\n<p>That&rsquo;s true. You don&rsquo;t really need #! to run shellscripts, but that&rsquo;s a fallback mechanism implemented by the shell, rather than the kernel, for example, if you try to strace the execution of a shell script lacking a shebang:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>$ cat test.sh\n<\/span><\/span><span style=\"display:flex;\"><span>echo <span style=\"color:#e6db74\">&#34;Hello, World!&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>$ .\/test.sh\n<\/span><\/span><span style=\"display:flex;\"><span>Hello, World!\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>$ strace .\/test.sh\n<\/span><\/span><span style=\"display:flex;\"><span>execve<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>, <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span><span style=\"color:#f92672\">]<\/span>, 0x7ffd9a1afcf0 \/* <span style=\"color:#ae81ff\">33<\/span> vars *\/<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> -1 ENOEXEC <span style=\"color:#f92672\">(<\/span>Exec format error<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>strace: exec: Exec format error\n<\/span><\/span><span style=\"display:flex;\"><span>+++ exited with <span style=\"color:#ae81ff\">1<\/span> +++\n<\/span><\/span><\/code><\/pre><\/div><p>It will fail with <code>ENOEXEC (Exec format error)<\/code>, since there&rsquo;s no indication of format for that file.<\/p>\n<p>To observe the shell&rsquo;s fallback behavior, we can trace a <em>new<\/em> shell instance invoking the script. We use <code>sh -c '.\/test.sh'<\/code> to ensure the child shell attempts the <code>execve<\/code>, rather than the parent shell interpreting the script directly. We&rsquo;ll use <code>strace<\/code> with <code>-f<\/code> (to follow child processes) and filter for key syscalls:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>strace -e trace<span style=\"color:#f92672\">=<\/span>execve,openat,read,close -f sh -c <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>If there&rsquo;s a <code>#!<\/code> in <code>test.sh<\/code>, it will return this:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>$ cat test.sh\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#75715e\">#! \/usr\/bin\/sh<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>echo <span style=\"color:#e6db74\">&#34;Hello, World!&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>$ strace -e trace<span style=\"color:#f92672\">=<\/span>execve,openat,read,close -f sh -c <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>execve<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;\/usr\/bin\/sh&#34;<\/span>, <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;sh&#34;<\/span>, <span style=\"color:#e6db74\">&#34;-c&#34;<\/span>, <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span><span style=\"color:#f92672\">]<\/span>, 0x7ffd51f86418 \/* <span style=\"color:#ae81ff\">33<\/span> vars *\/<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">(<\/span>...<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>strace: Process <span style=\"color:#ae81ff\">2522303<\/span> attached\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2522303<span style=\"color:#f92672\">]<\/span> execve<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>, <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span><span style=\"color:#f92672\">]<\/span>, 0x5ec40c994540 \/* <span style=\"color:#ae81ff\">33<\/span> vars *\/<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">(<\/span>...<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2522303<span style=\"color:#f92672\">]<\/span> openat<span style=\"color:#f92672\">(<\/span>AT_FDCWD, <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>, O_RDONLY<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">3<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2522303<span style=\"color:#f92672\">]<\/span> close<span style=\"color:#f92672\">(<\/span>3<span style=\"color:#f92672\">)<\/span>                  <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2522303<span style=\"color:#f92672\">]<\/span> read<span style=\"color:#f92672\">(<\/span>10, <span style=\"color:#e6db74\">&#34;#! \/usr\/bin\/sh\\necho \\&#34;Hello, Worl&#34;<\/span>..., 8192<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">36<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>Hello, World!\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2522303<span style=\"color:#f92672\">]<\/span> read<span style=\"color:#f92672\">(<\/span>10, <span style=\"color:#e6db74\">&#34;&#34;<\/span>, 8192<span style=\"color:#f92672\">)<\/span>        <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2522303<span style=\"color:#f92672\">]<\/span> +++ exited with <span style=\"color:#ae81ff\">0<\/span> +++\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">(<\/span>...<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>If no <code>#!<\/code> is found:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>$ cat test.sh\n<\/span><\/span><span style=\"display:flex;\"><span>echo <span style=\"color:#e6db74\">&#34;Hello, World!&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>$ strace -e trace<span style=\"color:#f92672\">=<\/span>execve,openat,read,close -f sh -c <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>execve<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;\/usr\/bin\/sh&#34;<\/span>, <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;sh&#34;<\/span>, <span style=\"color:#e6db74\">&#34;-c&#34;<\/span>, <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span><span style=\"color:#f92672\">]<\/span>, 0x7ffd4de7e798 \/* <span style=\"color:#ae81ff\">33<\/span> vars *\/<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">(<\/span>...<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>strace: Process <span style=\"color:#ae81ff\">2524967<\/span> attached\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> execve<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>, <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span><span style=\"color:#f92672\">]<\/span>, 0x651ce522f540 \/* <span style=\"color:#ae81ff\">33<\/span> vars *\/<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> -1 ENOEXEC <span style=\"color:#f92672\">(<\/span>Exec format error<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> openat<span style=\"color:#f92672\">(<\/span>AT_FDCWD, <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>, O_RDONLY|O_NOCTTY<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">3<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> read<span style=\"color:#f92672\">(<\/span>3, <span style=\"color:#e6db74\">&#34;echo \\&#34;Hello, World!\\&#34;\\n&#34;<\/span>, 128<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">21<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> close<span style=\"color:#f92672\">(<\/span>3<span style=\"color:#f92672\">)<\/span>                  <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> execve<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;\/bin\/sh&#34;<\/span>, <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;\/bin\/sh&#34;<\/span>, <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span><span style=\"color:#f92672\">]<\/span>, 0x651ce522f540 \/* <span style=\"color:#ae81ff\">33<\/span> vars *\/<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">(<\/span>...<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> openat<span style=\"color:#f92672\">(<\/span>AT_FDCWD, <span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>, O_RDONLY<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">3<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> close<span style=\"color:#f92672\">(<\/span>3<span style=\"color:#f92672\">)<\/span>                  <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> read<span style=\"color:#f92672\">(<\/span>10, <span style=\"color:#e6db74\">&#34;echo \\&#34;Hello, World!\\&#34;\\n&#34;<\/span>, 8192<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">21<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>Hello, World!\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> read<span style=\"color:#f92672\">(<\/span>10, <span style=\"color:#e6db74\">&#34;&#34;<\/span>, 8192<span style=\"color:#f92672\">)<\/span>        <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">[<\/span>pid 2524967<span style=\"color:#f92672\">]<\/span> +++ exited with <span style=\"color:#ae81ff\">0<\/span> +++\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f92672\">(<\/span>...<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>After filtering the output, it&rsquo;s clear that in the first case (with shebang), it&rsquo;s doing the initial <code>execve<\/code>for the shell instance we&rsquo;re creating, then another <code>execve<\/code> for <code>test.sh<\/code> and do all the process we described before. In the second case (no shebang), the child process&rsquo;s <code>execve<\/code> on <code>.\/test.sh<\/code> fails with <code>ENOEXEC<\/code>. The parent shell (<code>sh -c<\/code>) catches this error. It then likely uses <code>openat<\/code> and <code>read<\/code> to examine the file. Detecting it&rsquo;s likely a shell script, it then explicitly executes <code>\/bin\/sh .\/test.sh<\/code> via a <em>second<\/em> <code>execve<\/code> call.<\/p>\n<h1 id=\"bonus-dealing-with-permissions\">Bonus: Dealing with Permissions<\/h1>\n<p>We found out that the kernel runs the scripts through its own <code>execve<\/code> syscall assuming it contains a <code>#!<\/code> <em>and<\/em> has execute permission set. But where is that permission checked?<\/p>\n<p>If we try to invoke a script that doesn&rsquo;t have execute permissions, we&rsquo;ll get this:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>$ .\/test.sh\n<\/span><\/span><span style=\"display:flex;\"><span>zsh: permission denied: .\/test.sh\n<\/span><\/span><\/code><\/pre><\/div><p>But it doesn&rsquo;t give off too much. However, if we <code>strace<\/code> it again:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-shell\" data-lang=\"shell\"><span style=\"display:flex;\"><span>$ strace .\/test.sh\n<\/span><\/span><span style=\"display:flex;\"><span>execve<span style=\"color:#f92672\">(<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span>, <span style=\"color:#f92672\">[<\/span><span style=\"color:#e6db74\">&#34;.\/test.sh&#34;<\/span><span style=\"color:#f92672\">]<\/span>, 0x7ffd2b4a52d0 \/* <span style=\"color:#ae81ff\">33<\/span> vars *\/<span style=\"color:#f92672\">)<\/span> <span style=\"color:#f92672\">=<\/span> -1 EACCES <span style=\"color:#f92672\">(<\/span>Permission denied<span style=\"color:#f92672\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>strace: exec: Permission denied\n<\/span><\/span><span style=\"display:flex;\"><span>+++ exited with <span style=\"color:#ae81ff\">1<\/span> +++\n<\/span><\/span><\/code><\/pre><\/div><p>It returns the error code and description from the syscall: <code>EACCES (Permission denied)<\/code>. <strong>Error codes are always a good start point<\/strong>. Searching for <code>EACCES<\/code> in <code>fs\/exec.c<\/code> leads us to the check within the <code>do_open_execat<\/code> function<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">WARN_ON_ONCE<\/span>(<span style=\"color:#f92672\">!<\/span><span style=\"color:#a6e22e\">S_ISREG<\/span>(<span style=\"color:#a6e22e\">file_inode<\/span>(file)<span style=\"color:#f92672\">-&gt;<\/span>i_mode)) <span style=\"color:#f92672\">||<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a6e22e\">path_noexec<\/span>(<span style=\"color:#f92672\">&amp;<\/span>file<span style=\"color:#f92672\">-&gt;<\/span>f_path))\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#a6e22e\">ERR_PTR<\/span>(<span style=\"color:#f92672\">-<\/span>EACCES);\n<\/span><\/span><\/code><\/pre><\/div><p>Tracing the call stack back from <code>do_open_execat<\/code>, we find it&rsquo;s called during the setup of the <code>bprm<\/code> structure within <code>do_execveat_common<\/code>, the entrypoint to the <code>execve<\/code> syscall:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-c\" data-lang=\"c\"><span style=\"display:flex;\"><span>bprm <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">alloc_bprm<\/span>(fd, filename, flags);\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">IS_ERR<\/span>(bprm)) {\n<\/span><\/span><span style=\"display:flex;\"><span>\tretval <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">PTR_ERR<\/span>(bprm);\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#66d9ef\">goto<\/span> out_ret;\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p>Now, understanding how <code>path_noexec<\/code> checks the <em>execute<\/em> permission in the file involves a lot of other stuff like understanding how the kernel deals with the filesystem. But that&rsquo;ll be a future post.<\/p>\n<h3 id=\"edit\">EDIT<\/h3>\n<ul>\n<li>Switched the usage of <code>where<\/code> for <code>which<\/code>, since it&rsquo;s a zsh-only command. These add to my list of confusing commands just like <code>adduser<\/code> and <code>useradd<\/code>. Thanks <strong>u\/pihkal<\/strong>.<\/li>\n<li>I corrected calling ELF the &ldquo;traditional binary format&rdquo; of linux to &ldquo;regular binary format&rdquo;. Although ELF has been the regular format for so many years, calling it traditional was maybe not correct. Thanks <strong>\/u\/Admqui<\/strong>. Some material on ELF and the old <code>a.out<\/code> format:\n<ul>\n<li><a href=\"https:\/\/web.archive.org\/web\/20040713171954\/http:\/\/www.ibiblio.org\/pub\/historic-linux\/distributions\/slackware\/3.9\/docs\/ELF-HOWTO\">The Linux ELF HOWTO (1996)<\/a><\/li>\n<li><a href=\"https:\/\/lwn.net\/Articles\/631631\/\">How programs get run: ELF binaries (2015)<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Join the discussion on:<\/p>\n<ul>\n<li><a href=\"https:\/\/news.ycombinator.com\/item?id=43646698\">HackerNews<\/a><\/li>\n<li><a href=\"https:\/\/www.reddit.com\/r\/programming\/comments\/1jukuv3\/demystifying_the_shebang_kernel_adventures\/\">Reddit<\/a>.<\/li>\n<li><a href=\"https:\/\/hackaday.com\/2025\/04\/11\/tracing-the-how-the-linux-kernel-handles-the-shebang\/\">Hackaday<\/a><\/li>\n<\/ul>\n"},{"id":"https:\/\/crocidb.com\/post\/on-programming-with-ai-assistants-vibe-coding\/","title":"Treat Your LLM Like a Junior Dev, Not an Oracle","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/on-programming-with-ai-assistants-vibe-coding\/"}},"updated":"2025-03-26T00:00:00Z","summary":"<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Vibe_coding\">Vibe Coding<\/a>, the new hot buzzword, describes the act of programming <em>through<\/em> an LLM, rather than <em>with<\/em> it. It&rsquo;s akin to using AI to <em>compile<\/em> natural language (like English) into a programming language. This can be empowering; suddenly anyone can create programs that make the computer work for them better. Of course, there are several problems with this, such as LLMs generating literal copies of code from open-source projects, without any license warning, or outright wrong code. I think that taking whatever output an LLM gives and using it directly in a product is not a good idea. However, if it&rsquo;s for a simple tool you&rsquo;re creating to help you do something, it&rsquo;s absolutely valid in my opinion.<\/p>\n<p>Instead, I prefer to treat LLMs as a junior developer. This junior developer is knowledgeable, capable of doing extensive research very quickly, and providing results (mostly quantitative results), but lacks real-world experience. I wouldn&rsquo;t trust it with any real code, but I gladly take the opportunity to ask for assistance. This includes asking about standard library functionality, inquiring about suitable 3rd party libraries to solve a problem, asking for the correct usage of a module, or seeking help understanding an issue. Sometimes I might not even realize that I&rsquo;m dealing with a classic problem that&rsquo;s been discussed and has several proposed solutions. LLMs are also pretty good at diagnosing issues, obscure compiler errors, and weird behaviors.<\/p>\n<p>I think that the reason you use generative AI is to do things you think it would do a better or faster job than you. That means that either you think you&rsquo;re not good enough to do it, or you need the extra help to get it done. I think it&rsquo;s important to understand the difference. I can see how much harm generative AI can do (and is already doing) with people delegating creative work to it, but I try to focus on the transformative effect on education and learning. Using it as an assistant that helps you get things done and learn from it is the single most important distinction that can make or break the positive impact of AI.<\/p>\n<p>I&rsquo;ve been using Copilot Chat for a few months now as an occasional pair-programming buddy. I found that code completion is not really for me; when I&rsquo;m writing code, I&rsquo;m in <em>write<\/em> mode, if I have to stop and read the suggestion it has made for me, it often breaks my chain of thought, not to mention the extra mental effort to figure if that code is copyrighted or has other licensing issues. This isn&rsquo;t to say that I never use generated code, I&rsquo;ve had cases where I asked the assistant to change code for me or finish some repetitive code, like asking it to complete the content type function on my <a href=\"https:\/\/github.com\/CrociDB\/ush\">webserver implementation<\/a>:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-haskell\" data-lang=\"haskell\"><span style=\"display:flex;\"><span><span style=\"color:#66d9ef\">data<\/span> <span style=\"color:#66d9ef\">ContentType<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">TextPlain<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">ApplicationOctetStream<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">TextHTML<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">TextJavascript<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">TextCSS<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">ImageJPEG<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">ImagePNG<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">AudioMPEG<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">AudioOgg<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">Other<\/span> <span style=\"color:#66d9ef\">String<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f92672\">|<\/span> <span style=\"color:#66d9ef\">Undefined<\/span> <span style=\"color:#66d9ef\">String<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#66d9ef\">deriving<\/span> (<span style=\"color:#66d9ef\">Show<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#f92672\">::<\/span> <span style=\"color:#66d9ef\">ContentType<\/span> <span style=\"color:#f92672\">-&gt;<\/span> <span style=\"color:#66d9ef\">String<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">TextPlain<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;text\/plain&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> (<span style=\"color:#66d9ef\">Undefined<\/span> <span style=\"color:#66d9ef\">_<\/span>) <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;text\/plain&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>I just fed this asked it to complete all the possible values of <code>contentTypeToString<\/code> and, in a matter of seconds, I had:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-Haskell\" data-lang=\"Haskell\"><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#f92672\">::<\/span> <span style=\"color:#66d9ef\">ContentType<\/span> <span style=\"color:#f92672\">-&gt;<\/span> <span style=\"color:#66d9ef\">String<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">TextPlain<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;text\/plain&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">ApplicationOctetStream<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;application\/octet-stream&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">TextHTML<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;text\/html&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">TextJavascript<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;text\/javascript&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">TextCSS<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;text\/css&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">ImageJPEG<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;image\/jpeg&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">ImagePNG<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;image\/png&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">AudioMPEG<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;audio\/mpeg&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> <span style=\"color:#66d9ef\">AudioOgg<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;audio\/ogg&#34;<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> (<span style=\"color:#66d9ef\">Other<\/span> str) <span style=\"color:#f92672\">=<\/span> str\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a6e22e\">contentTypeToString<\/span> (<span style=\"color:#66d9ef\">Undefined<\/span> <span style=\"color:#66d9ef\">_<\/span>) <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#34;text\/plain&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>A key advantage of this approach, particularly when combined with good editor integration, is the minimization of context switching, consider the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Doorway_effect\">Doorway Effect<\/a>, where changing environments can cause you to forget details relevant to the previous environment. The same applies your screen windows. Constantly switching between my code editor and a browser window to search for solutions disrupts my mental flow. Being able to open a chat with the LLM and ask about what&rsquo;s going on without even taking my hands off the keyboard has been incredibly beneficial.<\/p>\n<p>It&rsquo;s not about raw productivity; I&rsquo;m not overly concerned with speed. Instead, it&rsquo;s about <em>quality of life<\/em>. By staying within the editor and interacting with the LLM directly, I minimize mental interference and can think more clearly about the LLM&rsquo;s suggestions. This reduced cognitive load leads to better, more considered decisions about how to implement the solution and significantly less stress overall.<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/retrospective-2024\/","title":"Retrospective 2024","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/retrospective-2024\/"}},"updated":"2025-01-15T08:33:51Z","summary":"<p>I was pretty excited to write this year&rsquo;s retrospective because it was pretty intense for me. Especially compared to the previous ones. As I <a href=\"https:\/\/crocidb.com\/post\/retrospective-2023\/\">posted here<\/a>, 2023 wasn&rsquo;t particularly productive. This one was much better, although not too smooth.<\/p>\n<p>I like to start it with a big picture. Last year, one of the highlights of this section was my recent notetaking habit. One round-around-the-sun later and I can confirm I&rsquo;ve been doing it a lot more and I can already see the benefits. I started using Obsidian for general notes and doing some sort of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Zettelkasten\">Zettelkasten<\/a> too. So now I have a git repo with my notes where I can sync anywhere and keep it safe.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_notesgraph.jpg\" alt=\"Obsidian Graph\"  \/>\n    <p class=\"image-description\">Obsidian Graph<\/p>\n<\/div>\n<\/p>\n<p>The main advantage of that note taking technique is connecting different notes and subjects together and Obsidian offers a really nice graph view. It does require some good amount of maintenance in the notes to keep them updated and connected to other ideas, but I think that&rsquo;s also good to keep some subjects fresh in mind. I feel like every time I go through them I have something new to add or some more insights on it. Also, I find I can develop the ideas much better with them written down like that than just in my head.<\/p>\n<p>I&rsquo;m still learning and still improving my technique, but I think that was incredibly mind opening for me. I never had a notetaking habit, I went through school without it, partially through university too. Maybe my brain was faster and more efficient back then, but I wonder how much more I could have learned if I did it.<\/p>\n<p>Obsidian is not my only notetaking tool, though. I always start with a quick google keep note. And I use it a lot. Every moment I think there&rsquo;s something interesting happening or going through my mind, I write a quick note about it. Then, later, I get these quick ideas and do some research, think a bit harder and turn into a more concise idea I can add to my Obsidian. I feel I can finally develop ideas I have even when I&rsquo;m about to sleep.<\/p>\n<p>This year is also the year that I embraced NeoVim as my main editor outside work. I spent a lot of time customizing it, setting up plugins and learning how to effectively navigate the text with motions. It&rsquo;s been a wild ride of frustration and many many bursts of joy when I get the correct combination of keystrokes. I wrote about <a href=\"https:\/\/crocidb.com\/post\/from-ides-to-the-terminal\/\">my code editor history here<\/a>.<\/p>\n<p>And since I already started with the topic of programming, I have to say it was a weird year. I had some sort of burnout at some point, but nonetheless it was one of the years I programmed the most in my life, since the first time I wrote a line of code, around 2002! And it was in so many programming languages that I&rsquo;ll have to list here:<\/p>\n<ul>\n<li><strong>C++<\/strong>: most of my work activity<\/li>\n<li><strong>Javascript<\/strong>: as I&rsquo;ll say later, worked on some JS games and other projects<\/li>\n<li><strong>Lua<\/strong>: a big part of customizing NeoVim is done in Lua and I think I&rsquo;m enjoying it a lot more<\/li>\n<li><strong>Haskell<\/strong>: since my <a href=\"https:\/\/crocidb.com\/post\/i-wrote-a-webserver-in-haskell\/\">Web Server<\/a>, my first real project I ever wrote in Haskell, I decided to go for it in this year&rsquo;s Advent of Code and it was definitely a good idea<\/li>\n<li><strong>Zig<\/strong>: I&rsquo;ve been trying to learn Zig for quite some time, but since CodeCrafters offered me the possibility, I took it and did a project in it. Honestly, I thought it was a lot more <em>complicated<\/em> than I anticipated. I&rsquo;ll post about it eventually.<\/li>\n<li><strong>Fennel<\/strong>: last couple years I wrote lots of Racket, this year&rsquo;s Lisp was Fennel, a lisp that runs in Lua. I used it for my game <a href=\"https:\/\/crocidb.com\/post\/postmortem-clone-stacking\/\">Clone Stacking<\/a>, for Ludum Dare 55.<\/li>\n<li><strong>Dart<\/strong>: I started a mobile app for personal use and I decided to use Flutter, because it looked simple, so I did have my time with Dart. Can&rsquo;t really say I enjoy it, but hey, it&rsquo;s a tool.<\/li>\n<li><strong>Python<\/strong>: talking about tools, I always say python is like pliers. I don&rsquo;t particularly like using <em>pliers<\/em>, but there are moments where I need to use it and I think I can use it pretty well.<\/li>\n<\/ul>\n<p>There were other stuff too, like <strong>GDScript<\/strong> for some Godot tests that I did, and I won&rsquo;t even mention the ungodly amount of <strong>HTML<\/strong> and <strong>CSS<\/strong> I did this year. I just think it was particularly a good amount of programming concepts going through and I&rsquo;m happy I did it.<\/p>\n<h2 id=\"blog\">Blog<\/h2>\n<p>I&rsquo;m happy I kept this blog more alive this year than ever before, by a slight margin. There was a total of <strong>11<\/strong> new posts, ignoring retrospective ones.<\/p>\n<ul>\n<li><a href=\"https:\/\/crocidb.com\/post\/camera-zeiss-ikon-nettar-517-2\/\">Old Camera: Zeiss Ikon Nettar 517\/2<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/whats-happening-to-digital-books\/\">What&rsquo;s happening to Digital Books?<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/capturing-screen-timelapses-ubuntu-gnome-wayland\/\">Capturing screen timelapses on Ubuntu (Gnome on Wayland)<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/genuary-2024\/\">My Genuary 2024 Entries<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/postmortem-clone-stacking\/\">Postmortem: Clone Stacking<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/from-ides-to-the-terminal\/\">From IDEs to the Terminal<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/i-wrote-a-webserver-in-haskell\/\">I Wrote a Webserver in Haskell<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/zig-slices-and-how-not-to-use-it\/\">Zig Slices and how not to use it<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/thoughts-about-reading-on-long-commutes\/\">Thoughts about reading on long commutes<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/transferring-browser-session-oasis-concert\/\">Transferring Browser Session: Oasis Concert Tickets<\/a><\/li>\n<li><a href=\"https:\/\/crocidb.com\/post\/vi-vim-nvim\/\">Vi, Vim and NeoVim<\/a><\/li>\n<\/ul>\n<p>I actually have so many others that are not complete, but they&rsquo;ll make the light of day at some point in 2025.<\/p>\n<h2 id=\"game-jams\">Game Jams<\/h2>\n<p>I attended to the two <strong>Ludum Dare<\/strong>s this year, 55 and 56.<\/p>\n<h3 id=\"clone-stacking\">Clone Stacking<\/h3>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_clonestacking.jpg\" alt=\"Clone Stacking\"  \/>\n    <p class=\"image-description\">Clone Stacking<\/p>\n<\/div>\n<\/p>\n<p>Main post is <a href=\"https:\/\/crocidb.com\/post\/postmortem-clone-stacking\/\">here<\/a>, but shortly, it&rsquo;s a puzzle game based on the mechanics of stacking clones of yourself. I really liked the mechanics and I got a pretty good position in <em>Fun<\/em> as a result. So much I&rsquo;m planning to make a version of this game for Game Boy.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_clonestracking_results.jpg\" alt=\"Results\"  \/>\n    <p class=\"image-description\">Results<\/p>\n<\/div>\n<\/p>\n<h3 id=\"fish-rescue\">Fish Rescue<\/h3>\n<div class=\"embed video-player\">\n<iframe class=\"youtube-player\" type=\"text\/html\" width=\"640\" height=\"385\"\n  src=\"\/\/www.youtube.com\/embed\/ow8LHb8VY5g\" allowfullscreen\n  frameborder=\"0\"\/>\n<\/iframe>\n<\/div>\n\n<p>A chill game about leading a group of fish. Simpler mechanics, but more pleasing visuals and movement. I&rsquo;m still to write a post-mortem about it, but a quick summary would be: I like how much attention to details I put into this one, but the gameplay mechanics were a bit too simple, maybe even boring, unfortunately. The presentation in terms of graphics, music and sounds, I love it. Unfortunately it didn&rsquo;t go that well compared to the previous entry, but I&rsquo;m proud nonetheless.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_fishrescue.jpg\" alt=\"Fish Rescue\"  \/>\n    <p class=\"image-description\">Fish Rescue<\/p>\n<\/div>\n<\/p>\n<h1 id=\"professional\">Professional<\/h1>\n<p>Despite some metal health issues, I am very proud of being part of the Audio Team of XDefiant, a fast-paced arena shooter released (and also killed) by Ubisoft. Unfortunately the game also got cancelled, along with two offices being shut and hundreds of people laid off, which I&rsquo;m deeply sorry for. Nonetheless, I can&rsquo;t state enough how much I enjoyed the game and how glad I am for being part of it.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_xdefiant.jpg\" alt=\"XDefiant\"  \/>\n    <p class=\"image-description\">XDefiant<\/p>\n<\/div>\n<\/p>\n<p>Here&rsquo;s a presentation on this game&rsquo;s audio by our Audio Director:<\/p>\n<div class=\"embed video-player\">\n<iframe class=\"youtube-player\" type=\"text\/html\" width=\"640\" height=\"385\"\n  src=\"\/\/www.youtube.com\/embed\/dcPwI_-YVnc\" allowfullscreen\n  frameborder=\"0\"\/>\n<\/iframe>\n<\/div>\n\n<h1 id=\"travel--photography\">Travel &amp; Photography<\/h1>\n<p>In the beginning of summer, I did some cool photos experimenting with my custom full-spectrum Fuji X-A5 camera with a Samyang 12mm lens and I really liked it, especially because of the surreal colors that I could pull out of it.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_photo1.jpg\" alt=\"Experiment with my full-spectrum setup\"  \/>\n    <p class=\"image-description\">Experiment with my full-spectrum setup<\/p>\n<\/div>\n<\/p>\n<p>So I knew what to take to our summer holidays in Portugal. We were in Porto, Lisbon, Aveiro and mostly Torreira. It was my first time in this beautiful country and I enjoyed it quite a lot. The food and the landscapes were even better than I anticipated. Despite the fact that I&rsquo;m extremely disappointed that they still treat their invasion and colonization of Brazil as a &ldquo;discovery&rdquo; in popular culture, it&rsquo;s good to be in a place that shares the same language as mine.<\/p>\n<p>In the end, I shot more film than the experimental full-spectrum gear, but nonetheless I think I managed to get some cool-looking shots with it.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_photo2.jpg\" alt=\"Experiment with my full-spectrum setup\"  \/>\n    <p class=\"image-description\">Experiment with my full-spectrum setup<\/p>\n<\/div>\n<\/p>\n<p>There were more film photos in Portugal, though.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_photo3.jpg\" alt=\"Analog photos in Portugal\"  \/>\n    <p class=\"image-description\">Analog photos in Portugal<\/p>\n<\/div>\n<\/p>\n<p>I also did some quick trips to Brussels, Copenhagen and London and I have posted the photos on my Instagram.<\/p>\n<h1 id=\"drawing--art\">Drawing &amp; Art<\/h1>\n<p>I&rsquo;m getting more and more acquainted with the concept of Art. Unfortunately, I wasn&rsquo;t raised with any type of incentive to make art or even to appreciate it. We were poor and my parents had many other things to worry about. It took me decades to actually understand what Art is, how to enjoy it and more recently that I can make it too. First of all is that it doesn&rsquo;t need to be <em>pretty<\/em>, it doesn&rsquo;t need to be <em>perfect<\/em>, it doesn&rsquo;t need to be <em>exact<\/em>. And you don&rsquo;t need to be <em>Van Gogh<\/em> to paint or <em>Bach<\/em> to compose.<\/p>\n<p>For some reason, deep inside, I thought that I <em>couldn&rsquo;t draw<\/em>, even thought I barely ever tried. Somehow we have this wrong idea that an artist is a person with divine skills, that was completely given on birth. The first misconception is that technique and skill don&rsquo;t make an artist. But I&rsquo;m improving my technique while trying to make art with what I have.<\/p>\n<p>I started drawing and painting with watercolor and a whole world of art opened to me. I feel more expressive, more free and more inspired. I have some sketchbooks at home and one that I carry around with a good liner. That aligned to the habit of taking notes, I feel like having a small notebook and a pen with me all the times is just necessary.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_painting1.jpg\" alt=\"Made after a photo I took the previous year\"  \/>\n    <p class=\"image-description\">Made after a photo I took the previous year<\/p>\n<\/div>\n<\/p>\n<p>As a result, I&rsquo;m focusing my thinking way more on creating cohesive things that don&rsquo;t need to be <em>products<\/em>. Stuff that is not about <em>making money<\/em>. So much of my thinking was going towards that direction, and guess what, I&rsquo;m not rich. The takeaway is more art projects than art pieces. I don&rsquo;t care that much for good singular photos than I care for a project that ties photos together centered on an idea. Not about one drawing I make, but how I can tell a story by combining drawings together.<\/p>\n<h1 id=\"music-production\">Music Production<\/h1>\n<p>As part of my <em>blooming<\/em> in art, I&rsquo;ve finally started learning how to record instruments and produce a song. In fact, I already have an album (or an EP) concept in progress. It&rsquo;s an instrumental Guitar + Synth project, I already have a few songs complete, just waiting to be mixed. I&rsquo;m still not set on how many songs I want for this project, more than I currently have, but the pressure of making it better may be keeping me from finishing it.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/r2024_music.png\" alt=\"Working on some music\"  \/>\n    <p class=\"image-description\">Working on some music<\/p>\n<\/div>\n<\/p>\n<p>I bought an online course on mixing, since it seems to be one of the hardest parts for me. Hopefully that will be out in 2025!<\/p>\n<h1 id=\"other\">Other<\/h1>\n<ul>\n<li>I did an incredible online workshop with the cartoon queen of Brazil, Laerte and her comic book artist son, Rafael Coutinho. It was a total of 4 online meetings through Zoom where they made a short comic book. The first night they came with the story, then followed by some <em>thumbnails<\/em>, then sketching and finally tracing on the last night. Of course they worked way more days than that, and by the end of the program, they still had a lot to do, but I&rsquo;m really really looking forward to have that book in my hands and finally read the story. I feel like I learned so much about comic book production. So much I even have ideas for projects.<\/li>\n<li>I&rsquo;ve always liked to cook, but not necessarily fancy dishes. Just straight up healthy fresh food. Lots of vegetable stews and soups and Brazilian traditional dishes. The pleasure of going home after work and focusing on making delicious and healthy dinner is something I never felt that much before. I guess I&rsquo;m just getting old, or it&rsquo;s inspiration from so much good food I had out this year.<\/li>\n<li>I started more books than I finished, and most of the ones I did finish were comics\/graphic novels.<\/li>\n<li>I&rsquo;m getting more aware of my attention problems. One might just say it&rsquo;s ADHD, but I&rsquo;m not fully convinced. We all are living in a world full of <em>attention offenders<\/em>, that are constantly training our brains to seek the quickest rewards possible. Reading a 400-page book is getting harder and harder. In that sense, I&rsquo;m more open do drop books that don&rsquo;t interest me that much, focus on the ones that can grab me more, thus training long reading sessions.<\/li>\n<\/ul>\n<h1 id=\"conclusion\">Conclusion<\/h1>\n<p>I get so excited by putting together everything that made me grow during the year, I think that really motivates me to do even better. I don&rsquo;t know what 2025 will bring, but I&rsquo;m certainly sure that comes with a lot more Art for me, music composing and producing, drawing and painting, gamejams, and programming. Stay around for more!<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/vi-vim-nvim\/","title":"Vi, Vim and NeoVim","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/vi-vim-nvim\/"}},"updated":"2024-11-06T00:00:00Z","summary":"<p>When I first heard of <strong>Vi<\/strong>, it was actually <strong>Vim<\/strong>. But people liked to call it <em>V-I<\/em>, so it was common for people to add aliases to call it <em>vi<\/em>: <code>alias vi=vim<\/code> was a common line in people&rsquo;s shell profiles.<\/p>\n<p>After the current <strong>NeoVim<\/strong> craze, with lots of influencer creating content on it and tons of new users (<a href=\"https:\/\/crocidb.com\/post\/from-ides-to-the-terminal\">myself included<\/a>), I see the interesting effect of people creating aliases to invoke <strong>NeoVim<\/strong> with <code>vim<\/code>. <code>alias vim=nvim<\/code>, (myself incldued).<\/p>\n<p>I wonder when the next big editor comes, in a decade or so, let&rsquo;s call it <strong>UltraNeoVim<\/strong>, <code>unvim<\/code>, people will start invoking it with <code>nvim<\/code>. <code>alias nvim=unvim<\/code>.<\/p>\n"},{"id":"https:\/\/crocidb.com\/post\/transferring-browser-session-oasis-concert\/","title":"Transferring Browser Session: Oasis Concert Tickets","link":{"@attributes":{"href":"https:\/\/crocidb.com\/post\/transferring-browser-session-oasis-concert\/"}},"updated":"2024-09-21T00:00:00Z","summary":"<p>So Oasis got back together for a tour. The tickets were supposed to be out at 31th of August, at 9am London time, 10am my time in Germany. I sit down at the computer half an hour earlier and I get in a queue, that I didn&rsquo;t know at the moment, but would take a long time.<\/p>\n<p>Oasis is very nolstalgic for me, and since my friend <a href=\"https:\/\/teodutra.com\/\">Teo Dutra<\/a>, who lives in Vancouver, was also up to watch and eventually meet in London next year, I told him &ldquo;don&rsquo;t worry, I&rsquo;ll get us the tickets&rdquo;, especially because the timezone in west Canada wouldn&rsquo;t be favorable for him.<\/p>\n<p>Two hours and a half later, it was still like that, less than halfway through:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/oasis_queue.png\" alt=\"a Queue\"  \/>\n    <p class=\"image-description\"><em>a Queue<\/em><\/p>\n<\/div>\n<\/p>\n<p>But I needed to go out help a friend move, so I opened it on my phone and&hellip; humm&hellip; another place in the queue:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/oasis_queue2.png\" alt=\"On my phone\"  \/>\n    <p class=\"image-description\">On my phone<\/p>\n<\/div>\n<\/p>\n<p>That would take another two and a half hours just to reach the point I was in my computer. So the chance I&rsquo;ll get tickets is even lower.<\/p>\n<p>But they do have a queue ID, what if I could transfer my session to my phone? So I opened the DevTools and found the cookies defined at the request headers for the page:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/oasis_cookie.png\" alt=\"Capturing the cookies\"  \/>\n    <p class=\"image-description\">Capturing the cookies<\/p>\n<\/div>\n<\/p>\n<p>It does look promising, I can find the queue id somewhere in that crazy amount of characters. Also I assume the session ID is also there somewhere, I don&rsquo;t understand exactly how webservers keep remote user sessions, but there must be with the cookie, right?<\/p>\n<p>Ok, next step, how do I set my phone&rsquo;s cookies manually? I found a few extensions, but they wouldn&rsquo;t work on phone. But hey, I can access the DevTools remotely, I&rsquo;m familiar with it, been working on a Javascript game for a while now.<\/p>\n<p>I found this <a href=\"https:\/\/superuser.com\/questions\/186609\/how-do-i-transfer-copy-cookies-from-one-browser-to-another-or-same-browser-fro\">answer online<\/a> that explains how to do it, and it turns out it&rsquo;s really easy:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"display:flex;\"><span><span style=\"color:#e6db74\">&#39;[cookies]&#39;<\/span>.<span style=\"color:#a6e22e\">split<\/span>(<span style=\"color:#e6db74\">&#39;;&#39;<\/span>).<span style=\"color:#a6e22e\">forEach<\/span>(<span style=\"color:#a6e22e\">c<\/span> =&gt; document.<span style=\"color:#a6e22e\">cookie<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#a6e22e\">c<\/span>);\n<\/span><\/span><\/code><\/pre><\/div><p>If I just run the code above with all those cookies from my computer in the console, it will set the mobile cookies.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/oasis_cookie2.png\" alt=\"Transferring the cookies\"  \/>\n    <p class=\"image-description\">Transferring the cookies<\/p>\n<\/div>\n<\/p>\n<p>It worked perfectly. But one Victory a day is enough, huh? Many hours later, as I was helping my friend carry her stuff up three stories, I got to the beginning of the queue. Just to be&hellip; thrown to <strong>another<\/strong> queue!<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/oasis_second_queue.png\" alt=\"the Queue\"  \/>\n    <p class=\"image-description\"><em>the Queue<\/em><\/p>\n<\/div>\n<\/p>\n<p>Turns out the first queue was just the queue for queue to shop. Or as the system calls <em><strong>a<\/strong> queue<\/em> and <em><strong>the<\/strong> queue<\/em>. Couple more hours and another update:<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/oasis_second_queue2.png\" alt=\"Finally&hellip; I guess?\"  \/>\n    <p class=\"image-description\">Finally&hellip; I guess?<\/p>\n<\/div>\n<\/p>\n<p>At this point I had just a tiny bit of hope. I didn&rsquo;t even know what the prices would be like. There was a chance I&rsquo;d be presented with \u00a3500+ and I would just give up in the end. It was over 5pm and I was exhausted. Just to be surprised with this, a never-ending spinning wheel.<\/p>\n<p><div class=\"image-div\">\n    <img src=\"images\/oasis_loading.png\" alt=\"I think at this point all the tickets were gone\"  \/>\n    <p class=\"image-description\">I think at this point all the tickets were gone<\/p>\n<\/div>\n<\/p>\n<p>Fuck Oasis. At least I got to be rewarded with one of the best Schnitzels in Cologne and lots of cold K\u00f6lsch beer. I hardly ever go to big concerts like that and that&rsquo;s one of the reasons.<\/p>\n"}]}