<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>dharmik parmar's blog</title>
    <description>Personal blog about systems programming, Linux, and various interests</description>
    <link>https://dhrm1k.github.io/</link>
    <atom:link href="https://dhrm1k.github.io/feed.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Sun, 23 Aug 2026 00:00:00 GMT</lastBuildDate>
    <language>en-us</language><item>
      <title>found a letter</title>
      <description><![CDATA[&lt;p&gt;in the month of july, i and rahul visited a book fair. after the fair, we went to have a milkshake and were thrilled to find a letter apparently dated february 16 tucked inside the pages of &amp;quot;the god delusion&amp;quot; by richard dawkins. we thought it was the 16th of january, but we were wrong. i already posted it on x, but found it interesting enough to dedicate a blog post to it.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; width=&quot;60%&quot; height=&quot;60%&quot; data-dnt=&quot;true&quot; data-theme=&quot;dark&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I (and &lt;a href=&quot;https://twitter.com/19Silica?ref_src=twsrc%5Etfw&quot;&gt;@19Silica&lt;/a&gt;) visited a book fair and were surprised to find a letter tucked inside the pages, apparently dated January 16, 1992. &lt;a href=&quot;https://t.co/tG2vdKvU3k&quot;&gt;pic.twitter.com/tG2vdKvU3k&lt;/a&gt;&lt;/p&gt;&amp;mdash; dharmik parmar (@dhrm1k) &lt;a href=&quot;https://twitter.com/dhrm1k/status/1684581460089667585?ref_src=twsrc%5Etfw&quot;&gt;July 27, 2023&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;  
&lt;p&gt;rahul took the trouble to digitize the letter in a google doc for others to read. here&#39;s the &lt;a href=&quot;https://docs.google.com/document/d/1wuADZoLhhHLRwKebREuBAPmcx8cCSCw6KVOZSaROqm8&quot;&gt;link to it&lt;/a&gt;.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/found-a-letter/</link>
      <guid>https://dhrm1k.github.io/blog/found-a-letter/</guid>
      <pubDate>Sun, 03 Sep 2023 00:00:00 GMT</pubDate>
    </item><item>
      <title>for computers</title>
      <description><![CDATA[&lt;p&gt;technology should be explored for the love of it and for the fun of it, rather than for the sake of it.&lt;/p&gt;
&lt;p&gt;you own a computer. that&#39;s a big deal. you have access to the internet. that connects you to the whole world. you have wikipedia. that&#39;s information on the tips of your fingers.&lt;/p&gt;
&lt;p&gt;anyone who is interested in programming should definitely read: &amp;quot;&lt;a href=&quot;http://www.catb.org/~esr/faqs/hacker-howto.html&quot;&gt;how to become a hacker&lt;/a&gt;&amp;quot;.&lt;/p&gt;
&lt;p&gt;after reading this, if you too want to make a website like this one you are on or the one visited of eric steven raymond, this &amp;quot;&lt;a href=&quot;https://neustadt.fr/essays/the-small-web/&quot;&gt;rediscovering the small web&lt;/a&gt;&amp;quot; might give you some more inspiration.&lt;/p&gt;
&lt;p&gt;let it be anything—rocks, your opinion on which paper size is perfect, or your thoughts. your little about page. curate it on the internet. you never know who they end up being helpful to. it might help someone who has similar interests as you to reach out to you. also, i don&#39;t think it&#39;s boring to maintain your personal site. it&#39;s a very fun task.&lt;/p&gt;
&lt;p&gt;i really love this hackernews &lt;a href=&quot;https://news.ycombinator.com/item?id=22347172&quot;&gt;comment&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;be infinitely curious and playful without bounds. try to break things, use things improperly, dig deeper and void warranties. coloring outside the lines and then some. find a tribe. if there isn&#39;t one, start one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dent the universe, or at least have a good time and live it up.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/for-posteriority/</link>
      <guid>https://dhrm1k.github.io/blog/for-posteriority/</guid>
      <pubDate>Thu, 12 Sep 2024 00:00:00 GMT</pubDate>
    </item><item>
      <title>wtfathon - we hosted a hackathon</title>
      <description><![CDATA[&lt;p&gt;earlier this month, i and few of my friends hosted a hackathon. it&#39;s called &lt;a href=&quot;https://www.wtfuckathon.com/&quot;&gt;wtfathon&lt;/a&gt;. the original idea was of &lt;a href=&quot;https://x.com/KaranJanthe&quot;&gt;karan&lt;/a&gt;. as i have put it in the .txt we were writing when planning for the hackathon:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this is not your normal crud hackathon. not limited to just a software. just make us scream what the fuck.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;you might have heard of &amp;quot;stupid shit no one needs &amp;amp; terrible ideas hackathon&amp;quot;. the goal is to not make something revolutionary, but to enjoy the joy of building it. don&#39;t build something useful. build something utterly useless. here you are not held up by any constraints. build whatever the hell your heart wants. you can search more about such hackathons. there are many hosted across the world with many wild ideas. banana zipper. scream bird which is flappy bird, but yes, you scream or a beer selfie stick. the list goes on... there are many across the world, but this is the first hosted in india. this was ahmedabad&#39;s version of it. we even promoted the hackathon on &lt;a href=&quot;https://old.reddit.com/r/ahmedabad/comments/1f3gxuh/ahmedabads_version_of_stupid_shit_no_one_needs/&quot;&gt;reddit&lt;/a&gt;. the post when i posted with a freshly made account was marked as spam, karan&#39;s friends with the moderator of r/ahmedabad. he pinged the moderator and voila, our post was up.&lt;/p&gt;
&lt;p&gt;the hackathon was a success. we have a &lt;a href=&quot;https://x.com/whathfathon&quot;&gt;twttr&lt;/a&gt; account where we plan to update you with any further fun we might have. do join if you are in the city. we have photos there too. :)&lt;/p&gt;
&lt;p&gt;we still have a lot of plans for the community that we have build. maybe, a newsletter? maybe, a mailing list? there&#39;s a lot to talk about. here&#39;s the group photo from the hackathon. it was a good day. yes, people showed up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/wtfathon_group.jpeg&quot; alt=&quot;wtfathon group photo&quot; /&gt;&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/hackathon/</link>
      <guid>https://dhrm1k.github.io/blog/hackathon/</guid>
      <pubDate>Wed, 18 Sep 2024 00:00:00 GMT</pubDate>
    </item><item>
      <title>setting up my nvim</title>
      <description><![CDATA[&lt;p&gt;tl;dr: here&#39;s the link to my vim &lt;a href=&quot;https://github.com/dhrm1k/config-files/blob/master/init.vim&quot;&gt;config file&lt;/a&gt;. it&#39;s pretty basic. if you want to see how it came together, then keep reading.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/my-nvim.png&quot; alt=&quot;my nvim setup&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;i recently had the inner urge to try vim. i settled on neovim. i had no interest in the lua capabilities, nor do i have any yet. i know i will tinker with it in the future, but for now i just went with neovim because, first of all, my main laptop is windows, so whatever i chose, i had to install it. it didn&#39;t come &lt;a href=&quot;https://unix.stackexchange.com/questions/194430/why-is-emacs-not-preinstalled/194438#194438&quot;&gt;preinstalled&lt;/a&gt; like it does with macos and linux.&lt;/p&gt;
&lt;p&gt;i love this quote from the unix stackexchange:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this makes emacs in effect an immigrant from a very different culture, while vi is a native.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;well, let&#39;s start from the beginning. i installed pop!_os on my brother&#39;s machine. i&#39;d been reading about pop!_os for a long time, but it wasn&#39;t until i saw karan had pop!_os installed on one of his machines that i was convinced. later, probably a month ago, i switched my personal computer to pop!_os. using linux on my computer made me appreciate foss more and more.&lt;/p&gt;
&lt;p&gt;my introduction to vim for the first time was in 11th grade. i also took my practical exams, which, from what i can remember, involved doing 10 things in vim. after that, i just knew about vim&#39;s existence and used it on and off very occasionally. up until this month, i decided to pick it up seriously. the people i follow on the now-soon-to-be-sunsetted &lt;a href=&quot;https://emacs.ch/&quot;&gt;emacs.ch&lt;/a&gt; and my new favorite &lt;a href=&quot;https://fosstodon.org/&quot;&gt;fosstodon.org&lt;/a&gt; also played a role. i then browsed through a few vim tutorials and got started with it.&lt;/p&gt;
&lt;p&gt;i installed neovim on my laptop, which is still running windows. i used vanilla neovim for a day or two without any plugins. i later realized that having a complete plugin system on windows would require too many unresolved changes. i then decided to go for wsl.&lt;/p&gt;
&lt;p&gt;wsl stands for windows subsystem for linux. it allows a windows computer to run a linux environment without the need for a separate virtual machine. among all the available options, i narrowed it down to either debian or ubuntu. i finally chose debian because i already had experience with pop!_os. i mean, debian is the base. installing wsl2 was much easier than i had anticipated. it just took the following command in powershell:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wsl --install -d debian
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and voila! i later installed the debian app from the windows store, which wasn&#39;t necessary.&lt;/p&gt;
&lt;h2&gt;a guide to my setup&lt;/h2&gt;
&lt;p&gt;first of all, i don&#39;t prefer line numbering to be relative. the plugins i currently use are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nerdtree&lt;/li&gt;
&lt;li&gt;vim-airline&lt;/li&gt;
&lt;li&gt;surround.vim&lt;/li&gt;
&lt;li&gt;vim-devicons&lt;/li&gt;
&lt;li&gt;vim-css-color&lt;/li&gt;
&lt;li&gt;awesome-vim-colorschemes&lt;/li&gt;
&lt;li&gt;youcompleteme (earlier, i settled on this for code completion when i could not manage to make &lt;strong&gt;conquer of completion&lt;/strong&gt; work.)&lt;/li&gt;
&lt;li&gt;now, &lt;strong&gt;coc.nvim&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;firstly, i use the vim plug plugin manager. setting up vim plug is easy too. just run the shell command from their github (i also feel uneasy copying the command and running it on my machine, but i had to trust the devs for this one).&lt;/p&gt;
&lt;p&gt;i expect you are familiar with &lt;code&gt;:pluginstall&lt;/code&gt; to install the plugins in the &lt;code&gt;.vim&lt;/code&gt; file and &lt;code&gt;:plugclean&lt;/code&gt; to remove the plugins. once, on my fresh wsl install, i messed with the &lt;code&gt;.gitconfig&lt;/code&gt; file, which was giving me errors when cloning. i removed and then reinstalled git, and that solved the problem.&lt;/p&gt;
&lt;p&gt;code completion was a tough issue. i didn&#39;t want to deal with node.js slop (also because i couldn&#39;t make it work with npm when i tried), so i settled on using youcompleteme. the catch with youcompleteme is that it has to be compiled with clang. i followed the readme. i mean, if the readme helped, then the folks building it surely knew what they were doing. props to them for that.&lt;/p&gt;
&lt;p&gt;after a few hours of using it, i realized i had just tried npm to build coc.nvim. i could also try it with yarn to see if it worked. so i started again to make coc.nvim work, this time with &lt;code&gt;yarn build&lt;/code&gt;, and magically, this time it did. i found another &lt;a href=&quot;https://stackoverflow.com/questions/69841916/neovim-coc-nvim-build-inderx-js-not-found-please-install-dependencies-and-com/76596658#76596658&quot;&gt;stack overflow&lt;/a&gt; answer that failed with yarn, so they were trying npm and found success.&lt;/p&gt;
&lt;p&gt;doing this wasn&#39;t the complete solution, but it was close. in case anyone has trouble dealing with code completions using neovim and stumbles upon this text in the future, i hope it&#39;s helpful.&lt;/p&gt;
&lt;p&gt;later, i just had to install pip, the python package manager. it went well, but installing jedi through pip3 gave me an error.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;error: externally-managed-environment

× this environment is externally managed
╰─&amp;gt; to install python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    if you wish to install a non-debian-packaged python package,
    create a virtual environment using python3 -m venv path/to/venv.
    then use path/to/venv/bin/python and path/to/venv/bin/pip. make
    sure you have python3-full installed.

    if you wish to install a non-debian packaged python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. make sure you have pipx installed.

    see /usr/share/doc/python3.11/readme.venv for more information.

note: if you believe this is a mistake, please contact your python installation or os distribution provider. 
you can override this, at the risk of breaking your python installation or os, by passing --break-system-packages.

hint: see pep 668 for the detailed specification.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you can refer to this &lt;a href=&quot;https://askubuntu.com/questions/1465218/pip-error-on-ubuntu-externally-managed-environment-%C3%97-this-environment-is-extern&quot;&gt;stack exchange&lt;/a&gt; answer for the error message. so, as instructed in the error message, i went with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install python3-jedi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the last thing to do was to run the following command in &lt;code&gt;init.vim&lt;/code&gt; to install the python extension:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:cocinstall coc-python
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and voila, you have code completion set up for python. the coc documentation is also very descriptive, so check it out.&lt;/p&gt;
&lt;p&gt;edit (made on 2025-10-25): it&#39;s funny how my autocomplete is mostly powered by copilot.nvim	now.&lt;/p&gt;
&lt;p&gt;if you want any help setting up your neovim and think i can help, feel free to email me at dharmiik [at] proton [dot] me. for faster responses, if you are on the fediverse, i suggest pinging me at [at] dharmik [at] fosstodon [dot] org.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/vim-config/</link>
      <guid>https://dhrm1k.github.io/blog/vim-config/</guid>
      <pubDate>Sun, 22 Sep 2024 00:00:00 GMT</pubDate>
    </item><item>
      <title>hosting a snac server (linuxusers.in)</title>
      <description><![CDATA[&lt;p&gt;i recently hosted a snac instance and am a proud owner of the domain &lt;a href=&quot;https://linuxusers.in/&quot;&gt;linuxusers.in&lt;/a&gt;. if you are friend of mine and frequently use fediverse, ping me and let me get you a account on the instance.&lt;/p&gt;
&lt;p&gt;or if you are just someone who uses fediverse, I am &lt;a href=&quot;https://linuxusers.in/@dharmik&quot;&gt;@dharmik@linuxusers.in&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;it&#39;s been a long time. the domain has expired and the instance was also shut down. maybe, some other day, we will start something like this again.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/snac-server/</link>
      <guid>https://dhrm1k.github.io/blog/snac-server/</guid>
      <pubDate>Thu, 14 Nov 2024 00:00:00 GMT</pubDate>
    </item><item>
      <title>how to install and try react without a framework</title>
      <description><![CDATA[&lt;p&gt;It can be quite bewildering to a beginner that the official &lt;a href=&quot;https://react.dev/learn/start-a-new-react-project&quot;&gt;React documentation&lt;/a&gt; doesn&#39;t provide a straightforward way to install or try React without a framework. While there is a section explaining why you should use a framework, this can be daunting for someone who is just getting started or simply wants to dabble in React.&lt;/p&gt;
&lt;p&gt;The official docs suggest adding React to an &lt;a href=&quot;https://react.dev/learn/add-react-to-an-existing-project#using-react-for-a-part-of-your-existing-page&quot;&gt;existing project&lt;/a&gt; using the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install react react-dom
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, when you run this command, you may encounter an error due to a version conflict between React and the testing libraries, particularly &lt;code&gt;@testing-library/react&lt;/code&gt;, which currently has a peer dependency on React 18.&lt;/p&gt;
&lt;p&gt;To resolve this, you can either downgrade React or use the &lt;code&gt;--legacy-peer-deps&lt;/code&gt; flag to bypass the peer dependency checks.&lt;/p&gt;
&lt;p&gt;But how is all of this supposed to be understood by a beginner?&lt;/p&gt;
&lt;p&gt;Here&#39;s a simple and easy-to-follow approach for anyone who wants to install React without a framework. You can use &lt;strong&gt;Vite&lt;/strong&gt;, a fast build tool, to quickly set up a React project:&lt;/p&gt;
&lt;h2&gt;Steps to Get Started with React Without a Framework:&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create a new Vite project with the React template:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm create vite@latest my-react-app -- --template react
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Navigate to your project directory:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd my-react-app
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Install the necessary dependencies:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;strong&gt;Start the development server:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By following these simple steps, you&#39;ll have a React app running without any framework, thanks to Vite.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/try-react-without-framework/</link>
      <guid>https://dhrm1k.github.io/blog/try-react-without-framework/</guid>
      <pubDate>Thu, 02 Jan 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>understanding the modulo operator in python</title>
      <description><![CDATA[&lt;p&gt;the elegant and beautiful background you&#39;re seeing started with me implementing conway&#39;s game of life in pygame. then i remembered danny lin&#39;s &lt;a href=&quot;https://kdrag0n.dev/&quot;&gt;website&lt;/a&gt; (he&#39;s the creator of orbstack, an alternative to docker desktop for managing vms easily), where he had it featured. i decided i wanted it on my site too. after some effort, i finally got it working and made it prettier. now, it&#39;s the background of this blog.&lt;/p&gt;
&lt;p&gt;it does look pretty.&lt;/p&gt;
&lt;p&gt;do you know how the modulo operator (&lt;code&gt;%&lt;/code&gt;) works in python? i&#39;m sure most of us think it just returns the remainder of the division, but we never really dive deeper to understand the logic behind it. turns out, python&#39;s implementation follows this formula:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a % b = a - b * floor(a / b)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;let&#39;s take an example to break it down. consider &lt;code&gt;6 % 3&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = 6
b = 3

6 % 3 = 6 - 3 * floor(6 / 3)
      = 6 - 3 * 2
      = 6 - 6
      = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i got curious about this while implementing conway&#39;s game of life and came across the concept of a toroidal boundary.&lt;/p&gt;
&lt;h2&gt;toroidal boundary and the modulo operator&lt;/h2&gt;
&lt;p&gt;a toroidal boundary (or periodic boundary condition) means the grid wraps around both horizontally and vertically, forming a donut-like shape. if a cell moves off one edge, it reappears on the opposite edge.&lt;/p&gt;
&lt;p&gt;let&#39;s say the grid width is 5, so valid column positions are 0, 1, 2, 3, and 4. now, if we&#39;re moving left, the new column is &lt;code&gt;x - 1&lt;/code&gt;. but what happens if &lt;code&gt;x = 0&lt;/code&gt;? subtracting 1 gives -1, which is outside the valid range.&lt;/p&gt;
&lt;p&gt;to handle this, we use the modulo operator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;left = (x - 1) % width
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when &lt;code&gt;x = 0&lt;/code&gt;, this gives:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;left = (0 - 1) % 5 = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this wraps the column around to the last position, keeping everything within the grid.&lt;/p&gt;
&lt;h2&gt;modulo behavior across different languages&lt;/h2&gt;
&lt;p&gt;one interesting thing to note: the result of the modulo operation in python takes the sign of the divisor (&lt;code&gt;b&lt;/code&gt;). this is different from some other languages.&lt;/p&gt;
&lt;h3&gt;example: -10 % 3&lt;/h3&gt;
&lt;p&gt;in python:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-10 % 3 = -10 - 3 * floor(-10 / 3)
        = -10 - 3 * -4
        = -10 + 12
        = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;but in c:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a % b = a - b * trunc(a / b)

-10 % 3 = -10 - 3 * trunc(-10 / 3)
        = -10 - 3 * -3
        = -10 + 9
        = -1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;why the difference? in c, the division result is truncated towards zero (it just chops off the decimal part), while python uses flooring. that&#39;s why the results aren&#39;t the same.&lt;/p&gt;
&lt;p&gt;so, next time you use the modulo operator, you&#39;ll know it&#39;s more than just finding the remainder—there&#39;s a bit of math magic behind the scenes!&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/conways-game-of-life-canvas/</link>
      <guid>https://dhrm1k.github.io/blog/conways-game-of-life-canvas/</guid>
      <pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>promocraft at lj innovation village</title>
      <description><![CDATA[&lt;p&gt;well, what were you doing this valentine&#39;s? what we were doing was presenting &lt;a href=&quot;https://www.promocraft.xyz/&quot;&gt;promocraft.xyz&lt;/a&gt; at lj innovation village.&lt;/p&gt;
&lt;img eleventy:ignore=&quot;&quot; src=&quot;https://www.promocraft.xyz/static/promocraft-logo.png&quot; alt=&quot;promocraft logo&quot; /&gt;
&lt;p&gt;it was an experience. for those who haven&#39;t had the time to understand what we&#39;re building, here&#39;s a brief explanation:&lt;/p&gt;
&lt;p&gt;with promocraft, we are streamlining the process of creating promotional material. right now, if you want to design promotional content for your business, the workflow looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;current process:&lt;/strong&gt;&lt;br /&gt;
google → canva → search the entire web for a great background → ensure there&#39;s no watermark → edit it → add text → remove background from logos → add more elements.&lt;/p&gt;
&lt;p&gt;while canva is great, it can be frustrating for users who just want something quick and efficient. that&#39;s where promocraft comes in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;with promocraft:&lt;/strong&gt;&lt;br /&gt;
google → promocraft → select a template from your collection → add your logo with our powerful editor → download the design.&lt;/p&gt;
&lt;p&gt;promocraft provides beautifully curated templates that you can customize with ease, all within one app. say goodbye to the hassle and hello to instant, high-quality promotional designs!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;promocraft made designing promotional material so effortless! the templates are amazing.&amp;quot; – event attendee&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;if you are a business that needs help with our editor or assistance in designing state-of-the-art promotional material to make your brand stand out, let us know. you can find my number in the &lt;a href=&quot;https://www.promocraft.xyz/contact&quot;&gt;contact us&lt;/a&gt; section of the website or email me at dharmiik at proton dot me.&lt;/p&gt;
&lt;h2&gt;photos from lj innovation village&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/promocraft-booth.jpeg&quot; alt=&quot;promocraft booth&quot; /&gt;
&lt;em&gt;promocraft booth&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/sarthak-making-changes.jpeg&quot; alt=&quot;our css wiz making changes directly to production as we speak&quot; /&gt;
&lt;em&gt;our css wiz making changes directly to production as we speak.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/dharmik-pic-promocraft.jpeg&quot; alt=&quot;my photo at promocraft booth&quot; /&gt;
&lt;em&gt;my photo at promocraft booth&lt;/em&gt;&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/promocraft-ljiv/</link>
      <guid>https://dhrm1k.github.io/blog/promocraft-ljiv/</guid>
      <pubDate>Sat, 15 Feb 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>notes on raycasting</title>
      <description><![CDATA[&lt;p&gt;heard of john carmack? i suppose you&#39;ve read &lt;em&gt;masters of doom&lt;/em&gt; too. well, who doesn&#39;t know the creator of &lt;em&gt;doom&lt;/em&gt;, john carmack? that book introduced me to raycasting, and ever since, i&#39;ve been a fan. recently, i stumbled upon this insane tutorial on implementing raycasting in c++ (https://lodev.org/cgtutor/raycasting.html). even now, i can&#39;t fully wrap my head around how games like &lt;em&gt;wolfenstein 3d&lt;/em&gt; managed to fake a 3d world inside a 2d grid. it&#39;s unreal.&lt;/p&gt;
&lt;p&gt;a couple of days later, i thought—why not build a basic raycasting engine, but in javascript? now, why javascript? good question. if you&#39;ve ever brought up javascript around me, you already know how i feel about it. hate is a mild word. so why use it? simple: everyone has a browser, and i wanted people to mess around with it without hassle.&lt;/p&gt;
&lt;p&gt;i won&#39;t go into the code here (it&#39;s on github, or will be by the time i finish writing this). what i want to talk about is the math and physics that blew my mind while working on it. honestly, if i had realized how useful this stuff was back in high school, i&#39;d have paid more attention.&lt;/p&gt;
&lt;p&gt;a while ago, when i was playing around with an arcade-style shooter in pygame, i used the distance formula for collision detection. seems basic, but i know my 15-year-old self—who was already deep into computers—would have been blown away.&lt;/p&gt;
&lt;p&gt;now, how hard could making 2.5d work be? i mean, just drawing on a 2d canvas and making it feel 3d? well, for me, it was... a task. i am not a smart man. you might want to try it for yourself.&lt;/p&gt;
&lt;p&gt;there&#39;s a lot still to do. you can try the output at &lt;a href=&quot;https://dhrm1k.github.io/raycast&quot;&gt;raycast&lt;/a&gt;. these are rough notes i take for future reference or anyone interested in raycasting or application of mathematics. i&#39;d still recommend you to read it, i had fun writing it.&lt;/p&gt;
&lt;p&gt;i strongly recommend reading &lt;a href=&quot;https://lodev.org/cgtutor/raycasting.html&quot;&gt;lode&#39;s computer graphics tutorial&lt;/a&gt; to get better idea of what comes here.&lt;/p&gt;
&lt;h2&gt;understanding the mathematics and logic&lt;/h2&gt;
&lt;p&gt;let&#39;s get into the core concepts of raycasting and the math behind making a 3d scene work.&lt;/p&gt;
&lt;h3&gt;player movement&lt;/h3&gt;
&lt;p&gt;moving the player is just basic trig. imagine you&#39;re on a grid, moving forward, backward, and rotating left or right. your position and direction can be calculated with simple math:&lt;/p&gt;
&lt;p&gt;moving forward is just using cosine and sine on your angle: &lt;code&gt;newx = player.x + math.cos(player.angle) * player.speed;&lt;/code&gt; and &lt;code&gt;newy = player.y + math.sin(player.angle) * player.speed;&lt;/code&gt;. moving backward? same thing, just subtracted. turning is just &lt;code&gt;player.angle -= player.turnspeed;&lt;/code&gt; for left and &lt;code&gt;player.angle += player.turnspeed;&lt;/code&gt; for right.&lt;/p&gt;
&lt;h3&gt;raycasting logic&lt;/h3&gt;
&lt;p&gt;raycasting is what makes a 2d grid look 3d. the idea? fire rays in different directions and see where they hit walls. that&#39;s it.&lt;/p&gt;
&lt;h4&gt;step-by-step explanation&lt;/h4&gt;
&lt;p&gt;start the ray at the player&#39;s position: &lt;code&gt;let rayx = player.x;&lt;/code&gt;, &lt;code&gt;let rayy = player.y;&lt;/code&gt;. get the direction: &lt;code&gt;const raydirx = math.cos(angle);&lt;/code&gt; and &lt;code&gt;const raydiry = math.sin(angle);&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;then, loop forward in small steps: &lt;code&gt;rayx += raydirx * stepsize;&lt;/code&gt;, &lt;code&gt;rayy += raydiry * stepsize;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;check if it hit a wall: &lt;code&gt;const mapx = math.floor(rayx);&lt;/code&gt;, &lt;code&gt;const mapy = math.floor(rayy);&lt;/code&gt;. if it did, calculate the distance: &lt;code&gt;const distance = math.sqrt((player.x - rayx)^2 + (player.y - rayy)^2);&lt;/code&gt;. repeat until you hit something.&lt;/p&gt;
&lt;h3&gt;rendering the scene&lt;/h3&gt;
&lt;p&gt;once we have distances, we draw walls. first, clear the canvas: &lt;code&gt;ctx.fillstyle = &#39;#000&#39;;&lt;/code&gt;, &lt;code&gt;ctx.fillrect(0, 0, canvas.width, canvas.height);&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;set the field of view: &lt;code&gt;const fov = math.pi / 3;&lt;/code&gt;, then cast a ray for each column of the screen. each ray&#39;s angle: &lt;code&gt;const rayangle = player.angle - fov / 2 + (i / raycount) * fov;&lt;/code&gt;. get the wall height: &lt;code&gt;const wallheight = (canvas.height / distance) * 0.5;&lt;/code&gt;, then draw it: &lt;code&gt;ctx.fillstyle = &#39;#ff0000&#39;;&lt;/code&gt;, &lt;code&gt;ctx.fillrect(i, (canvas.height - wallheight) / 2, 1, wallheight);&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;that&#39;s how you fake 3d from a 2d grid.&lt;/p&gt;
&lt;p&gt;i have a lot on my plate, but what&#39;s life if not fun. i am working on promocraft, but my next goal is to add textures. you may find the code on github.&lt;/p&gt;
&lt;h2&gt;debugging and refining&lt;/h2&gt;
&lt;p&gt;debugging raycasting is a pain, but here&#39;s how to keep your sanity:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;check initialization:&lt;/strong&gt; make sure the canvas and context are working.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;verify movement:&lt;/strong&gt; log player coordinates, make sure they update correctly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;debug raycasting:&lt;/strong&gt; log ray positions, check if they detect walls properly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;test rendering:&lt;/strong&gt; make sure wall heights actually make sense.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;take it step by step, debug as you go, and eventually, you&#39;ll have a working raycasting engine.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/notes-on-raycasting/</link>
      <guid>https://dhrm1k.github.io/blog/notes-on-raycasting/</guid>
      <pubDate>Fri, 28 Feb 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>udaipur trip with friends</title>
      <description><![CDATA[&lt;p&gt;i am loving this unsolicited ritual of a trip with the year. last year, we were hiking on girnar. this year, we decided to escape the state of gujarat and go to udaipur. well, the trip was a fun one. i do need to pen this so i can come back here to rejoy the fun we had.&lt;/p&gt;
&lt;p&gt;firstly, i was in a dilemma whether i was going to be able to make it on the trip or not. i had an internship planned for the national centre for biological sciences (ncbs), bangalore, and they assured me i was shortlisted for the most part and just wanted to ask a few questions. though later, the question of logistics came up, and i was in no state to make a commitment to go to bangalore for four months to work (my moronic college wouldn&#39;t let me then, and nor is it letting me for another opportunity that has come now). though i got the closure that the ncbs thing is off, and well, the trip was on.&lt;/p&gt;
&lt;p&gt;hats off to my friends who always plan everything out (anything not playing by their plans is a different thing). they planned the whole trip, starting from where we will go to where we will stay to how the itinerary there will be, while i lurked with my occasional criticism in the whatsapp group.&lt;/p&gt;
&lt;p&gt;we booked a bus to go from ahmedabad to udaipur, but the gsrtc scheduling sucks, and the bus that was supposed to be departing from ahmedabad at around 12:15-ish was nowhere to be seen till around 1:30. upon endless enquiring and the realization that the bus depot/station department just sucks and replies to enquiries like you have just asked them for a part in their parents&#39; will, we got to know that the specific bus we were supposed to climb/take getting late is not new. it often gets late.&lt;/p&gt;
&lt;p&gt;though after hours of trying to sleep and bickering in between about whose idea it was in the first place to choose to go by bus, we reached udaipur in the morning by around 6–6:30-ish. the first thing we did was rent activas and later rush to the hotel.&lt;/p&gt;
&lt;p&gt;the first place we decided to go to was bahubali hills. it&#39;s a great place. rahul spotted a lot of birds. on the way back, we had to go to a bar, because being born in a dry state never permitted us.&lt;/p&gt;
&lt;p&gt;after the bar thingy, we finally made it to the city palace. it was stunning, majestic, actually. definitely one of those &amp;quot;if you go to udaipur and don&#39;t see this, what are you even doing&amp;quot; kinda places.&lt;/p&gt;
&lt;p&gt;there&#39;s a lot we did, but i don&#39;t have the energy to write it all down. maybe later, maybe not. depends on how nostalgic i get. for now this will act as my time capsule.&lt;/p&gt;
&lt;p&gt;photos on the fediverse: &lt;a href=&quot;https://social.photo/dharmiik&quot;&gt;@dharmik@social.photo&lt;/a&gt; on pixelfed and &lt;a href=&quot;https://linuxusers.in/dharmik&quot;&gt;@dharmik@linuxusers.in&lt;/a&gt; on our friendly snac instance.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/udaipur-2025/</link>
      <guid>https://dhrm1k.github.io/blog/udaipur-2025/</guid>
      <pubDate>Fri, 11 Apr 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>experimenting with stable diffusion</title>
      <description><![CDATA[&lt;p&gt;&lt;em&gt;first entry (last updated on 02/05/2025):&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;i have hit the realization that my productivity happens to be directly proportional to the amount of hours i sleep (stretched over an entire week). recently i was tinkering with stable diffusion. to those unaware, the question arises from the title, what even is stable diffusion?&lt;/p&gt;
&lt;p&gt;stable diffusion is a text-to-image open source model. you give it a prompt like &amp;quot;a cat riding a skateboard,&amp;quot; and it spits out an image that looks like someone painted exactly that. the reason for choosing it is that it&#39;s fast, local, and customizable.&lt;/p&gt;
&lt;p&gt;the goal of the experiment was to generate stylish advertising banners, specific to a theme. think &amp;quot;personalized ad posters,&amp;quot; but with ai. why? i do love what &lt;a href=&quot;https://www.promocraft.xyz/&quot;&gt;promocraft&lt;/a&gt; is doing right now, but with the image-generation gpt provides right now, i mean, why would you use our tool now? not to forget that though gpt is good at generating images and can generate advertisements if asked to (though that will indeed require a lot of prompting back and forth, also not to ignore you&#39;d still have to add your logo to it, which can&#39;t be done by any model because of the fear that someone might be using copyrighted material), it will never be a complete swiss knife for marketing.&lt;/p&gt;
&lt;p&gt;here&#39;s the link to the repo for code if you want to try it or just browse: &lt;a href=&quot;https://github.com/dhrm1k/stable-diff-for-ads&quot;&gt;dhrm1k/stable-diff-for-ads&lt;/a&gt; (instructions in readme).&lt;/p&gt;
&lt;p&gt;i started with setting up the basics on my google colab. i used stable diffusion v1.5, why? because of the low hardware requirements it has (and sorry to say, but lol because that&#39;s what i researched upon). i prompted it to generate: &amp;quot;a stylish shoe advertisement with a clean background.&amp;quot; image below. there will be a github repo link somewhere in the post. ctrl-f github. i have not cleared the clutter i have made everywhere.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/stable-diffusion-experiment-og-prompt.png&quot; alt=&quot;original prompt result&quot; /&gt;&lt;/p&gt;
&lt;p&gt;it&#39;s apparent it&#39;s nowhere near up to the mark. if i posted this somewhere, the company would fire me immediately. so the question was: how do i make it generate better images? the answer to that is fine-tuning. fine-tuning refers to the process of training an already-existing pretrained model on a specific dataset to make it excel at those points of the dataset more. think of it like this, the model had the capabilities already, i am not starting from scratch, i am just introducing it to new styles or contexts.&lt;/p&gt;
&lt;p&gt;i had no idea how to fine-tune a model, so after a lot of questioning with gpt and google, it became clear to me i had to use a technique called dreambooth, but with lora. dreambooth lets you personalize a model to new concepts, and lora is a lightweight way to apply those changes without needing a 24gb gpu from 2037.&lt;/p&gt;
&lt;p&gt;an attempt to explain what lora does (i am not sure as of now that i myself quite understand it well): imagine you have a pre-trained model, it&#39;s already been trained on a huge dataset, like millions of images, and it&#39;s really good at general tasks. but now, you want to make it even better at something specific, like recognizing shoes. the problem is, this model has millions (or even billions) of parameters (weights), and training all of them from scratch takes a lot of time and computing power.&lt;/p&gt;
&lt;p&gt;instead of retraining everything, lora lets you adapt the model efficiently using a much smaller amount of data and computing power.&lt;/p&gt;
&lt;p&gt;so i downloaded the &lt;code&gt;train_dreambooth_lora.py&lt;/code&gt; script from hugging face&#39;s diffusers repo. that script takes in a directory of images, a class prompt (like &amp;quot;photo of a shoe&amp;quot;), and a placeholder token. it fine-tunes just a few extra weights (not the full model) and gives you back a lora weights file you can load when generating.&lt;/p&gt;
&lt;p&gt;for the dataset, i found something surprisingly decent on hugging face called &lt;a href=&quot;https://huggingface.co/datasets/PeterBrendan/AdImageNet&quot;&gt;adimagenet&lt;/a&gt;. it&#39;s a collection of categorized ad posters, way better than anything i could&#39;ve scraped together manually.&lt;/p&gt;
&lt;p&gt;of course, being me, i ran this on google colab&#39;s free tier. and after maybe 3–4 training loops, boom, it stopped. a popup said i&#39;d hit the usage cap. no restart button, no plea window. just the end of the line.&lt;/p&gt;
&lt;p&gt;that&#39;s when i tried modal. i&#39;d heard about it in passing and decided to give it a shot. i was half-expecting it to ask for a credit card right away, but to my surprise, it didn&#39;t. they just gave me $5 in free credits. (apparently, i can get $30 more if i add a card, but i haven&#39;t done that yet.)&lt;/p&gt;
&lt;p&gt;after some wrangling, i managed to get the fine-tuning script to run on modal. it actually worked. and the result... was kind of better? i mean, it wasn&#39;t a disaster, but it still wasn&#39;t what i&#39;d hoped for either.&lt;/p&gt;
&lt;p&gt;here&#39;s the updated image, same prompt: &amp;quot;a stylish shoe advertisement with clean background.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/stable-diffusion-experiment-modal-output.png&quot; alt=&quot;modal output image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;it&#39;s less generic than the first one, sure. but still not quite ad-agency ready. might need to tweak training steps, or prompt engineering, or maybe try a better lora setup.&lt;/p&gt;
&lt;p&gt;this post is still in progress and will be kept updated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;note:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;today, i got to know that using a placeholder token might have improved the output quality. understand it as that a placeholder token provides more context to the fine-tuned model. there&#39;s a lot to learn in this. i plan to experiment with this new method and come back again here.&lt;/p&gt;
&lt;p&gt;here&#39;s a song recommendation for you: i&#39;m a fan of blink-182&#39;s music.&lt;/p&gt;
&lt;iframe style=&quot;border-radius:12px&quot; src=&quot;https://open.spotify.com/embed/track/3HM4NePuYAqdXpAGBk7nNz?utm_source=generator&quot; width=&quot;70%&quot; height=&quot;152&quot; frameBorder=&quot;0&quot; allowfullscreen=&quot;&quot; allow=&quot;autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture&quot; loading=&quot;lazy&quot;&gt;&lt;/iframe&gt;
&lt;hr /&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/experimenting-with-stable-diffusion/</link>
      <guid>https://dhrm1k.github.io/blog/experimenting-with-stable-diffusion/</guid>
      <pubDate>Wed, 30 Apr 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>things you should get</title>
      <description><![CDATA[&lt;p&gt;well, i first thought of posting this on &lt;a href=&quot;https://linuxusers.in/&quot;&gt;linuxusers.in&lt;/a&gt; (go check it out if you haven&#39;t yet, and hop on the fediverse, there are a lot of smart people there too). but i wasn&#39;t sure if it would be appropriate there. i wanted this to live here for posterity. so here it is.&lt;/p&gt;
&lt;p&gt;also, i already have a post titled &lt;a href=&quot;https://dhrm1k.github.io/blog/for-posteriority/&quot;&gt;for posterity&lt;/a&gt;. if you like this one, you might like that one too.&lt;/p&gt;
&lt;p&gt;lately, i&#39;ve been commenting a lot on reddit under posts related to colleges in general or my college. knowingly or unknowingly, i might have given more than a few kids some advice. first-years, second-years. man, i pity them, because out of everyone they could have found, they found me. i tamper with my college&#39;s wikipedia page for fun, so how on earth am i supposed to show any love to my college. wiki once claimed that lju is among the top 5 technical colleges in ahmedabad. you&#39;re reading the blog of the person who removed it (i loved doing it. here&#39;s the &lt;a href=&quot;https://en.wikipedia.org/w/index.php?title=L._J._Institute_of_Engineering_and_Technology&amp;amp;diff=prev&amp;amp;oldid=1300166935&quot;&gt;diff&lt;/a&gt;). i honestly have no idea what to do. i don&#39;t really know at all. all i know are a few things not to do.&lt;/p&gt;
&lt;p&gt;what are the things i wish i had in my first year? (these are all free perks to take a chill pill. also, only get them if you genuinely want to tinker with things. and once you get them, use them. don&#39;t just grab them and forget about them.)&lt;/p&gt;
&lt;p&gt;first of all, you should get the github education pack. it comes with a ton of free stuff: domains, cloud credits, jetbrains tools, and a lot more. it&#39;s a must-have.&lt;/p&gt;
&lt;p&gt;also, please have an about page. i love those. i don&#39;t mean a portfolio, i mean your little corner on the internet. i have a lot of design ideas about them. i like mine to be minimal. make it the way you want to. add things you like. add things you don&#39;t. make it personal. you can use github pages for that. you can also use neocities or any other free hosting service. just have one.&lt;/p&gt;
&lt;p&gt;if you&#39;re into web development, get a free netlify account. it offers a lot. you can host static sites for free and even run serverless functions. vercel also offers the same.&lt;/p&gt;
&lt;p&gt;if you&#39;re into design, grab the student plan for figma. if i&#39;m not wrong, framer recently started offering a free student plan too.&lt;/p&gt;
&lt;p&gt;for note-taking or a personal wiki, students also get a free notion pro plan.&lt;/p&gt;
&lt;p&gt;i&#39;d recommend using x/twitter. i&#39;ve made a lot of friends there who are into tech (and really good at it).&lt;/p&gt;
&lt;p&gt;i&#39;d also recommend using linux. if you&#39;re on windows, install wsl2. it&#39;s fun and might keep your curiosity alive. i&#39;m writing this in vim on arch btw. i&#39;ve tried pop, debian, mint, and now i&#39;m playing with arch. (and yeah, i&#39;m open to helping anyone with linux errors. just dm me on linuxusers.in, x/twitter, or mail me, whatever. i&#39;m available any time of day or night for anything linux.)&lt;/p&gt;
&lt;p&gt;i&#39;m not sure yet, but i might be interning at this cool place. i&#39;m also trying something new (more about it if i don&#39;t change my mind on a few things). i&#39;ll write a separate post about that.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/things-you-should-get/</link>
      <guid>https://dhrm1k.github.io/blog/things-you-should-get/</guid>
      <pubDate>Fri, 23 May 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>how not to self-host adguard dns</title>
      <description><![CDATA[&lt;p&gt;i wish LLMs had peaked the self-hosting help department, but sadly they haven&#39;t. fuck chatgpt and all the moronic LLMs that couldn&#39;t assist me in my deployment of adguard dns for my personal usage (i am just kidding, don&#39;t fuck them. just in case the ai overlords rise one day and come to kill me, please forgive me my lords, i am a peasant who was just kidding)&lt;/p&gt;
&lt;p&gt;i don&#39;t know who it was, but i vividly remember hearing someone say once, &amp;quot;i have no clue about what to do. the only thing i know is what not to&amp;quot;. seems like that&#39;s the motto.&lt;/p&gt;
&lt;p&gt;note just incase you don&#39;t end up reading the whole thing (because of course no one does): if you are a friend and someone i know, feel free to ping me for help, if you try to self host or if you want access to my dns server.&lt;/p&gt;
&lt;h2&gt;what even is adguard and why did i do this to myself&lt;/h2&gt;
&lt;p&gt;adguard home is basically a network-wide ad blocker. instead of installing adblock on every device, you point all your stuff to use this as their dns server and boom - no ads anywhere. phone, smart tv, tablet, whatever.&lt;/p&gt;
&lt;p&gt;sounds cool right? it is. except i decided to self-host it at 2am on a 1gb ram azure vm because i&#39;m a broke student who values &amp;quot;control&amp;quot; and &amp;quot;privacy&amp;quot; over sanity. the alternative was paying $2/month for nextdns. obviously spending 48 hours debugging was the logical choice.&lt;/p&gt;
&lt;p&gt;(but does my site track you? sorry, it does. i too like data. but don&#39;t worry, i use umami instead of google analytics)&lt;/p&gt;
&lt;h2&gt;mistake #1: thinking azure would be easy&lt;/h2&gt;
&lt;p&gt;first thing you need to do is login to azure.microsoft.com, which for some reason is a big task in itself. if you haven&#39;t seen the website, please go look at it. why someone in good frigging conscience would design this monstrosity. the page physically hurts my eyes.&lt;/p&gt;
&lt;h2&gt;mistake #2: the yunohost rabbit hole&lt;/h2&gt;
&lt;p&gt;i had this brilliant idea to use yunohost because it supposedly makes self-hosting easy. except yunohost only works on debian 12. and azure doesn&#39;t have debian 12 images in india. image unavailable for some reason.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/azure-screenshot.png&quot; alt=&quot;azure debian image not available&quot; /&gt;&lt;/p&gt;
&lt;p&gt;so i googled which ubuntu version is based on debian 12. turns out it&#39;s ubuntu 22.04 LTS. perfect! except no.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/yuno-not-available-on-22-02.png&quot; alt=&quot;can&#39;t install on ubuntu screenshot&quot; /&gt;&lt;/p&gt;
&lt;p&gt;yunohost won&#39;t install on ubuntu either. this was the moment i realized people don&#39;t self-host because it&#39;s a choice. people don&#39;t self-host because there&#39;s no choice. no average person wants to deal with this crackedness after midnight.&lt;/p&gt;
&lt;h2&gt;mistake #3: casaos seemed like a good idea&lt;/h2&gt;
&lt;p&gt;i installed casaos. then immediately uninstalled it. why? because my tiny cute 1gb ram machine had 700mb occupied by casaos sitting idle. yeah, no thanks.&lt;/p&gt;
&lt;h2&gt;mistake #4: docker will make things easier (narrator: it didn&#39;t)&lt;/h2&gt;
&lt;p&gt;everyone says docker makes things easier. so i set up docker-compose like a good little developer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;version: &#39;3&#39;
services:
  adguard:
    image: adguard/adguardhome
    container_name: adguard
    ports:
      - &amp;quot;53:53/tcp&amp;quot;
      - &amp;quot;53:53/udp&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;except docker added another layer of bullshit to debug. permissions were fucked. certificates didn&#39;t work. volume mounts were confusing. every time i wanted to edit something, i had to &lt;code&gt;docker exec&lt;/code&gt; into the container like i&#39;m in the matrix.&lt;/p&gt;
&lt;p&gt;worst part? whenever i bound port 53 in docker and then stopped the container, my entire network would shit itself and ssh would die. i&#39;d have to reset the vm from azure portal like a caveman.&lt;/p&gt;
&lt;p&gt;eventually i said fuck it, ditched docker, and installed adguard natively. suddenly half my problems disappeared.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;lesson learned&lt;/strong&gt;: docker is great until it isn&#39;t. for a simple dns server on 1gb ram, native installation is better.&lt;/p&gt;
&lt;h2&gt;mistake #5: port 531 seemed reasonable&lt;/h2&gt;
&lt;p&gt;since port 53 kept killing my ssh, i moved dns to port 531. problem solved, right?&lt;/p&gt;
&lt;p&gt;wrong. android phones don&#39;t allow custom dns ports. you can&#39;t just type &amp;quot;531&amp;quot; anywhere in the settings. there&#39;s no field for it. phones only work with standard ports using encrypted dns.&lt;/p&gt;
&lt;p&gt;i spent hours testing why my phone wouldn&#39;t connect before realizing this.&lt;/p&gt;
&lt;h2&gt;mistake #6: not understanding cloudflare proxy&lt;/h2&gt;
&lt;p&gt;so i set up an A record pointing to my server. tried to query it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ dig @subdomain.mydomain.com google.com -p 531
;; communications error to [cloudflare-ip]#531: timed out
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;but direct ip worked fine:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ dig @[my-vm-ip] google.com -p 531
;; Got answer:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then it clicked. cloudflare proxy. the orange cloud. that motherfucker.&lt;/p&gt;
&lt;p&gt;when cloudflare proxy is on (orange cloud), they only forward HTTP/HTTPS on ports 80 and 443. everything else - dns, custom ports, ssh - gets blocked. my dns queries were hitting cloudflare&#39;s edge and getting dropped.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;the fix&lt;/strong&gt;: turn the proxy off. grey cloud that subdomain. suddenly everything worked.&lt;/p&gt;
&lt;h2&gt;mistake #7: trying to use https with an ip address&lt;/h2&gt;
&lt;p&gt;i wanted my dashboard at &lt;code&gt;https://[my-vm-ip]/&lt;/code&gt;. got let&#39;s encrypt certificates. pasted them into adguard. clicked save.&lt;/p&gt;
&lt;p&gt;service bricks. dashboard gone. browser says connection failed.&lt;/p&gt;
&lt;p&gt;then the revelation hit me: let&#39;s encrypt doesn&#39;t issue certificates for IP addresses. only domain names.&lt;/p&gt;
&lt;p&gt;so my cert for &lt;code&gt;subdomain.mydomain.com&lt;/code&gt; won&#39;t work when accessing via IP. browsers say no. android says hell no.&lt;/p&gt;
&lt;h2&gt;mistake #8: thinking cloudflare certificates would work&lt;/h2&gt;
&lt;p&gt;cloudflare gives free SSL certificates that last 15 years! perfect, right?&lt;/p&gt;
&lt;p&gt;except they&#39;re only trusted by cloudflare&#39;s proxy. phones don&#39;t trust them. browsers don&#39;t trust them. they&#39;re signed by cloudflare&#39;s private CA, not a public one.&lt;/p&gt;
&lt;p&gt;so cloudflare origin certs only work when proxy is ON. but when proxy is ON, cloudflare blocks dns traffic.&lt;/p&gt;
&lt;p&gt;catch-22.&lt;/p&gt;
&lt;h2&gt;what actually worked (eventually)&lt;/h2&gt;
&lt;p&gt;after 48 hours of pain, here&#39;s what finally worked:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;native installation&lt;/strong&gt;, not docker. just install adguard directly on ubuntu.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;cloudflare proxy OFF&lt;/strong&gt; (grey cloud) for the dns subdomain.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;let&#39;s encrypt&lt;/strong&gt; via adguard&#39;s automatic feature. it handles everything.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DNS-over-HTTPS and DNS-over-TLS&lt;/strong&gt; enabled. this is what phones actually need.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;open the right ports in azure: 53, 80, 443, 853.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;android settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;go to private dns&lt;/li&gt;
&lt;li&gt;enter your subdomain&lt;/li&gt;
&lt;li&gt;if it says &amp;quot;connected&amp;quot;, you won. if not, something&#39;s still fucked.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;things i learned the hard way&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;docker adds complexity when you don&#39;t need it&lt;/li&gt;
&lt;li&gt;cloudflare proxy blocks everything except http/https&lt;/li&gt;
&lt;li&gt;let&#39;s encrypt doesn&#39;t do IP addresses&lt;/li&gt;
&lt;li&gt;android requires encrypted dns (DoH/DoT)&lt;/li&gt;
&lt;li&gt;port 53 on linux is cursed because of systemd-resolved&lt;/li&gt;
&lt;li&gt;certificate file permissions matter&lt;/li&gt;
&lt;li&gt;azure&#39;s UI is a crime against design&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;was it worth it?&lt;/h2&gt;
&lt;p&gt;total time enjoyed (or wasted): 48 hours&lt;/p&gt;
&lt;p&gt;total money saved vs nextdns: $2/month&lt;/p&gt;
&lt;p&gt;was it worth it: hell yea, i love chaos.&lt;/p&gt;
&lt;p&gt;would i do it again: ask me after therapy&lt;/p&gt;
&lt;p&gt;but hey, everything works now. my phone uses my dns server. ads are blocked. i can see query logs. i feel like a hackerman.&lt;/p&gt;
&lt;p&gt;well, maybe.&lt;/p&gt;
&lt;p&gt;ask me again tomorrow.&lt;/p&gt;
&lt;p&gt;edit: all my life, my isp has been gtpl. lately, i also have airtel and frigging airtel literally doesn&#39;t want you to change settings. the input field to change dns are disabled. also after that, the save button is hidden by default and you have to change the setting, to see it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/airtel-save-button.png&quot; alt=&quot;airtel save button hidden&quot; /&gt;&lt;/p&gt;
&lt;p&gt;edit again:&lt;/p&gt;
&lt;p&gt;someone sent 2 lakh requests to my server at night. you little shits, i will block each one of you. he made request to sikelocci dot com while i was asleep. my poor vm almost died and apparently adguard also went dead for a while in the middle of the night, because i can see traffic drop and also my phone showing dns not working in the morning. f you you moron.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/too-many-requests.png&quot; alt=&quot;too many request&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;if you made it this far, i salute you. if you&#39;re trying to do the same thing, good luck. you&#39;ll need it. and maybe therapy afterward.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/how-to-selfhost-adguard/</link>
      <guid>https://dhrm1k.github.io/blog/how-to-selfhost-adguard/</guid>
      <pubDate>Sat, 08 Nov 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>building and booting my own kernel on arch linux</title>
      <description><![CDATA[&lt;p&gt;this is the final output you&#39;d see if you follow this entire blog post.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/kernel-on-arch-1.png&quot; alt=&quot;RESULT&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;is there any linux user who hasn&#39;t dreamed of building and booting their own kernel at least once? i know i have. recently, i decided to take the plunge and document my journey of building and booting my own custom linux kernel on arch linux. here&#39;s how it went.&lt;/p&gt;
&lt;p&gt;one of the other motivations behind this was to gain a deeper understanding of how the linux kernel works and how to customize it from the kernel level (not to ignore there wasn&#39;t a similar blog i found documenting this exact process with a good explaination, so i wanted to fill that gap too!).&lt;/p&gt;
&lt;p&gt;disclaimer: well, this post has been paraphrased or added more context using ai.&lt;/p&gt;
&lt;h2&gt;installing all the tools and necessary packages&lt;/h2&gt;
&lt;p&gt;well, the first thing you should do is update your system and install the necessary packages. on arch, you can do this using pacman:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;
sudo pacman -Syu
sudo pacman -S base-devel bc flex bison openssl zstd elfutils cpio qemu-full busybox
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;what each package does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;base-devel&lt;/code&gt;: essential development tools like gcc, make, etc.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bc&lt;/code&gt; and &lt;code&gt;flex&lt;/code&gt;/&lt;code&gt;bison&lt;/code&gt;: used for building the kernel and handling configurations.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openssl&lt;/code&gt;: for signing modules.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zstd&lt;/code&gt;: for compression.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;elfutils&lt;/code&gt;: tools for inspecting ELF binaries (kernel + modules).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cpio&lt;/code&gt;: this is the tool we’ll use to pack the initramfs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;qemu-full&lt;/code&gt;: full QEMU, lets us run kernels in a VM without bricking our laptop.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;busybox&lt;/code&gt;: a single binary that acts like multiple core Unix commands.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;cloning the linux kernel source code&lt;/h2&gt;
&lt;p&gt;next, we need to get the linux kernel source code. you can clone it from the official linux kernel git reposiory or from github. i will recommend not cloning from github as it is slow because of the large size of the linux kernel repo and the way it handles large files. instead, use the offical kernel repo:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/src
cd ~/src
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;configuring the kernel&lt;/h2&gt;
&lt;p&gt;this is something that every tutorial will tell you to do, but now explain it at all. the kernel configuration is basically a set of options that determine what features and drivers will be included. you can start with the default configuration for your architecture and then customize it.&lt;/p&gt;
&lt;p&gt;to start with the default configuration, run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;make defconfig
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you can customize with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;make menuconfig
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you&#39;d be wondering, why did we run the &lt;code&gt;make defconfig&lt;/code&gt; first? &lt;code&gt;make defconfig&lt;/code&gt; creates a default configuration file named &lt;code&gt;.config&lt;/code&gt; file in the kernel source directory. this file is essential for the kernel build process as it contains all the configuration options that determine which features, drivers, and modules will be included in the compiled kernel. just a fun fact that kernel config has ~15000 options. for example, you can enable/disable support for specific filesystems, hardware drivers, networking fetures, and more. the build command (&lt;code&gt;make&lt;/code&gt;) reads this &lt;code&gt;.config&lt;/code&gt; file to know what to include in the final kernel binary. you can even turn off compilation of certain features/drivers by disabling them in the config file, which can help reduce the kernel size and improve performance for specific use cases.&lt;/p&gt;
&lt;h2&gt;building the kernel&lt;/h2&gt;
&lt;p&gt;this builds the vmlinuz (bzImage) file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;make -j$(nproc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this command will start building the kernel using all available CPU cores to speed up the process.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;make&lt;/code&gt;: starts the kernel build system&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-j$(nproc)&lt;/code&gt;: uses all CPU cores to speed up the build. your cpu usage will spike to 100% during this process. to avoid that, limit the number of jobs, e.g., &lt;code&gt;-j4&lt;/code&gt; for 4 cores.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;output ends with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Kernel: arch/x86/boot/bzImage is ready
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this file is the &lt;strong&gt;actual bootable kernel&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;building an initrd with busybox&lt;/h2&gt;
&lt;p&gt;now if you are anything like me, you would want to boot this kernel and see it in action, but if you do that right now, you will get kernel panic. why?&lt;/p&gt;
&lt;p&gt;a kernel cannot boot without a root filesystem. if you start the kernel without a root filesystem, it will panic. so we need to create a minimal initrd (initial ramdisk) that contains busybox (appreciation for it and how great it is later).&lt;/p&gt;
&lt;p&gt;to make a filesystem for the initrd, create a directory structure like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir ~/initrd
cd ~/initrd
mkdir -p bin sbin etc proc sys usr/bin usr/sbin dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;why to make all these directories?&lt;br /&gt;
linux expects &lt;code&gt;/bin&lt;/code&gt;, &lt;code&gt;/proc&lt;/code&gt;, &lt;code&gt;/sys&lt;/code&gt;, &lt;code&gt;/dev&lt;/code&gt; inside rootfs.&lt;/p&gt;
&lt;h2&gt;more on file system structure&lt;/h2&gt;
&lt;p&gt;if you are just trying to boot the kernel, and are not interested in learning about linux filesystem structure, you can skip this section.&lt;/p&gt;
&lt;h2&gt;copy busybox binary&lt;/h2&gt;
&lt;p&gt;by default when you install busybox, it installs symlinks for all the commands it provides. we can copy the busybox binary to our initrd and create symlinks for the commands we want to use.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cp /usr/bin/busybox bin/
cd bin
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;create symlinks for common commands&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;for i in $(./busybox --list); do ln -s busybox $i; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://dhrm1k.github.io/when&quot;&gt;when&lt;/a&gt; doing this, you will see the warning, &lt;code&gt;ln: failed to create symbolic link &#39;busybox&#39;: File exists&lt;/code&gt;. you can ignore this warning. this comes because the link creation loop tries to recreate the ‘busybox’ symlink but it already exists.&lt;/p&gt;
&lt;h2&gt;create an init script&lt;/h2&gt;
&lt;p&gt;return to initrd root:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd ~/initrd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;create an &lt;code&gt;init&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat &amp;gt; init &amp;lt;&amp;lt; &#39;EOF&#39;
#!/bin/sh
echo &amp;quot;booted into dharmik&#39;s custom linux kernel!&amp;quot;

mount -t proc none /proc
mount -t sysfs none /sys

exec /bin/sh
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;if wanted, instead of the script above, you can have a pretty ascii art welcome message. here&#39;s a tool to generate ascii art: &lt;a href=&quot;https://patorjk.com/software/taag/#p=display&amp;amp;f=Doom&amp;amp;t=welcome+to+dharmik%27s+kernel&amp;amp;x=none&amp;amp;v=4&amp;amp;h=4&amp;amp;w=80&amp;amp;we=false&quot;&gt;patorjk.com/software/taag&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;make it executable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chmod +x init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;why we made this file?  the kernel runs &lt;code&gt;/init&lt;/code&gt; immediately after boot.  this is your &amp;quot;userspace&amp;quot;.  busybox provides &lt;code&gt;/bin/sh&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;packing the initrd&lt;/h2&gt;
&lt;p&gt;now we need to pack the initrd into a cpio archive and compress it. from inside the &lt;code&gt;~/initrd&lt;/code&gt; directory, run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;find . -print0 | cpio --null -ov --format=newc | gzip -9 &amp;gt; ../initrd.img
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;find . -print0 | cpio --null -ov --format=newc &amp;gt; ../initrd.cpio
cd ..
zstd initrd.cpio
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this will create a compressed initrd image at &lt;code&gt;~/initrd.img&lt;/code&gt; or &lt;code&gt;~/iinitrd.cpio.zst&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;booting the kernel with qemu&lt;/h2&gt;
&lt;p&gt;now we have everything we need to boot our custom kernel. we will use qemu to boot the kernel with the initrd we created.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;qemu-system-x86_64 &#92;
    -kernel /path/to/linux/arch/x86/boot/bzImage &#92;
    -initrd /path/to/initrd.img &#92;
    -append &amp;quot;console=ttyS0 rdinit=/init&amp;quot; &#92;
    -nographic
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kernel&lt;/code&gt;: boot this kernel image.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;initrd&lt;/code&gt;: load your initrd filesystem.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;append&lt;/code&gt;: kernel command line parameters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;console=ttyS0&lt;/code&gt;: print everything on serial port.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rdinit=/init&lt;/code&gt;: tell kernel to run /init from initrd.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nographic&lt;/code&gt;: run QEMU in terminal (no GUI).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;if everything goes well, you should see the message from your init script:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;booted into dharmik&#39;s custom linux kernel!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this message confirms that your custom kernel has booted successfully with your initrd.&lt;/p&gt;
&lt;p&gt;you will also see a warning like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;/bin/sh: can&#39;t access tty; job control turned off
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this is because we have not yet mounted &lt;code&gt;/dev/tty&lt;/code&gt;. you can ignore this warning for now. right now, it&#39;s just busybox&#39;s shell directly running on top of the kernel without any terminal support.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/building-booting-kernel-one/</link>
      <guid>https://dhrm1k.github.io/blog/building-booting-kernel-one/</guid>
      <pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>building and booting my own kernel on arch linux - part two</title>
      <description><![CDATA[&lt;p&gt;what do grown men wish for? wallpapers? drugs? rock-music (well, a little) idk. but the correct answer is building and booting your very own minimal linux distro.&lt;/p&gt;
&lt;p&gt;if you follow the given guide below for word by word, you will end up with a minimal linux distro that boots using your kernel. here&#39;s obligatory screenshot of babyos (bro, tell me i am not good at naming things) running in qemu:
&lt;img src=&quot;https://dhrm1k.github.io/assets/images/babyos-screenshot.png&quot; alt=&quot;babyos running in qemu&quot; /&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;well, part one was a sick learning experience. if you haven&#39;t read it yet and somehow ended up here, i recommend checking it out first: &lt;a href=&quot;https://dhrm1k.github.io/building-booting-kernel-one/&quot;&gt;building and booting my own kernel on arch linux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;i usually ssh into my arch linux machine from my main laptop, because well it&#39;s cold winters. i don&#39;t want to get out of my warm blankets, so the only terminal i use is kitty from my laptop, but today i decided to sit on the chair and use my arch machine directly and baby, i have lost the ability to type on a complete black screen. i love terminal and no that&#39;s not what i am talking about. i need a wallpaper now.&lt;/p&gt;
&lt;p&gt;i am right now using the cosmic de (been testing it for a while now, hopefully a blog about it too some day), but the default cosmic terminal has a black background. &lt;s&gt;i can change the background color, i think. let me see... no i can&#39;t. that too is not implemented yet. nor do i think it has a schema support for changing basic things. i might have contributed to it, but well, rust.&lt;/s&gt; it has support for themes. my bad.&lt;/p&gt;
&lt;p&gt;after understanding about the kernel configuration and how to build a basic kernel and boot it using qemu in part one. the question was what now? well, i wanted to build my own minimal distro that boots using my custom kernel, so let&#39;s get started for it.&lt;/p&gt;
&lt;p&gt;(jokes apart, i have always wanted something like bharatOS/indiaOS but well, i don&#39;t know how that will work out. there already exists something that is called boss linux and it is funded by the government of india, but well, i don&#39;t know how that is going. i even mailed them once asking for contributing, but they have never replied back. sad! nor do they have a public repo. i mean i get it, it&#39;s just a debian fork, but well, open source is about collaboration and sharing knowledge. not just making a distro and keeping it to yourself. the iso files are available for download on their website, but that&#39;s it.)&lt;/p&gt;
&lt;h2&gt;building babyos: let&#39;s make a minimal distro&lt;/h2&gt;
&lt;p&gt;i&#39;d love the chance to revise a few things first. busybox is a single binary that acts like multiple core unix commands. it is often used in embedded systems and minimal linux distributions. one of the few distro that includes/uses busybox is puppy linux (though i remember reading, there&#39;s a catch to that too).&lt;/p&gt;
&lt;p&gt;also another thing is initramfs. initramfs is a temporary root filesystem that is loaded into memory during the boot process. it contains the necessary files and drivers needed to mount the root filesystem and start the init process. in layman&#39;s terms, it solves the chicken and egg problem of needing drivers to access the root filesystem, but needing the root filesystem to load the drivers.&lt;/p&gt;
&lt;p&gt;now, let&#39;s get started with building babyos. i assume that you have already built your custom kernel as explained in part one. if not, please do so before proceeding. i am open to any questions about anything/regarding the post. contact me via email given in the contact section or ping me on twttr @dhrm1k.&lt;/p&gt;
&lt;h3&gt;step 1: verify you have everything&lt;/h3&gt;
&lt;p&gt;by default the kernel build process creates a bzimage file in the &lt;code&gt;yourfolder/arch/x86/boot/bzImage&lt;/code&gt;. this is the kernel image that we will use.&lt;/p&gt;
&lt;p&gt;confirm that you have the other necessary packages installed too:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;which qemu-system-x86_64 || echo &amp;quot;qemu missing&amp;quot;
which busybox || echo &amp;quot;busybox missing&amp;quot;
busybox --help | head -n 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;now, create a folder for babyos and navigate into it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/babyOS/rootfs
cd ~/babyOS/rootfs
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;step 2: create the rootfs skeleton&lt;/h3&gt;
&lt;p&gt;what is rootfs? rootfs is the root filesystem that will be used by the kernel during boot. we will create a minimal rootfs and give it utilities using busybox.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p bin sbin lib lib64 usr/bin usr/sbin usr/lib
mkdir -p etc dev proc sys tmp var home root
chmod 755 tmp
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;step 3: populate rootfs with busybox&lt;/h3&gt;
&lt;p&gt;now, we will copy busybox binary to the rootfs and create necessary symlinks for the commands provided by busybox (under the assumption that you have already installed it).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cp /usr/bin/busybox bin/
cd bin
for i in $(./busybox --list); do ln -s busybox $i 2&amp;gt;/dev/null; done
cd ../..
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i&#39;d like to focus more on why we create this symlink and how it works. essentially, these symlinks tell the kernel that whenever a command is called (ls, cat, etc.), it should execute the busybox binary with the name of the command as an argument. busybox then interprets this argument and executes the corresponding functionality.&lt;/p&gt;
&lt;p&gt;fun fact: the os for routers which is quite popular, openwrt, also uses busybox for providing basic unix commands.&lt;/p&gt;
&lt;h3&gt;step 4: setting up config files (passwd, group, inittab)&lt;/h3&gt;
&lt;p&gt;here we will give our operating system configuration files like the user database, group database and inittab.&lt;/p&gt;
&lt;p&gt;the password database file (&lt;code&gt;/etc/passwd&lt;/code&gt;) contains information about the user accounts on the system. we will create a minimal passwd file with a room for the root user. it tells the system every time it boots up that there is a root user with a uid of 0 and a home directory of /root.&lt;/p&gt;
&lt;p&gt;another fun fact: recently i was working on a jail shell. what a jail shell is that it restricts a particular user to a specific directory and prevents them from accessing files outside that directory. while doing that, i had to edit the &lt;code&gt;/etc/passwd&lt;/code&gt; file to add a new user for the jail shell. it was quite a fun experience. the problem with not doing it as a jail shell and customly parsing the commands that the user enters is that the tab autocomplete and other shell features won&#39;t work properly, so i had to create a proper user in the passwd file.&lt;/p&gt;
&lt;p&gt;back to here to creating the passwd file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat &amp;gt; etc/passwd &amp;lt;&amp;lt;EOF
root::0:0:root:/root:/bin/sh
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the format here for creating the user is as follows:
&lt;code&gt;username:password:uid:gid:comment/user_description:home_directory:shell&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;the uid of the root user is always 0. the password field right now is left empty for simplicity, but in a real-world scenario, you should set a proper password. the gid is also 0 for the root user.&lt;/p&gt;
&lt;p&gt;now, let&#39;s create the group database file (&lt;code&gt;/etc/group&lt;/code&gt;). the file contains information about the groups on the system. understanding user sounds intuitive consider most operating systems have it, but you must be wondering what groups are. groups are a way to organize users and manage permissions. for example, you can have a group for administrators, another for regular users. each user can belong to one or more groups. this helps in managing permissions and access control.&lt;/p&gt;
&lt;p&gt;let&#39;s create a minimal group file with a room for the root group:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat &amp;gt; etc/group &amp;lt;&amp;lt;EOF
root::0:
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the format here for creating the group is as follows:
&lt;code&gt;group_name:password:gid:user_list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;one of the thing that&#39;s on my mind though is that if we need the group file at all, considering that we have only one user (root) and one group (root). if a system boots up without a group file. well, i guess we will find out later.&lt;/p&gt;
&lt;p&gt;added later: well, it does seem to boot up without any issues even if the group file is not present.&lt;/p&gt;
&lt;p&gt;now, let&#39;s create the inittab file (&lt;code&gt;/etc/inittab&lt;/code&gt;). inittab is a configuration file that tells the init process what to do during the boot process. it defines the runlevels and the processes that should be started.&lt;/p&gt;
&lt;p&gt;in the previous part, we discussed that initramfs is a temporary root filesystem that is loaded into memory during boot. initramfs is loaded into ram and is not persistent. once the kernel has booted and the real root file system is mounted, the initramfs is no longer needed and is discarded, whereas the rootfs we are creating is the actual root filesystem that will be used by kernel after booting.&lt;/p&gt;
&lt;p&gt;let&#39;s create a minimal inittab file that starts a shell on boot:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat &amp;gt; etc/inittab &amp;lt;&amp;lt;EOF
::sysinit:/etc/init.d/rcS
ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the format here for creating the inittab entries is as follows:
&lt;code&gt;id:runlevels:action:process&lt;/code&gt;, so &lt;code&gt;::sysinit:/etc/init.d/rcS&lt;/code&gt; means that run &lt;code&gt;/etc/init.d/rcS&lt;/code&gt; one time at boot before anything else.&lt;/p&gt;
&lt;p&gt;also, line 2 is the login terminal configuration. it tells the init process to start a getty process on ttyS0 (the first serial terminal) with a baud rate (speed. think of it as without it being exact, the letters on screen will not be formatted proper) of 115200 and using the vt100 (a terminal emulation type).&lt;/p&gt;
&lt;h3&gt;create the rcS init script&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p etc/init.d
cat &amp;gt; etc/init.d/rcS &amp;lt;&amp;lt;&#39;EOF&#39;
#!/bin/sh
# rcS - minimal startup script for BabyOS v0.1
echo &amp;quot;=== BabyOS rcS: starting ===&amp;quot;

# mount virtual filesystems the kernel exposes
mount -t proc none /proc || echo &amp;quot;mount /proc failed&amp;quot;
mount -t sysfs none /sys  || echo &amp;quot;mount /sys failed&amp;quot;

# dev: prefer devtmpfs (kernel-managed), fall back to mdev if not available
mount -t devtmpfs none /dev 2&amp;gt;/dev/null || {
  echo &amp;quot;devtmpfs not available, falling back to mdev&amp;quot;
  /bin/mdev -s
}

# create basic device nodes if kernel didn&#39;t (safe-guard)
[ -e /dev/console ] || /bin/mknod -m 600 /dev/console c 5 1
[ -e /dev/null    ] || /bin/mknod -m 666 /dev/null c 1 3
[ -e /dev/tty0    ] || /bin/mknod -m 666 /dev/tty0 c 4 0

# bring up loopback (important for some network stacks)
ifconfig lo up

# optional: set hostname (you can edit this later)
echo &amp;quot;baby-os&amp;quot; &amp;gt; /proc/sys/kernel/hostname

# tune kernel logging (reduce spam)
dmesg -n 1

# run any other one-shot startup tasks you want to add
# e.g. mount extra filesystems, set timezone, start network later
# /bin/mount -a

echo &amp;quot;=== BabyOS rcS: initialization complete ===&amp;quot;
EOF

chmod +x etc/init.d/rcS
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this script is executed during the boot process and performs several important tasks to set up the system properly. what it does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;it mounts the virtual filesystem like &lt;code&gt;/proc&lt;/code&gt; and &lt;code&gt;/sys&lt;/code&gt;, which are essential for the kernel to expose information about processes and system hardware.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;mount -t devtmpfs none /dev&lt;/code&gt;. modern kernels create device nodes automatically via devtmpfs. if the kernel doesn&#39;t support/allow it, we fallback to running &lt;code&gt;mdev -s&lt;/code&gt;, which creates &lt;code&gt;/dev&lt;/code&gt; nodes from uevents.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;mknod lines ensure crucial device nodes exist in case neither devtmpfs nor mdev created them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;s&gt;&lt;code&gt;ifconfig lo up&lt;/code&gt; brings the loopback interface up; important for some programs and for DHCP tests later.&lt;/s&gt; (this is not important right now, this only exists because i was trying something else earlier. and noted it down here.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;echo &amp;quot;baby-os&amp;quot; &amp;gt; /proc/sys/kernel/hostname&lt;/code&gt; sets a hostname quickly (optional).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;dmesg -n 1&lt;/code&gt; limits kernel log level printed to console (optional).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;create nodes in /dev&lt;/h3&gt;
&lt;p&gt;now, we will create necessary device nodes in the &lt;code&gt;/dev&lt;/code&gt; directory. device nodes are special files that represent hardware devices. they allow user-space programs to interact with hardware devices.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo mknod -m 600 rootfs/dev/console c 5 1
sudo mknod -m 666 rootfs/dev/null c 1 3
sudo mknod -m 666 rootfs/dev/tty c 5 0
sudo chown root:root rootfs/dev/* || true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;more on device nodes:&lt;/p&gt;
&lt;p&gt;device nodes are not real files. they are handles to the kernel to access hardware devices.&lt;/p&gt;
&lt;p&gt;for example, &lt;code&gt;/dev/console&lt;/code&gt; is the device node for the system console. without it, the early boot messages and login prompt won&#39;t work.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/dev/null&lt;/code&gt; is a special device that discards all data written to it. &lt;code&gt;/dev/tty&lt;/code&gt; represents the current terminal.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;c&lt;/code&gt; in the mknod command stands for character device. the number 5 1 for console means major number 5 and minor number 1. it&#39;s more complex than what i can comprehend or explain right now. i too will read more about it later and update this post or something.&lt;/p&gt;
&lt;h3&gt;creating rootfs.ext4 image&lt;/h3&gt;
&lt;p&gt;we need a real disk image for qemu to boot from. we will create an ext4 filesystem image and copy our rootfs into it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;dd if=/dev/zero of=rootfs.ext4 bs=1M count=128
mkfs.ext4 rootfs.ext4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;line 1 of the following command creates a blank 128MB file filled with zeros. line 2 formats that file as an ext4 filesystem. i myself had very little idea about filesystems before starting this post. also how helpful this dd utility is.&lt;/p&gt;
&lt;h3&gt;mount and populate the disk image&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd ~/babyOS
mkdir -p mnt
sudo mount rootfs.ext4 mnt
sudo cp -a rootfs/* mnt/
sync
sudo umount mnt
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;boot babyos with qemu&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;qemu-system-x86_64 &#92;
  -kernel ~/src/linux/arch/x86/boot/bzImage &#92;
  -drive file=rootfs.ext4,format=raw &#92;
  -append &amp;quot;root=/dev/sda console=ttyS0&amp;quot; &#92;
  -nographic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with this setup the os will boot up and you will be presented with a login prompt on the serial console. you can log in as root. if you had the same config, then you won&#39;t have to set up any password.&lt;/p&gt;
&lt;p&gt;once logged in, you can try out some basic commands provided by busybox.&lt;/p&gt;
&lt;p&gt;you won&#39;t be able to write to the root filesystem right now because it is mounted as read-only. to remount it as read-write, you can use the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mount -o remount, rw / || echo &amp;quot;remount / rw failed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;also, later your qemu run command has to be modified to include &lt;code&gt;rw&lt;/code&gt; in the kernel parameters like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;qemu-system-x86_64 &#92;
  -kernel ~/src/linux/arch/x86/boot/bzImage &#92;
  -drive file=rootfs.ext4,format=raw &#92;
  -append &amp;quot;root=/dev/sda rw console=ttyS0&amp;quot; &#92;
  -nographic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tip: i had to google it on how to exit qemu. it&#39;s ctrl + a, then x.&lt;/p&gt;
&lt;p&gt;this will remount the root filesystem in read-write mode during boot.&lt;/p&gt;
&lt;h3&gt;ascii art&lt;/h3&gt;
&lt;p&gt;can anything be over without ascii art? no. so here it is:&lt;/p&gt;
&lt;p&gt;from what i read, after busybox login, /etc/motd is displayed. motd stands for message of the day. so let&#39;s create one:&lt;/p&gt;
&lt;p&gt;(the ascii art didn&#39;t work from etc/motd. it is probably because the tty is not configured properly. i will fix it later. for now, i am putting it in /etc/init.d/rcS to display it on every boot.)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;
cat &amp;gt; /etc/motd &amp;lt;&amp;lt; &amp;quot;EOF&amp;quot;
 o            o                            
O            O                             
O            O                             
o            o                             
OoOo. .oOoO&#39; OoOo. O   o       .oOo. .oOo  
O   o O   o  O   o o   O       O   o `Ooo. 
o   O o   O  o   O O   o       o   O     O 
`OoO&#39; `OoO&#39;o `OoO&#39; `OoOO       `OoO&#39; `OoO&#39; 
                       o                   
                    OoO&#39;
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i changed the ascii art in the screenshot because i had no energy to deal with formatting it properly.&lt;/p&gt;
&lt;p&gt;i enjoyed writing this post. it was an experience.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/building-booting-kernel-two/</link>
      <guid>https://dhrm1k.github.io/blog/building-booting-kernel-two/</guid>
      <pubDate>Sun, 07 Dec 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>how we make time bearable</title>
      <description><![CDATA[&lt;p&gt;if you have met me, and you are a friend, there’s a high chance you already know my other friends too. one of them is &lt;a href=&quot;https://rahulpat1l.github.io/&quot;&gt;rahul&lt;/a&gt;. he’s going back to my school to introduce 12th graders to pure sciences and academia. he is currently pursuing a bachelors at iiser bhopal. and like any sane person would, he doesn’t want to be accountable for any advice he gives to students who are about to make life-changing decisions.&lt;/p&gt;
&lt;p&gt;we were talking about this right now when we met for tea at 12 am. yes, we do that often. he’s not in the city for most of the time, so it feels worth stepping out whenever he is. i don’t remember how we arrived there, but the gist of the conversation slowly boiled down to one question: why do we do what we do?&lt;/p&gt;
&lt;p&gt;for a school-going kid who was happy doing nothing and having fun on the last bench (i personally think the last bench gives you more attention. if you want to be ignored completely, sit somewhere near the front), this question doesn’t even exist. how do you explain to a kid like that that one day it will matter what he works on? or that he will have to work at all? and why does it suddenly matter whether he likes what he does, or whether he’s good at it? why does he have to choose?&lt;/p&gt;
&lt;p&gt;my opinion on that is inspired by charles baudelaire’s famous poem &amp;quot;be drunk&amp;quot;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;you have to be always drunk. that’s all there is to it. it’s the only way. so as not to feel the horrible burden of time that breaks your back and bends you to the earth, you have to be continually drunk.&lt;br /&gt;
but on what? wine, poetry or virtue, as you wish. but be drunk.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;i have loved this poem ever since i first read it. baudelaire understood that time is a burden. if you really think about it, it’s overwhelming to realize that you are moving toward death every second. not that this is something to fear, or even something to avoid thinking about entirely. i actually believe death is the only real certainty we have in life. but if you keep that fact in your head all the time, you are going to be miserable.&lt;/p&gt;
&lt;p&gt;so what do we do? we get drunk. metaphorically. and in my case, literally isn’t even an option. i live in gujarat. it’s a dry state.&lt;/p&gt;
&lt;p&gt;baudelaire’s suggestion isn’t escapism. it’s engagement. be interested in something deeply enough that life doesn’t feel like days are just passing by. something that makes you forget about time altogether. something that makes you want to wake up the next day and do it all over again.&lt;/p&gt;
&lt;p&gt;i think even if people were given the choice to do nothing at all, most of them would still end up doing something. humans don’t sit well with emptiness. we look for friction. for attachment. for meaning, even if we have to invent it. without it, i don’t think it would have been possible for humans, as a collective species, to have survived this long. there’s a really good veritasium video about how humans are wired for meaning.&lt;/p&gt;
&lt;p&gt;i hope my opinion changes in the future. but then again, isn’t everything a facade? not in a fake sense, but in a survival sense. we do what we do to feel alive. to feel like we are more than just bodies moving through space and time, following instructions until we stop working.&lt;/p&gt;
&lt;p&gt;i spend hours on my computer doing things that might not make sense to anyone else. staring at text. breaking things. fixing things. building things that may never matter. but to me, that’s what makes me feel alive. it gives shape to time. it gives me a reason to wake up excited some days. i love it. we partly do many things because we don’t want to think about the things that are wrong in our lives.&lt;/p&gt;
&lt;p&gt;and i think this is what humans have always done. we find something to be drunk on. something that gives us a sense of meaning, even if it’s fragile or temporary. what does a family give you, if not that? a sense of belonging. a reason to show up. a purpose you accept, or sometimes consciously create for yourself.&lt;/p&gt;
&lt;p&gt;around the same time, i was watching the &amp;quot;tomorrow war&amp;quot;. it’s not a deep movie, but there was a line that stayed with me. the plot is about people from the future coming back to the present to recruit civilians to fight an alien invasion. the main character agrees to go, not because he wants to be a hero, but because he wants to see his daughter grow up.&lt;/p&gt;
&lt;p&gt;there’s a line where he says something like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;i’m not a hero. i was trying to save my daughter. if i have to save the world to save her, then i’m damn sure gonna do it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;that line stuck because that’s purpose in its rawest form. not noble. not abstract. not philosophical. just deeply personal. and strong enough to make someone walk into hell willingly.&lt;/p&gt;
&lt;p&gt;which brings me to a question that always comes up. there are so many people who don’t do what they love. they don’t chase passion. they don’t romanticize work or purpose. how do they survive?&lt;/p&gt;
&lt;p&gt;i think the answer, at least partly, is love. i genuinely believe that someone in a healthy romantic relationship can survive almost anything. love gives you a reason to live that exists outside of yourself. you don’t have to invent purpose every day. it’s already there, waiting for you in another person.&lt;/p&gt;
&lt;p&gt;and not everyone gets that. not everyone wants that. so it seems we find our own intoxicants. work. curiosity. ambition. faith. people. ideas. responsibility.&lt;/p&gt;
&lt;p&gt;maybe that’s enough. all i care about is if everyone is smiling.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/how-we-make-time-bearable/</link>
      <guid>https://dhrm1k.github.io/blog/how-we-make-time-bearable/</guid>
      <pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>admiration for arch linux</title>
      <description><![CDATA[&lt;p&gt;obligatory neofetch screenshot:
&lt;img src=&quot;https://dhrm1k.github.io/assets/images/arch-neofetch.png&quot; alt=&quot;neofetch&quot; /&gt;&lt;/p&gt;
&lt;p&gt;the way i came to arch linux is because what choice did i have? that is what all cool kids do, right? and ain&#39;t i the brand ambassador of the cool department? i too wanted to say &amp;quot;i use arch btw&amp;quot;.&lt;/p&gt;
&lt;p&gt;but as uncle ben put it, &amp;quot;with great power comes great responsibility&amp;quot;. and arch linux is pure sheer power. it gives you power to build your own system from the ground up. it gives you power to choose what you want and what you don&#39;t want. it gives you power to learn and understand how linux works.&lt;/p&gt;
&lt;p&gt;i didn&#39;t have this admiration always. i was a poopyhead for a long time. i have backups on this device of photos that are of size 60gb. i thought i made a bad choice of not choosing something stable, but i was wrong.&lt;/p&gt;
&lt;p&gt;there are many instances when i broke my system, but learned to fix it. one particular instance which caught my eye and heart was today. i was trying to setup a vm with qemu on my machine. i had already spent time setting up the same on my pop os machine, but well it was weekend and i was tired of looking at my laptop screen (you bloody gnome) so i moved to my desktop.&lt;/p&gt;
&lt;p&gt;i followed the same steps as i did on pop os, but it didn&#39;t work. everything worked except one thing which was dns. i tried everything. for a second i thought it was problem with my very own dns server, but no locally everything worked.&lt;/p&gt;
&lt;p&gt;i started and restarted the vm, hoping it would work, but no luck. i googled and googled, but nothing worked. i was about to give up, when i thought of checking the arch wiki.&lt;/p&gt;
&lt;p&gt;apparently, on pop os, preconfigures things for you (i have to read more about it later), but on arch, by default, there&#39;s no firewall rules set.&lt;/p&gt;
&lt;p&gt;on my machine, i had nftables enabled, and my forward chain had a default policy drop. so anything being forwarded, including vm traffic, was dropped unless i explicitly allowed it.&lt;/p&gt;
&lt;p&gt;i thought it&#39;s the default state on arch linux, but i was wrong. it has to be some package that i installed which enabled nftables with default drop policy.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; [dharmik@archlinux ~]$ sudo nft list ruleset

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;all i had to do was explicitly allow dns traffic within the forward chain.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nft add rule inet filter forward udp dport 53 accept
sudo nft add rule inet filter forward tcp dport 53 accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and voila! it worked. the vm could now access the internet.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; [dharmik@archlinux ~]$ sudo nft list ruleset
chain forward {
                type filter hook forward priority filter; policy drop;
                udp dport 53 accept
                tcp dport 53 accept
        }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;since vm traffic goes through the host’s forward chain, dns packets (udp/tcp port 53) were getting dropped. all i had to do was explicitly allow dns traffic in the forward chain.&lt;/p&gt;
&lt;p&gt;i love that. arch didn’t guess what i wanted. it waited for me to be explicit. that’s not inconvenience. that’s honesty. that’s control. that’s why i like arch linux. i did shed a tear that i have no account or memory of what on earth i was trying in the first place, but tears of joy, of course. i hope you guys had a happy weekend too.&lt;/p&gt;
&lt;p&gt;note: this post was supposed to be published on 21st dec 2025, but i had a few doubts about the nft commands and the default state of firewall rules on arch machine. so i waited till today to confirm it all. yes, the default policy is drop on arch linux fresh install.&lt;/p&gt;
&lt;p&gt;later, while setting up the vm further, i realized that forwarding itself was enabled, but the firewall policy was still dropping forwarded packets. i had to scratch my head on why i couldn&#39;t update the openwrt packages, because i could reach the internet and also could ping google from within the openwrt vm.&lt;/p&gt;
&lt;p&gt;turns out the real issue was still forwarding. openwrt package downloads originate inside the vm, so they rely on the host’s forward chain. once i allowed forwarded traffic, package updates immediately started working.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo iptables -P FORWARD ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i had briefly looked at the OUTPUT chain while debugging, but the actual fix was relaxing the forward policy.&lt;/p&gt;
&lt;p&gt;it’s funny that until recently, i had no knowledge of firewalls or iptables/nftables and was living in ignorance and bliss. now that i know about them, i’m disturbed by them all the time. i can’t unknow it anymore.&lt;/p&gt;
&lt;p&gt;though there&#39;s a lot i haven&#39;t learnt yet, i am open to it. i don&#39;t know what i will try next yet.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;something else also happened. i had a lot of local commits in a repo and i wanted to start afresh from remote. so i did git log first to get the hash/commit id of the last commit on remote. but guess what, i got&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;less not found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;well, not the first time i was doing git log, right? probably some bug in the routine system upgrade on my arch machine. without much thought, i did&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo pacman -Syu
sudo pacman -S less
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to which it says less already installed and up to date. what the heck? i go back and try git log again. well, this time less works but the error i get is github.com not found. arrrrrrgggghhh, i thought. my dns server was down again.&lt;/p&gt;
&lt;p&gt;i picked up my laptop, because my router was down already and i was on my computer connected via lan cable. from my laptop, i ssh into my vps and see there&#39;s no storage left. someone&#39;s been ddos-ing my dns server again and the logs have filled up my storage. i clear some logs, restart the dns and it works again. phew!&lt;/p&gt;
&lt;p&gt;but to those ddos-ers, please find a new hobby. this is old.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/arch-admiration/</link>
      <guid>https://dhrm1k.github.io/blog/arch-admiration/</guid>
      <pubDate>Sat, 27 Dec 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>klish tutorial</title>
      <description><![CDATA[&lt;p&gt;note: amidst writing this tutorial, i realized that klish 3 documentation in itself is quite self-sufficient. nevertheless, i am publishing this hoping it serves as either a quickstart guide or notes for me for future references.&lt;/p&gt;
&lt;p&gt;klish is a framework for building command line interfaces (clis). it lets you define the structure of your cli using simple xml files, takes care of parsing user input, and gives you some very sick tab autocompletion out of the box (along with escape characters, history, etc).&lt;/p&gt;
&lt;p&gt;this is not a comprehensive tutorial. it’s more of a quickstart to get you up and running with klish. for deeper details, refer to the &lt;a href=&quot;https://src.libcode.org/pkun/klish/src/master/docs/klish3.en.md&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;the main reason this post exists is because there’s surprisingly little about klish on the internet, and i wanted to change that.&lt;/p&gt;
&lt;p&gt;i’ve been behind on writing this, and there’s a reason for it. initially, i was working with klish 2.2 (the version available in the openwrt package manager), while the latest release is klish 3.&lt;/p&gt;
&lt;p&gt;from an architectural standpoint, a lot has changed. i’m now back in the learning phase myself and need to understand the newer version before continuing.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;one of the first things to do if you are setting up klish3 on your openwrt vm then is to install less (gnu less), because the busy box less that comes with openwrt does not support the -e option that klish uses when you have the pager enabled.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;opkg update
opkg install less
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i have took the trouble to build the latest klish from the source code for x86_64 architecture. you will be required to downgrade libxml2 to the version it requires. also you will need to install faux. i have all the packages &lt;a href=&quot;https://dhrm1k.github.io/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;setting up&lt;/h2&gt;
&lt;p&gt;after installation, you will have the klish binary at &lt;code&gt;/usr/bin/klish&lt;/code&gt;. you still need to set up a few things before you can run it.&lt;/p&gt;
&lt;p&gt;first, create a directory to hold your klish configuration files.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;mkdir -p /etc/klish/xml
cd /etc/klish
cat &amp;gt; klish.conf &amp;lt;&amp;lt; EOF
paste the contents of klish.conf from https://github.com/dhrm1k/klish/blob/master/klish.conf here.
EOF


cat &amp;gt; klishd.conf &amp;lt;&amp;lt; EOF
paste the contents of klishd.conf from https://github.com/dhrm1k/klish/blob/master/klishd.conf here.
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;make sure to adjust the paths in these configuration files. in particular, for klishd.conf, set the &lt;code&gt;DB.libxmls.XMLPath&lt;/code&gt; parameter to point to your configuation directory (i.e. &lt;code&gt;/etc/klish&lt;/code&gt; here).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DB.libxml2.XMLPath=/etc/klish/xml/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;there&#39;s a really good example configuration in the klish soruce code repository. you can copy the xml file from there to your configuration directory to get started.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;cat &amp;gt; /etc/klish/xml/example.xml &amp;lt;&amp;lt; EOF
paste the contents from example.xml at https://github.com/dhrm1k/klish/blob/master/examples/simple/example.xml here.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;now you need to run the klish daemon with &lt;code&gt;klishd&lt;/code&gt; to load the configuration. you can run it with the foreground option &lt;code&gt;-foreground&lt;/code&gt; for testing purposes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;the commands&lt;/h2&gt;
&lt;p&gt;this was the easy part. the main reason of writing this tutorial was to understand how to implement the command handlers. the basic xml for basic commands is pretty straightforward (but it does get a little dirty on a little user experience edge cases, more on that later).&lt;/p&gt;
&lt;h3&gt;ptype&lt;/h3&gt;
&lt;p&gt;when starting out, i myself was wondering, if ptype is some special type defined by klish or limited to klish or is it something more general. turns out, ptype is just short for parameter type. it defines the type of argument that a command takes.&lt;/p&gt;
&lt;p&gt;for example, in the example.xml file, you can see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;PTYPE name=&amp;quot;STRING&amp;quot;&amp;gt;
	&amp;lt;ACTION sym=&amp;quot;STRING@klish&amp;quot;/&amp;gt;
&amp;lt;/PTYPE&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this defines a parameter type named STRING, which takes a string argument.&lt;/p&gt;
&lt;p&gt;klish comes with a set of built-in ptypes, which you can find in the official documentation. i am also adding it here for reference:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;STRING: any string&lt;/li&gt;
&lt;li&gt;INT: integer&lt;/li&gt;
&lt;li&gt;UINT: unsigned integer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;you can also define your own ptypes. let&#39;s say you want to define a ptype for an ip address. now why would you want to do that? let&#39;s say you want to implement a command that takes an ip address as an argument (ping command, anyone?). you can define a ptype for an ip address as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;PTYPE name=&amp;quot;IP_ADDR&amp;quot;&amp;gt;
	&amp;lt;ACTION sym=&amp;quot;REGEX@klish&amp;quot;&amp;gt;
		^([0-9]{1,3}&#92;.){3}[0-9]{1,3}$
	&amp;lt;/ACTION&amp;gt;
&amp;lt;/PTYPE&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and you will be using it as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;COMMAND name=&amp;quot;ping&amp;quot; help=&amp;quot;Ping an IP address&amp;quot;&amp;gt;
	&amp;lt;PARAM name=&amp;quot;ip&amp;quot; ptype=&amp;quot;IP_ADDR&amp;quot; help=&amp;quot;IP address&amp;quot;/&amp;gt;
	&amp;lt;ACTION sym=&amp;quot;script&amp;quot;&amp;gt;ping -c 4 ${KLISH_PARAM_ip}&amp;lt;/ACTION&amp;gt;
&amp;lt;/COMMAND&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;actions&lt;/h3&gt;
&lt;p&gt;you have seen the action tag in the above examples. action defines what happens when a command ois executed when &lt;code&gt;sym&lt;/code&gt; is set to &lt;code&gt;script&lt;/code&gt;. above, you see, sym is set to &lt;code&gt;REGEX@klish&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;this means that klish will use the regex defined in the action tag to validate the input for that ptype. &lt;code&gt;REGEX@klish&lt;/code&gt; is a built-in action that is part of the default klish plugin.&lt;/p&gt;
&lt;p&gt;in the default example.xml file, you can see that prompt uses an action with sym set to &lt;code&gt;PROMPT@klish&lt;/code&gt;. this is another built-in action that is used to customize the prompt/appearance.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;PROMPT name=&amp;quot;prompt&amp;quot;&amp;gt;
	&amp;lt;ACTION sym=&amp;quot;prompt&amp;quot;&amp;gt;%u@%h&amp;amp;gt; &amp;lt;/ACTION&amp;gt;
&amp;lt;/PROMPT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to even give an example of how to use the parameters in the action script, here&#39;s a greet command that takes name and age as parameters and prints a greeting message.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;COMMAND name=&amp;quot;greet&amp;quot; help=&amp;quot;Greet user&amp;quot;&amp;gt;	
	&amp;lt;PARAM name=&amp;quot;name&amp;quot; ptype=&amp;quot;STRING&amp;quot; help=&amp;quot;Name&amp;quot;/&amp;gt;
	&amp;lt;PARAM name=&amp;quot;age&amp;quot; ptype=&amp;quot;INT&amp;quot; help=&amp;quot;Age&amp;quot;/&amp;gt;
	&amp;lt;ACTION sym=&amp;quot;script&amp;quot;&amp;gt;
		echo &amp;quot;hii, $KLISH_PARAM_name! you are $KLISH_PARAM_age years old&amp;quot;
	&amp;lt;/ACTION&amp;gt;
&amp;lt;/COMMAND&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;command hierarchy&lt;/h3&gt;
&lt;p&gt;commands can be organized in a hierarch to give better autocomplete experience. basic commands have already been implemented above. i&#39;d like to give a example to set up hierarchial commands. for example,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;COMMAND name=&amp;quot;show&amp;quot; help=&amp;quot;Show information&amp;quot;&amp;gt;
	&amp;lt;COMMAND name=&amp;quot;interface&amp;quot; help=&amp;quot;Show interface information&amp;quot;&amp;gt;
		&amp;lt;COMMAND name=&amp;quot;eth0&amp;quot; help=&amp;quot;Show eth0 details&amp;quot;&amp;gt;
			&amp;lt;ACTION sym=&amp;quot;script&amp;quot;&amp;gt;ip addr show eth0&amp;lt;/ACTION&amp;gt;
		&amp;lt;/COMMAND&amp;gt;
		&amp;lt;COMMAND name=&amp;quot;wlan0&amp;quot; help=&amp;quot;Show wlan0 details&amp;quot;&amp;gt;
			&amp;lt;ACTION sym=&amp;quot;script&amp;quot;&amp;gt;ip addr show wlan0&amp;lt;/ACTION&amp;gt;
		&amp;lt;/COMMAND&amp;gt;
	&amp;lt;/COMMAND&amp;gt;
&amp;lt;/COMMAND&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;root@OpenWrt&amp;gt; show interface
     eth0  wlan0
root@OpenWrt&amp;gt; show interface eth0
2: eth0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;views&lt;/h3&gt;
&lt;p&gt;a view is a context or mode that contains its own set of commands, prompts, and behavior. users navigate between views to access different functionality.&lt;/p&gt;
&lt;p&gt;an example of defining multiple views is as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;VIEW name=&amp;quot;main&amp;quot;&amp;gt;
	&amp;lt;PROMPT name=&amp;quot;prompt&amp;quot;&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;prompt&amp;quot;&amp;gt;user&amp;amp;gt; &amp;lt;/ACTION&amp;gt;
	&amp;lt;/PROMPT&amp;gt;
	
	&amp;lt;COMMAND name=&amp;quot;exit&amp;quot; help=&amp;quot;Exit CLI&amp;quot;&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;nav&amp;quot;&amp;gt;pop&amp;lt;/ACTION&amp;gt;
	&amp;lt;/COMMAND&amp;gt;
	
	&amp;lt;COMMAND name=&amp;quot;show&amp;quot; help=&amp;quot;Show information&amp;quot;&amp;gt;
		&amp;lt;COMMAND name=&amp;quot;version&amp;quot; help=&amp;quot;Show version&amp;quot;&amp;gt;
			&amp;lt;ACTION sym=&amp;quot;printl&amp;quot;&amp;gt;Version 1.0.0&amp;lt;/ACTION&amp;gt;
		&amp;lt;/COMMAND&amp;gt;
	&amp;lt;/COMMAND&amp;gt;
	
	&amp;lt;!-- Command to enter config view --&amp;gt;
	&amp;lt;COMMAND name=&amp;quot;configure&amp;quot; help=&amp;quot;Enter configuration mode&amp;quot;&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;nav&amp;quot;&amp;gt;push config&amp;lt;/ACTION&amp;gt;
	&amp;lt;/COMMAND&amp;gt;
&amp;lt;/VIEW&amp;gt;

&amp;lt;!-- Config VIEW: Configuration mode --&amp;gt;
&amp;lt;VIEW name=&amp;quot;config&amp;quot;&amp;gt;
	&amp;lt;PROMPT name=&amp;quot;prompt&amp;quot;&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;prompt&amp;quot;&amp;gt;config&amp;amp;gt; &amp;lt;/ACTION&amp;gt;
	&amp;lt;/PROMPT&amp;gt;
	
	&amp;lt;COMMAND name=&amp;quot;exit&amp;quot; help=&amp;quot;Exit to main view&amp;quot;&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;nav&amp;quot;&amp;gt;pop&amp;lt;/ACTION&amp;gt;
	&amp;lt;/COMMAND&amp;gt;
	
	&amp;lt;COMMAND name=&amp;quot;hostname&amp;quot; help=&amp;quot;Set hostname&amp;quot;&amp;gt;
		&amp;lt;PARAM name=&amp;quot;name&amp;quot; ptype=&amp;quot;STRING&amp;quot; help=&amp;quot;Hostname&amp;quot;/&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;script&amp;quot;&amp;gt;
			echo &amp;quot;Setting hostname to $KLISH_PARAM_name&amp;quot;
			# hostname $KLISH_PARAM_name
		&amp;lt;/ACTION&amp;gt;
	&amp;lt;/COMMAND&amp;gt;
	
	&amp;lt;COMMAND name=&amp;quot;ip&amp;quot; help=&amp;quot;IP configuration&amp;quot;&amp;gt;
		&amp;lt;PARAM name=&amp;quot;address&amp;quot; ptype=&amp;quot;STRING&amp;quot; help=&amp;quot;IP address&amp;quot;/&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;script&amp;quot;&amp;gt;
			echo &amp;quot;Setting IP to $KLISH_PARAM_address&amp;quot;
		&amp;lt;/ACTION&amp;gt;
	&amp;lt;/COMMAND&amp;gt;
&amp;lt;/VIEW&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this was intuitive and easy to implement.&lt;/p&gt;
&lt;p&gt;the thing that&#39;s been taking my mind and time is implementing a password protected view. i did achieve that in klish 2.2 by a custom action script that prompted for password input with the &lt;code&gt;read -s&lt;/code&gt; flag to hide input. however, in klish 3, i am not able to get that working yet. i will experiment more and update this tutorial for that part when i have something working.&lt;/p&gt;
&lt;p&gt;well, it seems i will have to write a custom plugin to test this.&lt;/p&gt;
&lt;p&gt;voila, i was an inch away from giving up when i found a way. after nothing worked, i thought to write a custom auth plugin in klish, but installing gcc made my vm run out of space, and well it&#39;s 3:55 am. though, i am not exhaused, i want to think about other pretty things.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;	&amp;lt;COMMAND name=&amp;quot;enable&amp;quot; help=&amp;quot;Enter privileged mode (password required)&amp;quot;&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;script&amp;quot; in=&amp;quot;tty&amp;quot; out=&amp;quot;tty&amp;quot;&amp;gt;&amp;lt;![CDATA[
#!/bin/sh

# Read password with hidden input using read -s
read -s -p &amp;quot;Password: &amp;quot; password
echo &amp;quot;&amp;quot;

# Check password
if [ &amp;quot;$password&amp;quot; = &amp;quot;admin123&amp;quot; ]; then
    echo &amp;quot;Access granted&amp;quot;
    exit 0
else
    echo &amp;quot;Access denied&amp;quot;
    exit 1
fi
]]&amp;gt;&amp;lt;/ACTION&amp;gt;
		&amp;lt;ACTION sym=&amp;quot;nav&amp;quot;&amp;gt;push enable&amp;lt;/ACTION&amp;gt;
	&amp;lt;/COMMAND&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this worked. to answer why,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;&amp;lt;ACTION sym=&amp;quot;script&amp;quot; in=&amp;quot;tty&amp;quot; out=&amp;quot;tty&amp;quot;&amp;gt;
    read -s -p &amp;quot;Password: &amp;quot; password
&amp;lt;/ACTION&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this tells klish to create a proper PTY and set KTP_STATUS_NEED_STDIN and KTP_STATUS_INTERACTIVE flags. even then, there&#39;s still a conflict: the client-side tinyrl holds the terminal in raw mode, and while it does call tinyrl_raw_mode() for interactive commands (line 570-571 in klish.c).&lt;/p&gt;
&lt;p&gt;the version of klish on my machine is &lt;code&gt;klish - 3.2.0-1&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;this is a ongoing tutorial. i will add more here. until then, if any questions, feel free to mail me. my email address is in the contact section of this webpage.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/klish-tutorial/</link>
      <guid>https://dhrm1k.github.io/blog/klish-tutorial/</guid>
      <pubDate>Sat, 27 Dec 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>all i needed to learn</title>
      <description><![CDATA[&lt;p&gt;this is the final post of the year. i am happy i have posted on this blog a lot more this year than i did well, in all the years that this blog has existed. i hope i continue this streak next year as well.&lt;/p&gt;
&lt;p&gt;the year is ending, and for most part, after the time passes, it&#39;s hard to remember when was i ever sad? the year was good, i had a lot of fun.&lt;/p&gt;
&lt;p&gt;one of my favourite write ups is &amp;quot;all i really needed to know i learned in kindergarten&amp;quot; by robert fulgham.&lt;/p&gt;
&lt;p&gt;for the innoncence of the poem, i urge you to read it, but i expected mr robert fulgham to be a lot more old than he actually is. i really thought it&#39;d be a old book, but it is relatively new, published in 1986.&lt;/p&gt;
&lt;p&gt;here&#39;s my favourite part if are not aware about the write up:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“These are the things I learned (in Kindergarten):&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Share everything.&lt;/li&gt;
&lt;li&gt;Play fair.&lt;/li&gt;
&lt;li&gt;Don&#39;t hit people.&lt;/li&gt;
&lt;li&gt;Put things back where you found them.&lt;/li&gt;
&lt;li&gt;CLEAN UP YOUR OWN MESS.&lt;/li&gt;
&lt;li&gt;Don&#39;t take things that aren&#39;t yours.&lt;/li&gt;
&lt;li&gt;Say you&#39;re SORRY when you HURT somebody.&lt;/li&gt;
&lt;li&gt;Wash your hands before you eat.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;... and it continues, on &lt;a href=&quot;https://www.goodreads.com/quotes/246075-these-are-the-things-i-learned-in-kindergarten-1-share&quot;&gt;goodreads&lt;/a&gt; here.&lt;/p&gt;
&lt;p&gt;i honestly miss the teen me and the innocence of those days. i miss the carefree attitude, the friends, the fun... only thing i&#39;d tell my younger self and all my younger brothers and sisters is to enjoy it.&lt;/p&gt;
&lt;p&gt;i like the freedom of adulthood, but i don’t really understand what people look forward to every day. what do adults even look forward to?&lt;/p&gt;
&lt;p&gt;all our lives, there was always something waiting ahead. finish primary school, then secondary school. when i was still writing with a wooden pencil, i was looking forward to growing up and finally getting a pen. in high school, we all looked forward to the 10th-grade ssc exams. after that, it was finishing high school, then college.&lt;/p&gt;
&lt;p&gt;but after college… what’s next?&lt;/p&gt;
&lt;p&gt;mr robert fulgham never told us how to deal with the complexities of adulthood. maybe, he didn&#39;t want us to be sad? idk. the year is ending and i&#39;d like to think we are just a lot of tiny organisms that live on a floating piece of rock. the mere fact that in the grand scheme of things, we don&#39;t even matter, makes me feel good. it&#39;s a little reassuring. happy new year guys!&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/all-i-needed-to-learn/</link>
      <guid>https://dhrm1k.github.io/blog/all-i-needed-to-learn/</guid>
      <pubDate>Wed, 31 Dec 2025 00:00:00 GMT</pubDate>
    </item><item>
      <title>installing openwrt on tp-link router via tftp</title>
      <description><![CDATA[&lt;p&gt;so what if i tell you i am in a good mood today?&lt;/p&gt;
&lt;p&gt;i don&#39;t want this energy to go waste! today might be one of those days where i will not say you a word even if you smash my laptop! i had a router lying around for a while (i have procrastinated on installing openwrt on my hardware for long, so today might be the day), let&#39;s flash it with openwrt! welcome to the guide on how to flash openwrt on tplink routers (mine specifically is model tl-wr850n)&lt;/p&gt;
&lt;p&gt;here&#39;s an image of the model.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/tp-link-router.jpeg&quot; alt=&quot;router image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;random electricity strikes in monsoon season have bricked a lot of routers of mine over time, so i had a spare one lying around for next time that happens.&lt;/p&gt;
&lt;p&gt;up until few months ago, prior to my internship, i had no idea about openwrt, but lately i tinker with it a lot, so why not experiment with it a little more.&lt;/p&gt;
&lt;p&gt;i hope you already know how to set up a tftp server! if not, you&#39;ve come to the right place!&lt;/p&gt;
&lt;p&gt;i installed tftpd-hpa, which is the standard TFTP server (and the only one i knew of) for debian/ubuntu-based systems.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install tftpd-hpa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the default configuration is at &lt;code&gt;/etc/default/ttpd-hpa&lt;/code&gt;. here&#39;s what mine looked like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-config&quot;&gt;TFTP_USERNAME=&amp;quot;tftp&amp;quot;
TFTP_DIRECTORY=&amp;quot;/var/lib/tftpboot&amp;quot;
TFTP_ADDRESS=&amp;quot;127.0.0.1:69&amp;quot;
TFTP_OPTIONS=&amp;quot;--secure&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i copied the openwrt firmware to the TFTP directory and renamed it to what the tp-link router expects, which is &lt;code&gt;tp_recovery.bin&lt;/code&gt;. the browsing through the web, it hit me that this is not consistent. a lot of models expect different names.&lt;/p&gt;
&lt;p&gt;i too had to open wireshark and capture and see what is the tftp protocol asking for&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/packet-capture.png&quot; alt=&quot;wireshark tftp request&quot; /&gt;&lt;/p&gt;
&lt;p&gt;one of the roadblock that i had no idea about was that i had to fix permission of the bin fil so the TFTP server (running as user tftp) could read it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo chown tftp:tftp /var/lib/tftpboot/tp_recovery.bin
sudo chmod 644 /var/lib/tftpboot/tp_recovery.bin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i started the tftp server with the command &lt;code&gt;sudo systemctl status tftpd-hpa&lt;/code&gt;, hoping it&#39;d take a minute or two, but no. are things that easy, never?&lt;/p&gt;
&lt;h3&gt;the tftp server that wasn&#39;t really running&lt;/h3&gt;
&lt;p&gt;i configured my PC&#39;s network interface to be on the same subnet as the router&#39;s recovery mode IP:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ip addr add 192.168.0.66/24 dev enp1s0
sudo ip link set enp1s0 up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the router in tftp recovery mode uses 192.168.0.2, and my PC was 192.168.0.66.&lt;/p&gt;
&lt;p&gt;i powered on the router in recovery mode (holding the reset button while plugging in power). I could see it in tcpdump, &lt;code&gt;sudo tcpdump -i enp1s0 port 69&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IP 192.168.0.2.2250 &amp;gt; pop-os.tftp: TFTP, length 34, RRQ &amp;quot;tp_recovery.bin&amp;quot; octet timeout 1
Beautiful! The router was asking for the file. But then... nothing. Just the same request over and over again, every 30 seconds. No response from my PC.
Debugging with tcpdump
I ran tcpdump with more detail to see what was actually happening:
bashsudo tcpdump -i enp1s0 -e -n host 192.168.0.2
This showed me the ethernet frames with MAC addresses and everything:
12:45:16.342929 00:00:0a:eb:13:09 &amp;gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 60: Request who-has 192.168.0.66 tell 192.168.0.2
12:45:16.342955 40:c2:ba:e0:82:69 &amp;gt; 00:00:0a:eb:13:09, ethertype ARP (0x0806), length 42: Reply 192.168.0.66 is-at 40:c2:ba:e0:82:69
12:45:16.351039 00:00:0a:eb:13:09 &amp;gt; 40:c2:ba:e0:82:69, ethertype IPv4 (0x0800), length 76: 192.168.0.2.1437 &amp;gt; 192.168.0.66.69: TFTP, length 34, RRQ &amp;quot;tp_recovery.bin&amp;quot; octet timeout 1
12:45:16.351071 40:c2:ba:e0:82:69 &amp;gt; 00:00:0a:eb:13:09, ethertype IPv4 (0x0800), length 104: 192.168.0.66 &amp;gt; 192.168.0.2: ICMP 192.168.0.66 udp port 69 unreachable, length 70
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;THERE IT IS!&lt;/p&gt;
&lt;p&gt;icmp 192.168.0.66 udp port 69 unreachable&lt;/p&gt;
&lt;p&gt;MY PC WAS ACTIVELY REJECTING THE PACKETS.&lt;/p&gt;
&lt;p&gt;i checked what was actually listening, &lt;code&gt;sudo lsof -i :69&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;nothing. empty output. even though systemctl said the service was running!
i tried running the TFTP server manually with verbose output to see what was happening, &lt;code&gt;sudo systemctl stop tftpd-hpa&lt;/code&gt; and &lt;code&gt;sudo /usr/sbin/in.tftpd -L -vvv -a 0.0.0.0:69 -s /var/lib/tftpboot&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;then triggered the router recovery again. watched tcpdump in another terminal... router sent requests... and the manual TFTP server showed absolutely nothing. no log output. no indication it received anything.
the packets were visible in tcpdump but never reaching the TFTP application. this meant something was dropping them at the kernel level before they could reach the application.&lt;/p&gt;
&lt;p&gt;like any sane person i disabled the firewall and blamed it&lt;/p&gt;
&lt;h3&gt;kill everything and start fresh&lt;/h3&gt;
&lt;p&gt;the problem? an old TFTP server process was probably still running from earlier attempts, bound to the wrong interface or in a zombie state. when i &amp;quot;restarted&amp;quot; the service via systemd, it might have failed to actually start a new process because the old one was still holding the port (or thought it was).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# i said f it and killed all the processes related to in.tftpd
sudo pkill -9 in.tftpd
# gotta verify if nothing is listening on port 69 now
sudo lsof -i :69

# and started the service fresh
sudo systemctl start tftpd-hpa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;also,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo lsof -i :69

Output:
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
in.tftpd 33576 root    4u  IPv4 252242      0t0  UDP *:tftp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;finally! the TFTP server was actually listening on port 69.&lt;/p&gt;
&lt;p&gt;with the TFTP server finally configured correctly and actually listening on the network, i power-cycled the router one more time. held the reset button, plugged in power, waited for the recovery mode...
started tcpdump to watch the magic happen:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tcpdump -i enp1s0 -e -n host 192.168.0.2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;this time, instead of ICMP unreachable messages, i saw:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;12:48:03.804887 00:00:0a:eb:13:09 &amp;gt; 40:c2:ba:e0:82:69, ethertype IPv4 (0x0800), length 60: 192.168.0.2.2205 &amp;gt; 192.168.0.66.46446: UDP, length 4
12:48:03.804918 40:c2:ba:e0:82:69 &amp;gt; 00:00:0a:eb:13:09, ethertype IPv4 (0x0800), length 558: 192.168.0.66.46446 &amp;gt; [192.168.0.2.2205](192.168.0.2.2205): UDP, length 516
12:48:03.805053 00:00:0a:eb:13:09 &amp;gt; 40:c2:ba:e0:82:69, ethertype IPv4 (0x0800), length 60: 192.168.0.2.2205 &amp;gt; 192.168.0.66.46446: UDP, length 4
12:48:03.805057 40:c2:ba:e0:82:69 &amp;gt; 00:00:0a:eb:13:09, ethertype IPv4 (0x0800), length 558: 192.168.0.66.46446 &amp;gt; 192.168.0.2.2205: UDP, length 516
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;BEAUTIFUL PACKET EXCHANGE!
what&#39;s happening here:&lt;/p&gt;
&lt;p&gt;UDP, length 4 - Router sending ACK packets (acknowledging received data blocks)
UDP, length 516 - My PC sending DATA packets (512 bytes of firmware data + 4 bytes TFTP header)&lt;/p&gt;
&lt;p&gt;the problem is that i expected even tftp logs to show something, but no logs were being printed. i guess tftpd-hpa doesn&#39;t do verbose logging by default. oh well, let&#39;s be glad, tcpdump is doing it&#39;s job good enough.&lt;/p&gt;
&lt;p&gt;the 8MB firmware file was being transferred 512 bytes at a time. the tcpdump output was scrolling rapidly with hundreds of these exchanges per second.&lt;/p&gt;
&lt;p&gt;note that TFTP switches from port 69 to a random high port (46446 in this case) after the initial request. this is why earlier when I was monitoring port 69 only, i wasn&#39;t seeing the actual transfer, it happens on a different port!&lt;/p&gt;
&lt;p&gt;i just sat there watching tcpdump scroll by, afraid to breathe or touch anything in case it broke again (or more worse, i brick my extra router). after a few minutes of continuous packet exchange, the transfer completed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;12:48:03.806750 40:c2:ba:e0:82:69 &amp;gt; 00:00:0a:eb:13:09, ethertype IPv4 (0x0800), length 46: 192.168.0.66.46446 &amp;gt; 192.168.0.2.2205: UDP, length 4
12:48:06.943308 40:c2:ba:e0:82:69 &amp;gt; 00:00:0a:eb:13:09, ethertype ARP (0x0806), length 42: Request who-has 192.168.0.2 tell 192.168.0.66, length 28
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the router stopped responding to ARP requests. itt was rebooting to flash the firmware! it was time for wireless freedom (openwrt&#39;s slogan).&lt;/p&gt;
&lt;p&gt;the router had rebooted. Time to access the shiny new openWrt interface! i tried visiting the web via 192.168.0.1. nothing. connection refused.&lt;/p&gt;
&lt;p&gt;i did,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ping 192.168.0.1
From 192.168.0.66 icmp_seq=1 Destination Host Unreachable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;hmm. maybe it takes a while to boot? waited 2 minutes (with the tp link firmware, i am sure the device took much less time to boot then it does right now). still nothing. then it hit me, openwrt defaults to 192.168.1.1, not 192.168.0.1! different subnet entirely.&lt;/p&gt;
&lt;p&gt;reconfigured my network interface:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ip addr flush dev enp1s0
sudo ip addr add 192.168.1.100/24 dev enp1s0
sudo ip link set enp1s0 up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tried again and opened the browser/web interface at 192.168.1.1 and voila, there it was! the openwrt web interface!&lt;/p&gt;
&lt;p&gt;and wooh, now i can ssh into my router too! is there a better feeling than that of trying a new distro and successfully logging into it?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/openwrt-ssh.png&quot; alt=&quot;openwrt ssh&quot; /&gt;&lt;/p&gt;
&lt;p&gt;my internet is working too slow. i blame the hardware offloading. i am still figuring things out. i want to set up a wireguard instance. i recently needed a file when i was not at home. casually. my brother was at home, so he assisted me in file sharing, but for future i need my vpn&lt;/p&gt;
&lt;p&gt;i made a new ssid and connected the wan to my lan of my gtpl fiber connection hoping to try new packages and things.&lt;/p&gt;
&lt;h3&gt;rant&lt;/h3&gt;
&lt;p&gt;i ranted the whole day on how the new ssid i created is too slow. i already had a little idead that the openwrt does impact the network connection and the throughput isn&#39;t what you expect with the propetiary os.&lt;/p&gt;
&lt;p&gt;later it hit me how the traffic would be flowing and yeah, double nat was the problem. the way i had it set up was lan → wan into openwrt, so it was unnecessarily routing and nat-ing everything again instead of just bridging traffic like an access point should.&lt;/p&gt;
&lt;p&gt;i reconfigured the openwrt to be an access point only, disabled dhcp and set the lan ip to be in the same subnet as my main router. boom! speeds were back to normal.&lt;/p&gt;
&lt;p&gt;for posterity, if someone ever arrives to the same problem, below are the uci commands to set up openwrt as an access point only:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;uci set dhcp.lan.ignore=&#39;1&#39;
uci commit dhcp

uci set network.lan.proto=&#39;static&#39;
uci set network.lan.ipaddr=&#39;192.168.0.2&#39;
uci set network.lan.netmask=&#39;255.255.255.0&#39;
uci set network.lan.gateway=&#39;192.168.0.1&#39;
uci set network.lan.dns=&#39;192.168.0.1&#39;
uci commit network

uci set network.wan.disabled=&#39;1&#39;
uci set network.wan6.disabled=&#39;1&#39;
uci commit network

/etc/init.d/network restart
/etc/init.d/dnsmasq restart
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the thing that hit me is that when you hit the &lt;code&gt;etc/init.d/network restart&lt;/code&gt; command, it will drop your ssh connection if you are connected over the lan interface. so be careful! this will be a problem when your main router is not in the same subnet as openwrt lan ip. gpt phrased it as &lt;code&gt;classic AP-mode transition lockout&lt;/code&gt;, to which i was like, classic, aeh! cool.&lt;/p&gt;
&lt;p&gt;for that you will need to assign your device a static ip then ssh again into the access point and reconfigure it to match your main router subnet.&lt;/p&gt;
&lt;p&gt;i did a speed test after this and the i have to say the speeds are better and the experience overall is better than it was prior...
&lt;img src=&quot;https://dhrm1k.github.io/assets/images/openwrt-speedtest-dumb-accesspoint.png&quot; alt=&quot;speed test image&quot; /&gt;
there are a lot of packages and things i want to try! first is setting up a vpn, so i can access my home network from anywhere. more on that later!&lt;/p&gt;
&lt;h4&gt;update: dt: 15/01/2026&lt;/h4&gt;
&lt;h3&gt;reverse ssh tunnel for remote access&lt;/h3&gt;
&lt;p&gt;this was the reason i wanted to set up openwrt on my router in the first place. i wanted to access my home network remotely. well, i could have set up tailscale or wireguard on my computer instead, right? but i wanted to experiment with the router itself.&lt;/p&gt;
&lt;p&gt;i don&#39;t know if i have mentioned already, but on opewrt too, my default plan was to install tailscale in the first place. there&#39;s this sick computer user i follow on twitter, and they worked on tailscale tui at neuralink (follow at &lt;a href=&quot;https://x.com/kognise7&quot;&gt;x.com/kognise7&lt;/a&gt;). but flash memory of my router is only 2mb and the tailscale package alone is around 8mb, so well, that was a no go.&lt;/p&gt;
&lt;p&gt;instead now the plan was to set up a reverse ssh tunnel from openwrt router to my vps, and then access my home network via the vps.&lt;/p&gt;
&lt;p&gt;now my isp is not so great. even if i called the customer care, they&#39;d have no idea what a port forward is. i did even call them and they told me to mail them about it. so the thing was i had to implement everything without disturbing the main router settings.&lt;/p&gt;
&lt;p&gt;i already had a vps with a public ip (let&#39;s be thankful to student github plan). i was high on adrenaline to setup a reverse ssh tunnel from openwrt to my vps.&lt;/p&gt;
&lt;p&gt;i made a new user and assigned it no shell access (for security reason)&lt;/p&gt;
&lt;p&gt;also generated a ssh key pair on my openwrt router via &lt;code&gt;ssh-keygen -t rsa -b 2048 -f /root/.ssh/vps_tunnel -N &amp;quot;&amp;quot;&lt;/code&gt; then copied the public key to my vps&lt;/p&gt;
&lt;h3&gt;the autossh struggle&lt;/h3&gt;
&lt;p&gt;i knew setting up autossh wouldn&#39;t be easy. i have had my fair share of it already.&lt;/p&gt;
&lt;p&gt;i installed &lt;code&gt;opkg install autossh&lt;/code&gt; on openwrt, and manually ran this command to test if it works:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;AUTOSSH_GATETIME=0 &#92;
AUTOSSH_PATH=/usr/bin/dbclient &#92;
autossh -M 0 -N &#92;
  -o ServerAliveInterval=30 &#92;
  -i /root/.ssh/vps_tunnel &#92;
  -R 127.0.0.1:2222:localhost:22 &#92;
  tunnel@VPS_IP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tunnel appeared on the vps, i could ssh from the vps to my router via ssh root@127.0.0.1 -p 2222, everything worked. beautiful. but can we really have good things?&lt;/p&gt;
&lt;p&gt;but when i tried to run it as a procd service? crash loop. every single time.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Instance reverse-ssh::instance1 is in a crash loop&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;and as any sane person i cursed a lot. cursed openwrt. cursed my life choices, but later it hit me, that there&#39;s nothing else i have to do except this crap on my computer. this is the only high i get!&lt;/p&gt;
&lt;p&gt;i checked the logs via &lt;code&gt;logread -f&lt;/code&gt; and simultaneously tried to login from the vps to see what was happening. the logs showed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;logread | grep autossh

&amp;gt; exit code 127. that&#39;s &amp;quot;command not found.&amp;quot; ran `which autossh` and found it at `/usr/sbin/autossh`, not `/usr/bin/autossh`. classic. fixed the path in my service file.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;restarted. still crashing. checked logs again:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bash
daemon.err autossh[8550]: /usr/bin/dbclient: Ignoring unknown configuration option &#39;ServerAliveCountMax=3&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;oh. openwrt uses dropbear, not openssh. those &lt;code&gt;-o ServerAliveInterval&lt;/code&gt; flags? dropbear doesn&#39;t understand them. dropbear has its own syntax. openssh uses &lt;code&gt;-o ServerAliveInterval=30&lt;/code&gt;, dropbear uses &lt;code&gt;-K 30&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;fixed that. restarted. still crashing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;daemon.err autossh[8550]: Host &#39;myipisnoneofyourip&#39; is not in the trusted hosts file.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ah, ok better. i thought i connected via ssh key once and trusted the host, but apparently not. i manually ssh-ed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/usr/bin/dbclient -i /root/.ssh/vps_tunnel tunnel@myipisnoneofyourip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;connected fine, saw &amp;quot;this account is currently not available&amp;quot; (because the tunnel user has no shell), but the connection worked. host key should be saved now, right?&lt;/p&gt;
&lt;p&gt;now i am in a moral dillemma. do i want to share everything about my config. even the parts that are sensitive and i know are a security hazard? well, for now i did something dirty. i will not mention it here.&lt;/p&gt;
&lt;p&gt;after all that debugging there was a zombie existing autossh process that hindered the new one from starting. i killed all autossh processes via &lt;code&gt;pkill -9 autossh&lt;/code&gt; and restarted the service again on my openwrt router.&lt;/p&gt;
&lt;p&gt;the current config file for autossh service is as below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/sh /etc/rc.common

START=99
STOP=10
USE_PROCD=1

start_service() {
    procd_open_instance
    
    procd_set_param command /usr/sbin/autossh &#92;
        -M 0 &#92;
        -N &#92;
        -y &#92;
        -K 30 &#92;
        -i /root/.ssh/vps_tunnel &#92;
        -R 127.0.0.1:2222:localhost:22 &#92;
        tunnel@myipisnoneofyourip
    
    procd_set_param env AUTOSSH_GATETIME=0
    procd_append_param env AUTOSSH_PATH=/usr/bin/dbclient
    procd_append_param env AUTOSSH_PIDFILE=/var/run/autossh.pid
    
    procd_set_param respawn 3600 5 0
    procd_set_param stdout 1
    procd_set_param stderr 1
    
    procd_close_instance
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and boom&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh myvpsusername@MY_VPS_IP
ssh root@127.0.0.1 -p 2222
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;my router is now a a layer 2 bridge or a dumb access point with a reverse ssh tunnel to my vps for remote access. there&#39;s something that&#39;s still bugging me though. i can always see the devices connected to my openwrt router ssid via the arp table on openwrt, but i can&#39;t see the hostnames. i have looked into mDNS, but for some reason avahi-daemon package isn&#39;t available for the latest openwrt version. more on that later!&lt;/p&gt;
&lt;p&gt;for now i have decided to write a device profile script that decides what device it is dependent on the ttl value (and the number of arp requests it sends and things) and map a device profile accordingly.&lt;/p&gt;
&lt;p&gt;and here&#39;s the as-usual obligatory screenshot of the reverse ssh tunnel working. i first ssh-ed into my vps and then to it&#39;s this port i assigned that forwarded to my home router. later i did arp -a (and well because hostnames are not available and nmap scan will be waste of resources, i plainly guessed my laptop&#39;s ip from the mac address). and well then myhostname which is dharmik@laptops_ip and bam i was in my laptop.&lt;/p&gt;
&lt;p&gt;next time when i am roaming outside and i out of where need or have an urge to look for a file, i can well just open termius app on my phone.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/reverse-ssh-works.png&quot; alt=&quot;reverse ssh termius screenshot&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ofcourse, i have edited out all the things some moronic attackers might find interesting.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/installing-openwrt-on-tplink/</link>
      <guid>https://dhrm1k.github.io/blog/installing-openwrt-on-tplink/</guid>
      <pubDate>Sun, 11 Jan 2026 00:00:00 GMT</pubDate>
    </item><item>
      <title>powering a home machine remotely and how wake on lan actually works</title>
      <description><![CDATA[&lt;p&gt;should i sleep right now? probably yes, but not before i finish writing this post.&lt;/p&gt;
&lt;p&gt;do you ever wish that when you are not at home, you could just turn on your home computer remotely? well, i do too. we have that in common if you do. i&#39;d rather want a home server, but i am not able to convince myself to spend money on one right now (though i think, i might not be able to resist for long. grown men do need toys after all).&lt;/p&gt;
&lt;p&gt;anyone who has read my &lt;a href=&quot;https://dhrm1k.github.io/blog/installing-openwrt-on-tplink/&quot;&gt;last post&lt;/a&gt; knows i was tinkering with getting remote access to my home computer but not open to using wireguard. i wanted something simpler, though we all know that&#39;s just an excuse because i don&#39;t have energy past 9 pm these days to set it up.&lt;/p&gt;
&lt;h2&gt;a big f you autossh, hello tailscale&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/tailscale-dashboard.png&quot; alt=&quot;tailscale dashboard after i added my devices&quot; /&gt;&lt;/p&gt;
&lt;p&gt;yesterday night i was so frustrated on autossh fails. decided to finally give tailscale a try to set up a vpn so i can access my home network even when i am not home. tailscale is a zero-config vpn that uses wireguard under the hood. it is super easy to set up and works like a charm. i have never used it prior to this but read a lot about it and i gotta say it&#39;s so sick. i have installed it on all my devices, my laptop, my home computer, even my phone. i still have to try the neuralink made tui for tailscale, that i keep reading and telling everyone about, but later.&lt;/p&gt;
&lt;p&gt;now i have wake on lan enabled on my home computer and all my devices are now added to tailscale. the only problem was power management. there must be times when i want to remotely shutdown my computer. i don&#39;t know when, but there must come a time right? like what if i accidentally leave some heavy process running and want to shut it down to save power? or what if i just want to feel the power of turning off a machine hundreds of kilometers away? the universe works in mysterious ways.&lt;/p&gt;
&lt;h2&gt;a little gibberish on what is wake-on-lan?&lt;/h2&gt;
&lt;p&gt;wol is a networking standard that allows a computer to be turned on or awakened by a network message. the way it works is actually pretty neat - when you want to wake up a computer, you send what&#39;s called a &amp;quot;magic packet&amp;quot; over the network. this packet is just a specific sequence of bytes: six bytes of all 255s (0xFF in hex), followed by sixteen repetitions of the target computer&#39;s MAC address. that&#39;s it. no encryption, no fancy protocol, just a very specific pattern of bytes broadcast over the network.&lt;/p&gt;
&lt;p&gt;the interesting part is how a computer that&#39;s &amp;quot;off&amp;quot; can even receive this packet. when you properly shut down a computer (like using &lt;code&gt;systemctl poweroff&lt;/code&gt; on linux), it doesn&#39;t actually cut all power. modern power supplies have what&#39;s called a 5VSB (5 volt standby) line that stays on even when the computer is off. this standby power keeps a tiny part of the network card alive and listening for that magic packet. when the network card sees the right pattern with its MAC address, it sends a signal to the motherboard to trigger the power supply and boot up the computer. it&#39;s like leaving a very specific doorbell running that can wake up the entire house.&lt;/p&gt;
&lt;p&gt;one of the thing that got me curious was that how does the computer listen to the wol packet when it&#39;s off? i specifically use the command &lt;code&gt;systemctl poweroff&lt;/code&gt; to turn it off. turns out that even when the computer is off, this specific command takes it to soft off (what&#39;s technically called ACPI state S5), and the 5VSB line of the power supply is still on, which powers the network card to listen for the wol packet. the network card basically sits there in an extremely low power state, just waiting for that one specific pattern of bytes. when it sees it, boom, it tells the motherboard &amp;quot;hey, someone&#39;s calling for us&amp;quot; and the whole computer springs to life. cool right?&lt;/p&gt;
&lt;p&gt;priorly to this, i have a l2 wireless switch on my network that too gets an ip address from my router. this switch is nothing but a spare lying tp-link router. i flashed openwrt on it (already in the last post) and set it up as a dumb access point. this way, all devices connected to this access point are on the same subnet as my main router.&lt;/p&gt;
&lt;p&gt;i already had the autossh part figured out in the last part, but still the problem was that whenever my computer was off, i could not reach it to send the wol packet. also another problem was that now that i want to setup kasm at places without worrying about logging into services at random computers and later worry that i logged in, and then cause a chaos spree to change passwords everywhere.&lt;/p&gt;
&lt;p&gt;i had to set up wol from bios first and to my surprise it was already on. maybe, some other day, i might have had the urge to turn it on but quit setting it up because i was lazy. it&#39;s funny how easily i am forgetting things lately. i had a vm fully configured on my device, though i have no memory or account of when i did that. i was just grateful &amp;quot;oh, mother frigging god, it exists&amp;quot; state.&lt;/p&gt;
&lt;p&gt;to first test things out, i tried sending the wol packet from my local network from my pop os machine to my arch machine. our beloved package manager has a package called wakeonlan that makes it super easy to send the packets from the terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install wakeonlan
wakeonlan &amp;lt;mac-address-of-arch-machine&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it worked like a charm. the arch machine woke up in no time. enabled wake on lan and it mother frigging works in the first try.&lt;/p&gt;
&lt;h2&gt;as usual, storage problems&lt;/h2&gt;
&lt;p&gt;now the next step was to send the wol packet from my openwrt router. to which i assumed &amp;quot;hey lord, opkg must have a package&amp;quot; and it very much did, the package size was just in 5-6 kb range, and it was also titled wakeonlan, so i assumed installing it would be a breeze, until wait, i clicked on install and it showed that one of the dependencies was perl that too alone is 3mb. well, i hope i have ranted enough already how my router&#39;s flash memory is 2mb. so i had to find an alternative way.&lt;/p&gt;
&lt;p&gt;after some googling and gpting, i figured out i could install socat package and use it to send the wol packet. socat is a command line based utility that establishes two bidirectional byte streams and transfers data between them. the package was small enough to fit on my router and it could construct and send the magic packet using UDP broadcast.&lt;/p&gt;
&lt;p&gt;here&#39;s the script i ended up with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;root@OpenWrt:~# cat socat-wol.sh 
MAC=&amp;quot;your-mac-address-without-colons&amp;quot;
printf &#39;&#92;xff&#92;xff&#92;xff&#92;xff&#92;xff&#92;xff&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#92;x18&#92;xc0&#92;x4d&#92;x3d&#92;xc8&#92;xc6&#39; | socat - UDP-DATAGRAM:255.255.255.255:9,broadcast
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you can see the magic packet structure right there in the printf - six &lt;code&gt;&#92;xff&lt;/code&gt; bytes followed by my MAC address repeated sixteen times. that&#39;s exactly what the network card is waiting for. i tested this script and well, it did turn on my computer.&lt;/p&gt;
&lt;h2&gt;the final thing&lt;/h2&gt;
&lt;p&gt;now i can autossh into my router from the tunnel i have via my vps anytime, wake my computer on lan via the magic packet, and after waiting hopefully for 30 seconds (sometimes it feels like an eternity when you&#39;re staring at the terminal waiting for the ssh connection to establish, wondering if the packet even reached or if your computer decided to be moody and never wake up), i can then ssh into the computer via tailscale and use it even as a vpn to access my home network.&lt;/p&gt;
&lt;p&gt;the whole chain looks like this: ssh into vps, then reverse ssh into home router, then run the wol script to wake the computer, wait a bit while questioning all life decisions that led to this moment, and finally ssh into the computer via tailscale. smooth and sweet as a pie.&lt;/p&gt;
&lt;p&gt;i can also power it off remotely because &lt;code&gt;systemctl poweroff&lt;/code&gt; doesn&#39;t actually kill all power, the NIC stays powered by that 5VSB line, still listening for the magic packet like a faithful guard dog. when the packet arrives, the motherboard asserts the power-on signal and boom, the whole computer springs to life.&lt;/p&gt;
&lt;p&gt;it&#39;s been a good day.&lt;/p&gt;
&lt;p&gt;now i should probably sleep.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/powering-a-home-machine-remotely-how-wake-on-lan-works/</link>
      <guid>https://dhrm1k.github.io/blog/powering-a-home-machine-remotely-how-wake-on-lan-works/</guid>
      <pubDate>Fri, 30 Jan 2026 00:00:00 GMT</pubDate>
    </item><item>
      <title>random dump</title>
      <description><![CDATA[&lt;p&gt;have we reached the endgame now that i have started to shit post on my blog as well (and what a caveman habit to title this post as &amp;quot;random dump&amp;quot;). maybe!? where else do i go to pretend that people care about opinions?&lt;/p&gt;
&lt;p&gt;there was this time when i was reading two books side by side. one was american prometheus and another was ah, idk remember, ah, it was &amp;quot;too big for a single mind&amp;quot; by tobias hurter. one is on the life of robert oppenheimer, the other one is about the lives that shaped the field of quantum mechanics as we know it.&lt;/p&gt;
&lt;p&gt;this is around the 2023 days when the movie oppenheimer was still not out and i wanted to be ready to enjoy the experience.&lt;/p&gt;
&lt;p&gt;i don&#39;t rememeber which book it was but the quote that&#39;s been my head rent free since then is something like&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Love is a nuisance to everyone but the parties involved.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;i really admire this one. i am never sure where i read this one. i think it has to be american prometheus, considering the complex relationship of jean tatlock and oppie, it certainly looks like something that oppie might say. it&#39;s not i have not tried looking it up. i have and i have lost behind it. i don&#39;t know if it&#39;s even a real quote or something i have made up in my head. i like that it lives rent free in my head. everytime i look at old records (notes app, todo lists, txt files and shit), i do find it.&lt;/p&gt;
&lt;p&gt;i am sure this is not the original one but it&#39;s paraphrased.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;i spend a good chunk of my week going back to the idea of why anyone in india should start a &lt;a href=&quot;https://frame.work/&quot;&gt;framework&lt;/a&gt; like laptop company. those guys are not just deciding to sell in india and i mean, it could be my selection bias (it definitely is), but i can&#39;t think of someone who won&#39;t like the idea of laptop they can repair and upgrade instanly. india ofcourse, after all is one of the most price sensitive markets to exist.&lt;/p&gt;
&lt;p&gt;i sometimes then also think how the public service computing in india should entirely move to linux. it&#39;s free. it can save government a lot of money, but then my friend karan thinks that that is not sensible considering that if anyone finds a serious bug in the source code then well, what do we have? armageddon? idk.&lt;/p&gt;
&lt;p&gt;i have mentioned this prior but just in case anyone new reading this. government of india did once start developing their own debian based linux distro called &amp;quot;bharatos&amp;quot;, but well, they didn&#39;t do much with it. the iso is available online.&lt;/p&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;you either die a hero, or you live long enough to see yourself become the villain&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;i most certainly didn&#39;t die soon enough, and that has brought me to learn rust... yes, rust. the language that i might have cursed a lot in the past about the long compile times (that only come once the compilation actually starts, most of the time that doesn&#39;t happen in the first place)&lt;/p&gt;
&lt;p&gt;why rust? looks like a fun language. i have never learnt in depth about how it handles memory and how owning works in that.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;mgk recently published a new variant of his old album that i have been listening more than a little. pop punk is not for the weak. the album is the classic old &amp;quot;tickets to my downfall&amp;quot; but a all new access version. the songs are not something i have heard. i love to stream to leak music i can find on soundcloud. i already was familiar with the songs, but well, something new.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;i have also set up my home server. i am still quite confused and doubtful about the uptime my amd ryzen can pull. the pc that runs it is a custom built and quite old (2020-ish) covid times. i quit using it a lot of times. it used to run windows, later i think i switched to pop os, later for long it was running arch and i had a immich server running on it.&lt;/p&gt;
&lt;p&gt;a little mishap with how i installed the immich server (i used a snap package) and ofcourse didn&#39;t change the defaults until i exceeded the storage limit. i had to take backups and i had photos of more than around 80gb.&lt;/p&gt;
&lt;p&gt;that pc now runs a ubuntu server. they say you always need stability, i guess i do too. i couldn&#39;t deal with arch. not as a distro for a home setup. i do hear catchy os folks are planning to release a server, but we will see. as much as i say, i love chaos, i also love stability. chaos makes you feel lively. entropy is the proof that you are living, but i am tired af.&lt;/p&gt;
&lt;p&gt;the homelab server runs immich, syncthing, and a firefox docker container for now. immich because ofcourse how do you trust google photos with your memories? as of now, i do pay them for the storage, but i don&#39;t trust them.&lt;/p&gt;
&lt;p&gt;syncthing instance is a little redundant for now, i have one already running on my vps that is on azure, but i plan to... no. some other post for this...&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/random-dump/</link>
      <guid>https://dhrm1k.github.io/blog/random-dump/</guid>
      <pubDate>Fri, 06 Feb 2026 00:00:00 GMT</pubDate>
    </item><item>
      <title>selfhosting jellyfin, ram prices &amp; more</title>
      <description><![CDATA[&lt;p&gt;i recently finished watching lord of the rings trilogy. it&#39;s been on my watchlist for a long time. i have watched and read about tolkien&#39;s works, his life and his world. there was a tolkien biography movie that came out long ago. i watched it and that&#39;s what got me into. i also know of tolkien because i know he&#39;s childhood friend of c.s. lewis. i don&#39;t know how i know that. i don&#39;t remember reading any works of c.s lewis either, but somehow i know that.&lt;/p&gt;
&lt;p&gt;i wish more people i know get on letterboxd. i have been using it for more than a while now. it&#39;s a great platform to keep track of movies you have watched or just add it to watchlist for later. in case, you haven&#39;t followed my letterboxd, you can find me here: &lt;a href=&quot;https://letterboxd.com/dharmiik&quot;&gt;letterboxd.com/dharmiik&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/altman-meme-reddit.webp&quot; alt=&quot;mandatory-meme&quot; /&gt;&lt;/p&gt;
&lt;p&gt;my home topology is something right now. it used to be far more complex until i looked into my ISP&#39;s default firmware and realized that it ain&#39;t that bad. it&#39;s amazing, it ain&#39;t that bad will be me phrasing it like it&#39;s stupid.&lt;/p&gt;
&lt;p&gt;there are a lot of cool features it provides. i can configure static routes (it&#39;s basic or maybe ofcourse my lack of experience with propetiary firmware. it frigging let&#39;s me configure QoS policies. Who frigging thought that for a consumer router).&lt;/p&gt;
&lt;p&gt;the topology used to be weird prior. i had two networks. the default isp one and then my openwrt one. i finally when setting up my homelab and setting up a lot of services (more on that later in the blog), thought finally, today might be the day and added a static route. primarily because, adguard runs on my tower running ubuntu server, and that is on the openwrt network, so the few devices that were on the isp one couldn&#39;t use it.&lt;/p&gt;
&lt;p&gt;i still want to find a good time and try the QoS policy on OvH firmware.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;sometime in the month i read jeff geerling&#39;s &lt;a href=&quot;https://www.jeffgeerling.com/blog/2026/dram-pricing-is-killing-the-hobbyist-sbc-market/&quot;&gt;DRAM pricing is killing the hobbyist SBC market
&lt;/a&gt; and it&#39;s so true. i really might have heard a dozen of friends of how they are waiting for the pricing to go down. everyone is wanting to upgrade.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;i have been cleaning the machine that runs casa os and ubuntu server for me at home. oh boy, i haven&#39;t opened the cabinet of that device in years. well, not years. never ever since it&#39;s inception and holy moly that thing was dusty. photos for reference.&lt;/p&gt;
&lt;div style=&quot;display: flex; gap: 10px; flex-wrap: wrap;&quot;&gt;
  &lt;img src=&quot;https://dhrm1k.github.io/assets/images/dusty-motherboard.jpeg&quot; style=&quot;width: 100%; max-width: 300px; height: auto;&quot; /&gt;
  &lt;img src=&quot;https://dhrm1k.github.io/assets/images/ram-image-dusty.png&quot; style=&quot;width: 100%; max-width: 300px; height: auto;&quot; /&gt;
&lt;/div&gt;
&lt;!-- ![dusty motherboard](/assets/images/dusty-motherboard.jpeg) --&gt;
&lt;!-- ![dusty ram](/assets/images/ram-image-dusty.png) --&gt;
&lt;p&gt;i have got new thermal paste to apply on the processor. it was all dried up. got a extra hdd for backups. my immich backups are increasing.&lt;/p&gt;
&lt;p&gt;one of the incident that hit me was two days ago, when last weekend started, i put everything apart. removed the fan processor, ram, wiped everything (physically, wipe off the dust). and put things inside. pushing back the cpu on the motherboard required a little pressure. i was a little afraid that i was going to press a little harder. the dust was too much and i was not able to figure out where was the traingle i was supposed to align with on the motherboard.&lt;/p&gt;
&lt;p&gt;then when i got things together and booted it up, the problem is i didn&#39;t have monitor. so i had to ssh into my router and see the dhcp.leases file. now it did boot, fan started, but no lights getting started on the motherboard when i plug in the lan cable. now i am under the impression, &amp;quot;voila, we have fried the motherboard or maybe broke it or something&amp;quot;. then i gave up thinking the game&#39;s over and i will have to order a new computer.&lt;/p&gt;
&lt;p&gt;next day i woke up with new born motivation. picked up my screw driver and opened everything again. well, looking at the ram i saw, i haven&#39;t closed the hinge properly. well, closed it. plugged the power and lan cable again and magic, it started again. ssh&#39;ed to my router and there it was. working fine like a good wine.&lt;/p&gt;
&lt;p&gt;now we are hitting the basics. can a computer boot without ram? well, the answer is evident, it can&#39;t. it&#39;s basic. how can one think otherwise? well, i was supposed to know this.&lt;/p&gt;
&lt;p&gt;a os upon starting stores instructions, initialize firmware (BIOS/UEFI) and things.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;i love immich. i have been using it for around more than a year. the setup has changed a lot. priorly, i was used to sync it in my network. now i do it every once in a while from odd places when i have not get anything else to do using tailscale.&lt;/p&gt;
&lt;p&gt;talking about my homelab. i also have a media server stack i have been tinkering with. it&#39;s a combination of qbittorent, radarr, prowlarr and jellyfin.&lt;/p&gt;
&lt;p&gt;i use casaos (though i now think using zimaos, fork of casa would be more fun). now i initially thought to do it via casa itself, to setup all the services... but i ditched it (i found it more complex. the way casaos handles docker containers) and did it manually.&lt;/p&gt;
&lt;p&gt;i can now search for a movie in radarr and it gets downloaded to my jellyfin library via the prowlarr indexing. though i am still looking for good bollywood torrents (drop me a message if you have something).&lt;/p&gt;
&lt;p&gt;i do have a lot of posts i have to post (yes, deliberate use of post as a noun and verb in a single sentence).&lt;/p&gt;
&lt;p&gt;it&#39;s been a long time i have posted on anything on this blog and i have got a lot of opinions about a lot of things. newer opinions. starting from my new born hatred for the cosmic desktop environment and how system76 has ruined it, new born love for wireguard, oh boy i have loved tailscale. i have been dipping my hands in yocto. i will write about it once i have something good going in it. right now a lot i don&#39;t understand there. i got introduced to firewalla (it&#39;s a device. check their story)  and i am a fan. one would say life&#39;s been a lot of fun.&lt;/p&gt;
&lt;p&gt;everyonce in a while i have the urge to upgrade my homelab setup. for right now it&#39;s mostly powered by a old computer i built (got built, i was idk 16. must be 2020?. i don&#39;t recall). i don&#39;t have problem with keeping it on all the time, except for ofcourse then i need to figure out power consumption. how much power it consumers. power is still relatively cheap in india. it&#39;s not a problem for us, but i need to see how much it adds up.&lt;/p&gt;
&lt;p&gt;my setup&#39;s that. a tp link router that has openwrt flashed and a vps i use for tunnels, so i get a public ip.&lt;/p&gt;
&lt;p&gt;i just setup jellyfin, radarr qbittorent, prowlarr. trust me i honestly don&#39;t put my time aside to watch a movie (not like i don&#39;t have time. i am in bed all weekend, it&#39;s not a conscious decision i make), in retrospect to the dedication i am putting in getting things up. as they say &amp;quot;heart wants what the heart wants&amp;quot;, and it sure wants a few containers automating my movie watching work(leisure)flow this time.&lt;/p&gt;
&lt;p&gt;i am plannning to cancel all my subscriptions. get a few friends onboard as well.&lt;/p&gt;
&lt;p&gt;yup, this is what bugs me. &amp;quot;getting friends onboard&amp;quot;. i thought tailscale would be easy. easy as well. it&#39;s not. ACL get&#39;s more confusing everytime you look at it.&lt;/p&gt;
&lt;p&gt;i had to build a solution on top of wireguard for this.
my use case is a bit different though. i started because i wanted to give friends access to specific things in my homelab, but very selectively. like “you can use jellyfin on this one machine, but you can’t ssh, and you can’t even see my other devices”&lt;/p&gt;
&lt;p&gt;tailscale is honestly amazing for getting devices connected, i still use it a lot. but once i started trying to do these very specific “this machine can talk to that machine only on this port” kind of setups, it started feeling more complex than it should be, at least for personal use. ACL editor is more confusing when it comes to this. i know we have got option for tags and things, but those are very poorly documented and i haven&#39;t found a single tutorial that works nicely.&lt;/p&gt;
&lt;p&gt;to which a controdictory opinion would be why not use netbird? well, for starters, i want complete control, and hosting netbird, the control plan consumes around 400mb ram. another things is that people who use my homelab (well, i want it to be this way) would be mostly non technical people. i don&#39;t want them to make their account on one more service. a simple curl command that&#39;d fetch my script and install it. i am still buiding it. i haven&#39;t thought of a name, but i&#39;d put it on my github. (rip my cgit instance).&lt;/p&gt;
&lt;p&gt;my go service barely consumes any. it&#39;s nothing more than UI built on top of wireguard.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;recently ubuntu 26.04 LTS came out and i am reading the mixed opinions it&#39;s having. i didn&#39;t know ubuntu in the linux community has the reputation of microsoft. apparently people hate on snaps and the &amp;quot;Buy pro ads&amp;quot; that canonical keeps pushing in their community builds is hurting people&#39;s sentiments.&lt;/p&gt;
&lt;p&gt;well, from a generalist view, ubuntu definitely is something that has made a lot of people use linux. i am under the impression that if it weren&#39;t for ubuntu, the year of linux on desktop would be still decades away from now.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;i have tried oreo golden. the biscuits along with the cream are vanilla flavoured and there are very less things i have eaten that top this.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;found this very intersting middle earth map while i was browsing the web. i am a lotr fan. i am a new fan. i am yet to read the books. i am looking for a good time to read it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/middleearth-map.webp&quot; alt=&quot;middle earth map&quot; /&gt;&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/selfhosting-ram-and-more/</link>
      <guid>https://dhrm1k.github.io/blog/selfhosting-ram-and-more/</guid>
      <pubDate>Sat, 25 Apr 2026 00:00:00 GMT</pubDate>
    </item><item>
      <title>watchcat timing fix openwrt</title>
      <description><![CDATA[&lt;p&gt;&lt;s&gt;i recently got a small change merged in the openwrt package tree.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;i even maintain that package in the openwrt tree. it&#39;s a fun task to do.&lt;/p&gt;
&lt;p&gt;at first i thought it was a very small thing. basically a timer detail in
&lt;code&gt;watchcat&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;but while working on it, and especially during review, i realised the behaviour
was more interesting than i first thought.&lt;/p&gt;
&lt;p&gt;the pr is here:&lt;/p&gt;
&lt;p&gt;https://github.com/openwrt/packages/pull/29326&lt;/p&gt;
&lt;h2&gt;the basic idea&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;watchcat&lt;/code&gt; is an openwrt package that can keep checking if something is
reachable by pinging one or more hosts.&lt;/p&gt;
&lt;p&gt;if the pings keep failing for long enough, it can take some recovery action.&lt;/p&gt;
&lt;p&gt;depending on the config, it can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;restart an interface&lt;/li&gt;
&lt;li&gt;restart networking&lt;/li&gt;
&lt;li&gt;run a script&lt;/li&gt;
&lt;li&gt;reboot the device&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;the part i was looking at was mostly around these two modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;restart_iface&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run_script&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;in my head, watchdog behaviour was always simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;thing goes down
watchdog notices it
wait for failure period
run recovery action
keep checking
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;simple enough.&lt;/p&gt;
&lt;p&gt;but then i started thinking about one detail:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;what happens to the failure timer after the recovery action runs?&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;the timing problem&lt;/h2&gt;
&lt;p&gt;say the config is something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;failure_period = 60 seconds
recovery action takes 15 seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then the timeline looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;t=0    outage starts
t=60   recovery action starts
t=75   recovery action finishes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;now the question is, what is the next baseline?&lt;/p&gt;
&lt;p&gt;should the next recovery action happen around &lt;code&gt;t=120&lt;/code&gt;?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;t=0    outage starts
t=60   restart #1 starts
t=75   restart #1 finishes
t=120  restart #2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or should it happen around &lt;code&gt;t=135&lt;/code&gt;, because the first restart finished at
&lt;code&gt;t=75&lt;/code&gt;, and only after that we start a fresh 60 second failure window?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;t=0    outage starts
t=60   restart #1 starts
t=75   restart #1 finishes
t=135  restart #2 would be the earliest next retry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;my first thought was the second one.&lt;/p&gt;
&lt;p&gt;after the recovery action finishes, reset the timer and give the interface or
script some room. otherwise it can keep doing recovery actions too close to each
other.&lt;/p&gt;
&lt;p&gt;for a normal interface, that feels noisy. it may not help anything, and it can
make the logs harder to understand.&lt;/p&gt;
&lt;h2&gt;the part i almost missed&lt;/h2&gt;
&lt;p&gt;the review made this more interesting.&lt;/p&gt;
&lt;p&gt;daniel f. dickinson, who maintains watchcat now, pointed out that the old
behaviour is useful for some setups and should not just be changed.&lt;/p&gt;
&lt;p&gt;that was the important bit.&lt;/p&gt;
&lt;p&gt;imagine a wireguard or openvpn interface.&lt;/p&gt;
&lt;p&gt;the upstream internet may come back, but the tunnel itself may still be stuck
until it is kicked again. if &lt;code&gt;watchcat&lt;/code&gt; is pinging through that tunnel, it does
not really know that the underlying internet recovered.&lt;/p&gt;
&lt;p&gt;it only knows one thing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;the monitored path is still failing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;so in that case, continuing to retry during the outage can be useful. maybe even
necessary.&lt;/p&gt;
&lt;p&gt;so this was not really:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;old behavior bad
new behavior good
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it was more like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;there are two valid timing models
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and that changed the patch.&lt;/p&gt;
&lt;h2&gt;what changed&lt;/h2&gt;
&lt;p&gt;instead of changing the default behaviour, the merged pr added an opt-in option:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;option reset_failure_timer &#39;1&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when this option is enabled, &lt;code&gt;watchcat&lt;/code&gt; starts a fresh failure window after the
recovery action finishes.&lt;/p&gt;
&lt;p&gt;so the default behaviour stays the same. that keeps the old retry model useful
for vpn/tunnel style setups.&lt;/p&gt;
&lt;p&gt;and the new behaviour is there for setups where repeated recovery actions too
close to each other are not useful.&lt;/p&gt;
&lt;p&gt;the merged commit was:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;945029322 watchcat: add optional failure timer reset
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the pr also cleaned up some log wording, added timing notes in &lt;code&gt;TIMINGS.md&lt;/code&gt;,
bumped &lt;code&gt;PKG_RELEASE&lt;/code&gt;, and added a small CI version test override.&lt;/p&gt;
&lt;h2&gt;what this looks like&lt;/h2&gt;
&lt;p&gt;default behaviour:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;t=0    outage starts
t=60   restart #1 starts
t=75   restart #1 finishes
t=120  restart #2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with &lt;code&gt;reset_failure_timer=1&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;t=0    outage starts
t=60   restart #1 starts
t=75   restart #1 finishes
t=135  restart #2 would be the earliest next retry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it decides whether the time spent inside the recovery action counts toward the
next failure window or not.&lt;/p&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/watchcat-timing-fix-openwrt/</link>
      <guid>https://dhrm1k.github.io/blog/watchcat-timing-fix-openwrt/</guid>
      <pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate>
    </item><item>
      <title>rainbow trails for neovim</title>
      <description><![CDATA[&lt;p&gt;i am a vim fan. i love the efficiency and doing everything without taking my
hands off the keyboard. but i do not like things bland. i want colors. i
admire colors.&lt;/p&gt;
&lt;p&gt;and now it does, whenever it moves.&lt;/p&gt;
&lt;img eleventy:ignore=&quot;&quot; src=&quot;https://normalmo.de/plugins/images/rainbow-trailser.gif&quot; alt=&quot;rainbow trails following the cursor in neovim&quot; /&gt;
&lt;p&gt;the plugin is here: &lt;a href=&quot;https://github.com/dhrm1k/rainbow-trails.nvim&quot;&gt;rainbow-trails.nvim on
github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;this is my native lua rewrite of rich cheng&#39;s original
&lt;a href=&quot;https://github.com/sedm0784/vim-rainbow-trails&quot;&gt;vim-rainbow-trails&lt;/a&gt;. the
original was written in vimscript and already worked in neovim, but i wanted a
version built around neovim&#39;s lua api. i also fixed the old window cleanup
problem and made trails visible past the ends of short and empty lines.&lt;/p&gt;
&lt;h2&gt;installing it&lt;/h2&gt;
&lt;p&gt;with vim-plug:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;call plug#begin()
Plug &#39;dhrm1k/rainbow-trails.nvim&#39;
call plug#end()

lua require(&#39;rainbow_trails&#39;).setup({ enabled = true })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;run &lt;code&gt;:PlugInstall&lt;/code&gt;, restart neovim, and move around.&lt;/p&gt;
&lt;p&gt;you can also enable or disable it manually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:RainbowTrails
:RainbowTrails!
&lt;/code&gt;&lt;/pre&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/rainbow-trails-neovim/</link>
      <guid>https://dhrm1k.github.io/blog/rainbow-trails-neovim/</guid>
      <pubDate>Sat, 25 Jul 2026 00:00:00 GMT</pubDate>
    </item><item>
      <title>bcm53xx sysupgrade error</title>
      <description><![CDATA[&lt;h2&gt;the stock asus firmware&lt;/h2&gt;
&lt;p&gt;i (not so) recently got my hands on asus rt-ac68u. it&#39;s a great router. loving it. firstly, i have to give a shoutout to whosoever is responsible for the firmware at asus. though it was on the default on the asus firmware for very tragic short time, before i flashed it with openwrt, the features it provided blew my mind. compared to my default isp gateway that just thinks debugging is just ping and traceroute commands, the default asus firmware gave a lot of commands. to be honest it looked straight out of alien movie. image for reference below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/asus-default-firmware.jpeg&quot; alt=&quot;asus default firmware web ui&quot; /&gt;&lt;/p&gt;
&lt;p&gt;it has a lot of features that i wouldn&#39;t expect in a home consumer router (mentioning only those that surprised me. it already has the most basic ones that you&#39;d expect any router to have):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;managing qos bandwidth&lt;/li&gt;
&lt;li&gt;live traffic monitoring with graph&lt;/li&gt;
&lt;li&gt;ftp sharing via usb plugged in the two ports it provides&lt;/li&gt;
&lt;li&gt;option to configure 2.4ghz + 5ghz dual ssids (though of no use for me. more on that below)&lt;/li&gt;
&lt;li&gt;frigging radius client config settings (why on earth, but yes)&lt;/li&gt;
&lt;li&gt;good firewall features with keyword filter as well as url based&lt;/li&gt;
&lt;li&gt;system log with ability to download the logs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;flashing openwrt&lt;/h2&gt;
&lt;p&gt;i flashed it with openwrt the day i got it. how i got it is a different story in itself. now the problem with openwrt is that rt-ac68u has broadcom wifi drivers and they don&#39;t opensource them&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://dhrm1k.github.io/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, so no wifi support. though that is not an issue, cause i had a tp link router lying around that i now use as dumb AP.
with openwrt too this is a beast and one hell of a firewall. i get 102mb of overlay space and trust me, that is not less than heaven for a router. there&#39;s so much that i do and have done on it.
one of the first things i did after flashing was installing tailscale. make a new zone for it and allow everything on it (hoping one day i will even use it as a exit node as well). i run a custom dns config with smartdns and adblock-fast. my primary ISP router also forwards dns queries to this network.
now this weekend, i had the idea to tweak firmware a little. make it a little rolling-release instead of getting stable release, add a little more security hardening than is already there. this router&#39;s board is bcm53xx/generic. now i had two options. either i could setup an entire build machine or download a image builder. considering it was the weekend i decided to go for the image builder path considering i wouldn&#39;t have to dedicate an hour or two in building the initial firmware image only.
i downloaded the image builder and found the target.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;make info | grep -i -A 10 &#39;RT-AC68U&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;lost my configuration&lt;/h2&gt;
&lt;p&gt;now i built it and then got the .trx file. i was shocked to see this. i am usually familiar with the sysupgrade images and was expecting that, but it turns out the broadcom drivers expect this. i took a obligatory backup of my router and then moved it to my homelab that is under the asus network and not the isp network and flashed the new image. well, i used the command sysupgrade -T to test the image and later sysupgrade -v to flash it. now what sysupgrade does is, it retains config on upgrade via sysupgrade, but for me, this time it didn&#39;t. it completely removed all config. now i had to again give my laptop a static ip, configure the network and restore from the backup and figure out why this happened. now the problem with this was that i had no idea. i had to first figure out and learn about how sysupgrade works.&lt;/p&gt;
&lt;p&gt;while digging through this, i found out that i wasn&#39;t the first one to hit this (kinda sad well to see this). there was already a fix &lt;a href=&quot;https://github.com/openwrt/openwrt/commit/758d309f64ae6738b03a049f1b04650e6db25f1b&quot;&gt;upstream&lt;/a&gt; for bcm53xx nand devices losing config during sysupgrade. the problem was just that the fix hadn&#39;t made it into the latest image i was using yet.&lt;/p&gt;
&lt;p&gt;the fix&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://dhrm1k.github.io/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; itself was hilariously small.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;- local root_type=$(identify $dir/root)
+ local root_type=$(identify &amp;quot;$dir/root&amp;quot; &amp;quot;cat&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;identify&lt;/code&gt; had changed earlier to expect the extraction command as another argument. the bcm53xx upgrade code was still calling it the old way. because of that, it failed to identify the extracted root filesystem as ubi and fell back to writing the whole trx image directly to the firmware partition.&lt;/p&gt;
&lt;p&gt;and that fallback was the important part. the normal nand upgrade path knows how to carry &lt;code&gt;/tmp/sysupgrade.tgz&lt;/code&gt; into the new ubi/rootfs_data setup. the fallback path didn&#39;t. so the router booted perfectly fine after the upgrade, except it came up like i had passed &lt;code&gt;-n&lt;/code&gt; to sysupgrade and nuked the config myself.&lt;/p&gt;
&lt;h2&gt;what should have happened&lt;/h2&gt;
&lt;p&gt;the path should have been pretty simple. &lt;code&gt;sysupgrade&lt;/code&gt; first creates &lt;code&gt;/tmp/sysupgrade.tgz&lt;/code&gt; containing the configuration files that need to be preserved. it then switches into the ram-based upgrade environment and starts flashing the new firmware. since this router uses nand with ubi&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://dhrm1k.github.io/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;, the upgrade process is also responsible for carrying that backup into the new writable filesystem before the router reboots. once the new firmware comes up, the old configuration should already be there on the new overlay and the router should come back almost exactly the way it was before the upgrade.&lt;/p&gt;
&lt;p&gt;since this router uses nand with a ubi image, the upgrade process is also responsible for carrying that backup into the new writable filesystem before the router reboots.&lt;/p&gt;
&lt;h2&gt;what actually happened&lt;/h2&gt;
&lt;p&gt;the actual path started the same way. &lt;code&gt;sysupgrade&lt;/code&gt; created &lt;code&gt;/tmp/sysupgrade.tgz&lt;/code&gt; and switched into the ram-based upgrade environment. the bcm53xx upgrade code then extracted the kernel and root filesystem from the trx image. after that, it called &lt;code&gt;identify&lt;/code&gt; to check whether the extracted root filesystem was a ubi image.&lt;/p&gt;
&lt;p&gt;this is where things went wrong. because the extraction command was not passed to &lt;code&gt;identify&lt;/code&gt;, it couldn&#39;t read the file and returned an empty result. the upgrade code took that to mean that the image was not using ubi and returned to the generic upgrade path. that path then wrote the complete trx image directly to the firmware partition.&lt;/p&gt;
&lt;p&gt;the generic path did still pass the configuration backup to &lt;code&gt;mtd&lt;/code&gt;, which works for the non-ubi images that this fallback is actually meant for. but mine was a ubi image that had been identified incorrectly. &lt;code&gt;mtd&lt;/code&gt; could write the trx image, but it couldn&#39;t put &lt;code&gt;/tmp/sysupgrade.tgz&lt;/code&gt; inside the new &lt;code&gt;rootfs_data&lt;/code&gt; ubi volume. the router therefore booted the new firmware with the default configuration while my backup was left behind in the old upgrade environment and disappeared when the router rebooted.&lt;/p&gt;
&lt;h2&gt;another problem hiding in the same upgrade path&lt;/h2&gt;
&lt;p&gt;while trying to understand why the configuration disappeared, i kept reading through the rest of the bcm53xx upgrade code and found another problem in the same fallback path. the nand-aware helpers used &lt;code&gt;return&lt;/code&gt; not only when an image was intentionally supposed to use the old whole-image upgrade path, but also when something had actually failed.&lt;/p&gt;
&lt;p&gt;this meant that failures such as not being able to extract the trx partitions, the new kernel being too large for its partition, failing to prepare the kernel trx or failing to prepare the ubi image didn&#39;t necessarily stop the upgrade. the helper returned to its caller, and the caller continued by writing the complete image directly to nand anyway. in the kernel size case, this practically meant that the code could tell you that the kernel didn&#39;t fit and then continue flashing the image through a path that bypassed the same check.&lt;/p&gt;
&lt;p&gt;the earlier &lt;code&gt;identify&lt;/code&gt; fix solved the bug that had removed my configuration, but these other returns were still there. so i opened &lt;a href=&quot;https://github.com/openwrt/openwrt/pull/24841&quot;&gt;openwrt/openwrt#24841&lt;/a&gt; to change the genuine preparation errors from &lt;code&gt;return&lt;/code&gt; to &lt;code&gt;exit 1&lt;/code&gt;. now, if one of those steps fails, the upgrade stops instead of falling through to the whole-image writer.&lt;/p&gt;
&lt;p&gt;the patch still keeps the intentional return for actual non-ubi images. those images are supposed to use the older &lt;code&gt;default_do_upgrade()&lt;/code&gt; path, where configuration can still be preserved using &lt;code&gt;mtd -j&lt;/code&gt;. so the change is not about removing the fallback completely. it is about making sure that only images meant for that fallback can reach it, while real errors stop the upgrade.&lt;/p&gt;
&lt;p&gt;let&#39;s see if our changes get merged. it was a fun day of debugging and learning how the upgrade process works. i have named my gateway without a doubt with the lack of ideas and a name hiding in plain-sight:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dhrm1k.github.io/assets/images/ana_v1.png&quot; alt=&quot;ana v1 terminal screenshot with my kitty wallpaper&quot; /&gt;&lt;/p&gt;
&lt;p&gt;for now we have fixed a few bugs. my image builder now has all the packages i want by default, has all the config of the packages i use by default. has the patch we wanted and we made, so future sysupgrades are smooth. there&#39;s more i want to do. maybe changing privilege daemon&#39;s have or i don&#39;t know. keeping it out for a while. the above was one hell of a experience.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;broadcom&#39;s driver used by the stock firmware is proprietary. linux has the open source &lt;code&gt;b43&lt;/code&gt; driver, which was largely developed by reverse engineering broadcom hardware, but its support for the bcm4360 ac phy used by the rt-ac68u is marked as broken and is not enabled by openwrt. because of that, neither of the router&#39;s built-in wifi radios is usable with openwrt. proper support could still be developed if broadcom ever released the required hardware documentation, programming guides or an open source driver. &lt;a href=&quot;https://dhrm1k.github.io/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;code&gt;identify&lt;/code&gt; is a shell function from openwrt&#39;s nand upgrade library. it reads the first few bytes of an image and compares their magic number to known formats such as ubi, ubifs, fit and gzip. its first argument is the image to inspect and its second argument tells it how to read that image. for a normal uncompressed file, that command is &lt;code&gt;cat&lt;/code&gt;. without the second argument, the function couldn&#39;t read the extracted root filesystem and returned an empty result instead of &lt;code&gt;ubi&lt;/code&gt;. &lt;a href=&quot;https://dhrm1k.github.io/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;ubi stands for unsorted block images. it is a layer designed for raw nand flash that handles things such as bad blocks, wear levelling and dividing the flash into volumes. on this router, those volumes hold the read-only root filesystem and the writable &lt;code&gt;rootfs_data&lt;/code&gt; used for configuration and other changes. ubi itself is not a filesystem. filesystems such as ubifs can be placed inside its volumes. &lt;a href=&quot;https://dhrm1k.github.io/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
]]></description>
      <link>https://dhrm1k.github.io/blog/bcm53xx-sysupgrade-error/</link>
      <guid>https://dhrm1k.github.io/blog/bcm53xx-sysupgrade-error/</guid>
      <pubDate>Sun, 23 Aug 2026 00:00:00 GMT</pubDate>
    </item></channel>
</rss>