[{"content":"The motivation Being one of the maintainers of Termux, I have to constantly build packages from source. Sometimes I have to rebuild the same package multiple times to fix some bug. Some of the packages are not so friendly on disk-writes. Some packages require cloning large git repos (multiple gigabytes), build itself does writes of multiple gigabytes at times. Some of the builds fail mid-way, thus having to repeat the entire cycle once again. Once again gigabytes of writes to the disk. During rebuilds of large packages with large reverse dependency trees, rebuilds are even longer. Once I even managed to do 1TBW on my SSD in a span of 10 hours. This was during test-rebuild of around 300 or the 3000 packages we have during CMake 4.0 update. Initially the plan was to rebuild all packages and obtain a list of all packages that are failing to build but it was later abandoned due to large amount of disk writes it\u0026rsquo;d do.\nIf I continued on that journey, I\u0026rsquo;d have exhausted a good chunk of my SSD\u0026rsquo;s lifespan by now, and my SMART diagnostics would have been screaming at me.\nAlso the blinking hard disk LED on my laptop is quite annoying, so that\u0026rsquo;s an additional motivational factor.\nAnalyzing docker directories Docker stores it\u0026rsquo;s daemon configuration at /etc/docker/daemon.json. Looking over there:\n1 2 3 { \u0026#34;data-root\u0026#34;: \u0026#34;/var/lib/docker\u0026#34;, } This is where docker stores all persistent data including containers, images, volumes, and all 1\nFirst we need to stop docker, or else weird things could happen:\n1 2 sudo systemctl stop docker.service sudo systemctl stop docker.socket So let\u0026rsquo;s try to mount /var/lib/docker on tmpfs. First we clean the entire /var/lib/docker as we need to mount a tmpfs. And mounting filesystems require the directory to be empty. You can also choose to copy your /var/lib/docker to restore it back after mounting the tmpfs if you wish to keep your old containers and images. For me I had nothing very important so I decided to just nuke it all for good.\n1 2 sudo rm -r /var/lib/docker sudo mkdir /var/lib/docker Now, let\u0026rsquo;s mount the tmpfs:\n1 sudo mount -t tmpfs none -o size=48G /var/lib/docker The default tmpfs size is of 16G, which may not be enough for your needs. It wasn\u0026rsquo;t for me atleast, as the docker image we use for building packages is 8G+ uncompressed when built locally.\nIf you had earlier backed-up your /var/lib/docker, it\u0026rsquo;s time to restore it now.\nNow, the moment of truth. Let\u0026rsquo;s get docker running and see if things are working as expected.\n1 sudo systemctl start docker.service Now start your favourite container and do some writes.\n1 dd if=/dev/random of=myfile.txt count=4 size=1G And your disk LED indicator doesn\u0026rsquo;t blink (in your dreams). You ask yourself, why is it blinking? It\u0026rsquo;s not supposed to.\nPartial failure Your write-heavy docker container is still writing to disks and you can see that in your htop\u0026rsquo;s I/O tab or iotop (whihchever\u0026rsquo;s your favourite. I prefer htop but you are allowed to have your own choices).\nSo what went wrong? Remember the docker documentation about docker storing persistent files in /var/lib/docker, then why is it doing writes outside of there?\nTurns out it\u0026rsquo;s containerd, the underlying layer which docker uses to manage namespaces, volumes for containers with the Linux kernel. Containerd mounts overlayfs for mounting partitions for the containers that are going to run. Since we know that it\u0026rsquo;s using overlayfs, we can just give a peek at all the mounts we have using mount:\n1 2 3 4 proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) sys on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) ... overlay on /var/lib/docker/rootfs/overlayfs/555bd63bcb12dc7a5676d1005fdc22140555e6786e05e637fbdbe9758236043d type overlay (rw,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/10/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/9/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/8/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/7/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/5/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/4/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/3/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/2/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/11/fs,workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/11/work,index=off) If you don\u0026rsquo;t know how overlayfs works, I\u0026rsquo;d highly recommend checking out the Linux kernel documentation about overlayfs first before reading beyond this section 2\nA quick brief would be that overlayfs allows you to mount multiple directories together. Effectively merging them without actually merging them. You get a virtual filesystem which is a combination of all the lowerdir. All writes goes to upperdir. workdir is used internally by the kernel, documentation doesn\u0026rsquo;t specify how though. Files are first looked for in upperdir and only looked for in lowerdir if they don\u0026rsquo;t exist in upperdir. This is also what allows docker to split an image in layers. Each layer is effectively the upperdir of the step which was executed during image creation. At the end when running the container, additional overlayfs is mounted with the image as lowerdir and container\u0026rsquo;s specific writes inside it\u0026rsquo;s own upperdir. This is what allows docker to run large containers without having to copy each and every file. Pretty amazing, huh! Anyways this is not what we are after right now, so let\u0026rsquo;s not get sidetracked.\nSo looking at the above overlayfs mount, we can see that containerd, is storing the actual container\u0026rsquo;s files when it is executed in /var/lib/containerd/*, so this also needs to be on tmpfs like /var/lib/docker.\nSo it\u0026rsquo;s simple, just mount /var/lib/containerd on tmpfs like we did for /var/lib/docker and restart the docker service. Instant profit! This time we also need to restart the containerd service as we are messing with it\u0026rsquo;s files\n1 2 3 4 5 6 7 8 9 10 sudo systemctl stop docker.service sudo systemctl stop docker.socket sudo systemctl stop containerd.service sudo rm -r /var/lib/containerd sudo rm -r /var/lib/docker sudo mkdir /var/lib/containerd sudo mkdir /var/lib/docker sudo mount -t tmpfs -o size=48G /var/lib/containerd sudo mount -t tmpfs -o size=48G /var/lib/docker sudo systemctl start docker.service When starting the docker service, it should start the containerd service as well. Stopping docker daemon doesn\u0026rsquo;t seem to stop containerd, so we have to do it manually\nAnd voila! It\u0026rsquo;s running on tmpfs now. Although I would consider you to be a total crazy person like me to use RAM for such crazy experiments considering RAM prices nowadays.\nAdditional notes and recommendation I\u0026rsquo;d recommend setting up zram on your device depending on the amount of system memory you have available. Most of the stuff in /var/lib/docker/rootfs can be compressed pretty well, and you might benefit from compression when those files are not accessed. And anyways zram doesn\u0026rsquo;t kick in unless you actually need it depending on your swappiness kernel config. It acts just like swap but on RAM. Setting up zram should also allow you to overprovision the size of your mounts for containers and docker data directory. Depending on memory pressure, zram gives easily around 2:1 compression ratio on average. With multiple VMs running during benchmarking of zram for my personal use, I have even managed to get 3.5:1 compression ratio. Your mileage will definitely vary based on your workflow.\nFor me honestly the performance benefits of not having to write to the disks, and additionally not absolutely burning my SSD\u0026rsquo;s lifespan is definitely worth it. And with zram, I can manage to even keep my browser, and other applications open with full docker running on tmpfs and building most packages for Termux.\nDocker data directory https://docs.docker.com/engine/daemon/#daemon-data-directory\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nOverlayFS kernel documentation https://docs.kernel.org/filesystems/overlayfs.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","permalink":"https://thunders.website/posts/2026-01-10-docker-on-tmpfs/","summary":"\u003ch2 id=\"the-motivation\"\u003eThe motivation\u003c/h2\u003e\n\u003cp\u003eBeing one of the maintainers of \u003ca href=\"https://termux.dev\"\u003eTermux\u003c/a\u003e, I have to constantly build packages from source. Sometimes I have to rebuild the same package multiple times to fix some bug. Some of the packages are not so friendly on disk-writes. Some packages require cloning large git repos (multiple gigabytes), build itself does writes of multiple gigabytes at times. Some of the builds fail mid-way, thus having to repeat the entire cycle once again. Once again gigabytes of writes to the disk. During rebuilds of large packages with large reverse dependency trees, rebuilds are even longer. Once I even managed to do 1TBW on my SSD in a span of 10 hours. This was during test-rebuild of around 300 or the 3000 packages we have during \u003ca href=\"https://github.com/termux/termux-packages/pull/25014\"\u003eCMake 4.0 update\u003c/a\u003e. Initially the plan was to rebuild all packages and obtain a list of all packages that are failing to build but it was later abandoned due to large amount of disk writes it\u0026rsquo;d do.\u003c/p\u003e","title":"Docker on tmpfs"},{"content":"If you are anywhere on the internet today, you know how much the internet is now filled with posts, comments and content just generated straight up by Large Language Models (or LLMs). While I am amazed by how much just random numbers and next word prediction has progressed, and I am just truly annoyed by how much they are being overused. I\u0026rsquo;m all in on technology, and strongly believe that we all should progress and adopt new technology. Adopting new technologies makes us a better version of ourselves and spend more time on the actual fun-stuff and less on the boring parts.\nI see people around me using these tools like ChatGPT, Gemini for writing birthday wishes, LinkedIn posts about a project that was clearly not vibe coded entirely by AI. Heck people are even using AI to write their entirely of social media feed.\nI started using the internet back when there was no ChatGPT or any other AI which you could use to write out essays for you. The most advanced AIs I used know of were Stockfish, AlphaZero which were the strongest chess engines of the time. And the chatbot type AIs used to be Akinator, which amazed me back then how it worked. The only way how I would come up with design of such an algorithm would be to use a combination of if-else statements, and looking now I know that this wouldn\u0026rsquo;t suffice. Now with ChatGPT, people can just ask it to correct the grammar or ask it to write a shiny text for their new posts on social media. People seem to have just lost the concept of what makes social media social, it\u0026rsquo;s the people. By just eliminating yourself from the equation, it\u0026rsquo;s just pushing towards Dead Internet Theory.\nI don\u0026rsquo;t want to read your 1000 word essay made with ChatGPT or any other fancy AI that\u0026rsquo;s in the town right now. If it\u0026rsquo;s a 10-word thing, I wanna read that 10-word description which you came up with yourself.\nThe situation is very disheartening for me since I used to love reading blogs, articles and stuff online. In fact that\u0026rsquo;s how I learnt a lot of stuff about technology. This is also in fact the reason why Reddit as a social media platform is so popular. Most of the content is user generated and as a forum it\u0026rsquo;s pretty popular. Reddit combined the fragmented communities which used to be scattered around various forums (although Reddit also led to the extinction of a lot of those forums, but that\u0026rsquo;s a topic for some other day). I still to this day follow a lot of individual blogs about technology and stuff that interests me. This AI slop culture makes me very sceptical of new writings, I just don\u0026rsquo;t trust if I\u0026rsquo;m reading some genuine writings written by some human who is sharing words of wisdom learnt through experience or some guy who just decided to fill the internet with some AI generated bullshit. I have followed engineering blogs since a while now, and more I keep on reading nowadays, I feel less like reading them, it seems like the ChatGPT effect has kicked in everywhere. A lot of people are now poisoning their original train of thoughts by using LLMs for writing things for them to increase the wordcount or to make it more fancy.\nThis is also one of the reasons why I don\u0026rsquo;t feel like writing blogs more often. Although I do have some more blogs I wanted to write to share my thoughts. I don\u0026rsquo;t know how many of my visitors are going to be these AI crawlers just crawling this page continuously and how many of them actual humans. But if atleast one other person learns something interesting thing from my writing, I\u0026rsquo;ll consider that I helped another version of me who learnt a lot about tech from blogs and articles written by some another stranger on the internet\u0026hellip;\nBtw, if you found this interesting, you might be interesting in following this blog. You can do so using RSS which is also an open standard so you don\u0026rsquo;t need to surrender your email or any personal info in order to keep updated with what I post. You just need to hook the RSS URL into your RSS client of choice.\n","permalink":"https://thunders.website/posts/2025-12-13-ai-slop/","summary":"\u003cp\u003eIf you are anywhere on the internet today, you know how much the internet is now filled with posts, comments and content just generated straight up by Large Language Models (or LLMs). While I am amazed by how much just random numbers and next word prediction has progressed, and I am just truly annoyed by how much they are being overused. I\u0026rsquo;m all in on technology, and strongly believe that we all should progress and adopt new technology. Adopting new technologies makes us a better version of ourselves and spend more time on the actual fun-stuff and less on the boring parts.\u003c/p\u003e","title":"AI Slop"},{"content":"I have updated my GPG keys. A copy of the new keys is available on my website at https://thunders.website/yaksh.gpg.\nThis is a periodic rotation of secrets, and all my devices to the best of my knowledge are not compromised. Kindly import the new keys so that you can verify my signatures/send me private messages as usual.\nWarning\nThe old keys had no expiry when I created and circulated them, please make sure that after importing the keys from my website, they have an expiry date set to 2035-06-10. If you find that the keys do not have an expiry date, please delete them and re-import the keys from my website.\nImporting the new keys You can import the new keys using the following command:\n1 2 curl https://thunders.website/yaksh.gpg -o yaksh.gpg gpg --import yaksh.gpg Key fingerprints Old key fingerprint (make sure you verify the expiry date of the key):\n1 2 3 4 5 6 pub ed25519 2021-05-08 [SC] [expires: 2026-06-12] 6E519146C7A2B81BBF801A9CF7486BA7D3D27581 uid [ultimate] Yaksh Bariya \u0026lt;thunder-coding@termux.dev\u0026gt; uid [ultimate] Yaksh Bariya \u0026lt;thunder@termux.org\u0026gt; uid [ultimate] Yaksh Bariya \u0026lt;yakshbari4@gmail.com\u0026gt; sub cv25519 2021-05-08 [E] New key fingerprint (set to expire 10 years as of writing this post):\n1 2 3 4 5 pub ed25519 2025-06-12 [SC] [expires: 2035-06-10] 94104F935B5362B3150EB7C1FDD928D965207016 uid [ultimate] Yaksh Bariya \u0026lt;thunder-coding@termux.dev\u0026gt; uid [ultimate] Yaksh Bariya \u0026lt;yakshbari4@gmail.com\u0026gt; sub cv25519 2025-06-12 [E] [expires: 2035-06-10] Signed message The below signed message is signed with my old key, stating that I have updated my keys. You can verify the signature using the old key fingerprint above. This should serve as a proof that the new keys are indeed from me and not someone else.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -----BEGIN PGP MESSAGE----- owG9lDtoFEEYx88YBVfEIJhCDXza+CC32d27vUcQSTZ3F+NbEhAJV8zdTm4n2Z1Z d2buciD4qCxjY+UDbKxEm4CIaKEgWihERBsrQdBKtDCVOru5KBIUbdxuvpn///v+ vx1mbsPqlNb7Zf/MjRcL8tyqm91ObX2AOUcNrItZ4R3c9mTCIxw6NWghDq2ICIEp MAoHJMVgWuAjgYGShidg19j4xG4dxvrhJJrhHjgoIm0EHmpiCFgTuyAYIKC4BaPH RmEGt3VNm/BwUlErUN0QlEuWbZvFpDAtuQCfzGAQ6lgY4SZhkqv2uB84AyKAe0z6 LtQwcDSFYSpiAZySiAoZQJ0FoRQ44jrEXWLDzvHY0m//VLEITAPaGEUccFMFRFNK B8htIlrHAaaCA6EdQ0IbELIWjnRt2HWJIIwiX7nFIzLfVbq4E2UtlVwFooBnQxKp 3lgkU7eZ3On7S1gUkAiTIGSRUGl0OMkk1JVkilA3MZShqwC7sWWcGzwhQj44MCA8 Sd04WgvXOBF4oB0j1xthQ9OO+sn52KSBozAiVAxqoawBAHaX4FqGZaYNO20UYHJ8 pAqTyYyYD8Y7ubSRS5tWVYPky5WVwszmRvLDllMwHadSMMzh4kglny3knOF8KVOy 8nbB1CRx4ec3KX1BAjV89dfrsLczerrOXDXgkAIdyFndxc19/+iwLGVR46+lCaaa WmSHGgEivq7+6T6NJ3DqzZVwylVNO9K5nn/maS9RW8kzs7RjLPMsZk0jWylmbMfO 5CwnY9pG2cmPmJVSqWgVSsWcbRl5w8z9P55/CeVHwvLvAl6Q3aleLdXXu72bHr9Y eXz9ztYHZ7dcXn5k1nTFr0pKW9ezXDk/kfpGpx/2fTwzlp8/fevw1fPz74Zuvznx 6dJbZ/fCxvbang/3Ul/3vL62f+7Z/VdXsk93bAoWTy0Gdw99fpTdXH3+sv99aXPX dw== =FYP0 -----END PGP MESSAGE----- You can verify the signature using the following command (make sure you have my old key in your keyring first):\n1 2 # First save the abovee pgp signed message to a file, say message.asc gpg --decrypt message.asc You should see a message printed out containing the my old as well as new key fingerprints.\n","permalink":"https://thunders.website/posts/2025-06-13-update-to-my-gpg-keys/","summary":"\u003cp\u003eI have updated my GPG keys. A copy of the new keys is \u003ca href=\"https://thunders.website/yaksh.gpg\"\u003eavailable on my website at https://thunders.website/yaksh.gpg\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThis is a periodic rotation of secrets, and all my devices to the best of my knowledge are not compromised. Kindly import the new keys so that you can verify my signatures/send me private messages as usual.\u003c/p\u003e\n\n\n  \n  \u003cblockquote class=\"alert alert-type-warning\"\u003e\n    \u003cdiv class=\"alert-heading-container\"\u003e\n      \u003csvg\n  xmlns=\"http://www.w3.org/2000/svg\"\n  viewBox=\"0 -960 960 960\"\n  class=\"alert-icon\"\n\u003e\n  \u003cpath\n    d=\"m40-120 440-760 440 760H40Zm138-80h604L480-720 178-200Zm302-40q17 0 28.5-11.5T520-280q0-17-11.5-28.5T480-320q-17 0-28.5 11.5T440-280q0 17 11.5 28.5T480-240Zm-40-120h80v-200h-80v200Zm40-100Z\"\n  /\u003e\n\u003c/svg\u003e\n\n      \n        \u003cp class=\"alert-heading\"\u003eWarning\u003c/p\u003e","title":"Update to my GPG keys"},{"content":"TL;DR If you want to have a good read, I\u0026rsquo;d recommend skipping the TL;DR segment. You might miss some fun when reading the article if you read this out. You\u0026rsquo;ve been warned.\n/dev/stdin, /dev/stdout, and /stderr are not required to be implemented by the POSIX standard, but are listed as POSIX extensions. Similarly, /proc (or the procfs) is also just listed as an extension just once in the POSIX standard, with no mention of what things need to be there inside.\nIf you read it fully, you might find a interesting thing or two which you might get surprised to figure out even if you are using a Unix-like system since years.\nNote\nFrom Wikipedia\u0026rsquo;s POSIX page section \u0026ldquo;Versions after 1997\u0026rdquo;\nAfter 1997, the Austin Group developed the POSIX revisions. The specifications are known under the name Single UNIX Specification, before they become a POSIX standard when formally approved by the ISO.\nFrom Wikipedia\u0026rsquo;s UNIX page The Single UNIX Specification (SUS) is a standard for computer operating systems, compliance with which is required to qualify for using the \u0026ldquo;UNIX\u0026rdquo; trademark.\nSo every UNIX system can also be referred as a POSIX system.\nIntroduction If you have use Linux, or any other *NIX like system, or even just wrote bash scripts to automate your silly tasks in the terminal, you might be knowing of the following special files:\n/dev/stdin /dev/stdout /dev/stderr /dev/null /dev/random /dev/sd{a,b,...} /proc/* If not all, you might be atleast knowing a few of them (if not others atleast /dev/stdin, /dev/stdout or /dev/stderr). Now I am not going to the depths of *NIX and try to explain in detail about each and every of these files, because I believe there are better articles on the web that do their job pretty well.\nThis article is going to be about portability and how a lot of the Unix-like systems which are popular today differ from the POSIX standards\nFile Descriptors on POSIX systems File Descriptors (or FDs in short) are just integer identifiers for open files for a process. There are 3 special FDs:\n0: for stdin 1: for stdout 2: for stderr Now if you are a seasoned Bash scripting guy (or even a seasonal one), you might have done something like this to pass the output of previous command to an executable that does not support reading from the stdin, but only supports reading from a file:\n1 2 3 # prev_command | ./another_executable # Above commented command doesn\u0026#39;t work as another_executable isn\u0026#39;t designed to read from stdin prev_command | ./another_executable /dev/stdin This just tricks the another_executable to read from stdout even though the executable doesn\u0026rsquo;t support it. This is because on a lot of UNIX-like Systems (but not all UNIX-like systems, and we\u0026rsquo;ll be coming to that point later), /dev/stdin is a special block device that just contains the contents of the standard input.\nSimilarly, the following hack is also used at times when the command only supports writing to a file, but you want the output in stdout:\n1 ./command /dev/stdout This works, just like the above command. UNIX philosophy of everything is a file is indeed really wonderful.\nYou might have used /dev/stdin, /dev/stdout and /dev/stderr on a multitude of systems including Linux, FreeBSD, etc.\n/dev and /proc virtual filesystem If you are using a Unix-like system since some time, you must be knowing that /dev, /proc and /sys are some special directories containing virtual devices/other virtual files that contain system information like uptime information, kernel version, cpu information, etc. You might even have read from them for your own bash scripts at times.\nSurprise 1: /dev/stdin, /dev/stdout and /dev/stderr may not exist on a POSIX compliant system This started with me running the Node.js test suite on my old Android device running Android 7.0. Some of the tests that failed on it were tests trying to read the /dev/stdin file instead of just simply trying to read the file via process.stdin. There are tests for process.stdin separately as well, but some were hardcoded to simply read /dev/stdin on Unix-like systems. One of the tests had something like (test/parallel/test-fs-readfile-pipe-large.js):\n1 2 3 4 5 6 ... fs.readFile(\u0026#39;/dev/stdin\u0026#39;, function(er, data) { assert.ifError(er); process.stdout.write(data); }); ... Then I was like, huh! Why is this failing with an error indicating that the file /dev/stdin does not exist? I just did a quick file /dev/stdin and found that it doesn\u0026rsquo;t exist. Nor did /dev/stdout and /dev/stderr exist. I just used to assume that they do exist on all devices! Then I looked on the internet to find the workaround, and StackOverflow gave me a workaround: to use /proc/self/fd/{fd} where fd is 0, 1 or 2. And it worked.\nThen I fixed the tests and kept the patches with me for around 3 years and forgot about it. And now that I decided to upstream some of the Android build fixes for Node.js which I was keeping with me for a long time so that others may benefit too. I sent the PR and waited for CI to turn green, but it failed. And it turns out that MacOS doesn\u0026rsquo;t have procfs (/proc), but exposes most of those stuff via sysctl as documented over https://web.archive.org/web/20200103161748/http://osxbook.com/book/bonus/ancient/procfs/.\nThen I looked over on my Arch Linux system:\n1 2 3 4 $ file /dev/std* /dev/stderr: symbolic link to /proc/self/fd/2 /dev/stdin: symbolic link to /proc/self/fd/0 /dev/stdout: symbolic link to /proc/self/fd/1 Huh! and they exist on my Arch box. with symlinks to /proc/self/fd/0, /proc/self/fd/1 and /proc/self/fd/2\nThis time, I checked whether the patch was actually needed right now. Maybe Android now as /dev/stdin, /dev/stdout and /dev/stderr. Then I once again looked on my newer Android device:\n1 2 3 4 5 6 7 # Can\u0026#39;t use glob /dev/std* without root on Termux (works with ADB though) ~ $ file /dev/stdin /dev/stdin: symbolic link to /proc/self/fd/0 ~ $ file /dev/stdout /dev/stdout: symbolic link to /proc/self/fd/1 ~ $ file /dev/stderr /dev/stderr: symbolic link to /proc/self/fd/2 Perhaps /dev/std* didn\u0026rsquo;t exist on the older Android device as it had a really older kernel version. I didn\u0026rsquo;t have the older device with me, so I tried running a Android VM with Android 8.1 and it didn\u0026rsquo;t have /dev/std* as well, so definitely it was a Android using older version of the Linux kernel.\nSo, Linux just has /dev/std* as symlinks to /proc/self/fd/*. But those don\u0026rsquo;t exist on MacOS. Why is MacOS acting weird and not just following the standard. And FreeBSD too didn\u0026rsquo;t have /proc virtual filesystem. I couldn\u0026rsquo;t find any information about why this is the case, so I decided to take matters into my own hands and try to read the POSIX manual and figure out if /dev/stdin, /dev/stdout /dev/stderr, and /proc/* are even part of the specification. And they aren\u0026rsquo;t! Yes, you read that right, /dev/stdin, /dev/stdout and /dev/stderr are mentioned exactly once together and are mentioned as optional non-standard extensions.\nBoth /dev/stdin and /dev/stderr are just mentioned exactly once in the entire POSIX.1-2024 document (the latest as of writing this blog) which defines the standards for any system to be POSIX complaint.\nThe system may provide non-standard extensions. These are features not required by POSIX.1-2024 and may include, but are not limited to: \u0026hellip; Additional character special files with special properties (for example, /dev/stdin, /dev/stdout, and /dev/stderr) 2.1.1 Requirements, Page 15\nBut funnily enough there are a total of 14 mentions of /dev/stdout in the document as possible files passed to various POSIX command line utilities. Some of them mentions that passing - or /dev/stdout to the output file would result in the output being written to the stdout. So perhaps, even though the /dev/stdout is an optional non-standard extension, it\u0026rsquo;s popular enough that the authors decided to mention it in the document.\nSurprise 2: /proc itself may not exist on a POSIX compliant system The POSIX standards do not even specify anything about /proc, even the procfs is mentioned only once in the entire document as a non-standard extension. (See Page 15 of the document 2.1.1 Requirements).\nFreeBSD doesn\u0026rsquo;t even have procfs enabled by default and you need to mount it yourself. And it\u0026rsquo;s deprecated\nSurprise 3: /dev/random is also not part of the POSIX specification You might have just read from /dev/random multiple times in bash scripts when trying to gather some entropy or random data. But the POSIX has no mention it in it\u0026rsquo;s entire specification! In fact the POSIX specification only mentions about /dev/null, /dev/tty and /dev/console. Yes, that\u0026rsquo;s the entire thing documented under chapter 10 titled \u0026ldquo;Directory Structure and Devices\u0026rdquo;.\nConclusion There /dev/stdin, /dev/stdout or /dev/stderr which is guaranteed to work on all POSIX systems. It is very important to note this when writing portable software. Although all these files exist on almost all POSIX system I have used (if you add the old Android phone I mentioned of earlier), it\u0026rsquo;s not guaranteed to work. The only portable way is to actually just use the file descriptors 0, 1, and 2 for /dev/stdin, /dev/stdout and /dev/stderr respectively. And there exists no portable way to specify a program to write to stdout/stderr instead of a file unless the program supports doing it.\nAlthough a lot of the special files which we are used to in the /dev and /proc are missing from the POSIX standard, I must say that almost all of the C APIs are really well thought and documented really well. The structs for all the data types like addrinfo are really well thought of. The C API documentation also is very extensive. This proves that even after 50+ years of existing, Unix\u0026rsquo;s design was just really good and well thought that still today we have majority of webservers, and almost all of the supercomputers in the world running either Linux or other Unix-like OS like BSDs. I just find it wild how well thought the entire Unix OS was at that time!\nAlso at times, it\u0026rsquo;s better to read the documentation/manual/specification instead of relying on internet articles to solve your problems if you don\u0026rsquo;t want headaches in future.\nCorrections This article may not be completely accurate, although I have tried to ensure that it is. In case you find any inaccuracies, feel free to reach me out via email: yakshbari4@gmail.com, I\u0026rsquo;d be happy to correct this article.\n","permalink":"https://thunders.website/posts/interesting-case-of-procfs-and-dev-on-posix-systems/","summary":"\u003ch2 id=\"tldr\"\u003eTL;DR\u003c/h2\u003e\n\u003cp\u003eIf you want to have a good read, I\u0026rsquo;d recommend skipping the TL;DR segment. You might miss some fun when reading the article if you read this out. You\u0026rsquo;ve been warned.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e/dev/stdin\u003c/code\u003e, \u003ccode\u003e/dev/stdout\u003c/code\u003e, and \u003ccode\u003e/stderr\u003c/code\u003e are not required to be implemented by the POSIX standard, but are listed as POSIX extensions.\nSimilarly, \u003ccode\u003e/proc\u003c/code\u003e (or the procfs) is also just listed as an extension just once in the POSIX standard, with no mention of what things need to be there inside.\u003c/p\u003e","title":"Interesting case of /proc and /dev on POSIX systems"},{"content":" Note\nIf you would like to see my final submitted code, you can find it on thunder-coding/Hackslash-SigSTP2025-Solutions\nBackground One of the coding clubs at NIT Patna, HackSlash recently conducted a induction where problems were given for joining the club. I guess the idea is that people who solved the most of the problems with the best approach would be selected in the club. There were different tasks for different team. Since I wanted to join the DSA Team (also called SigSTP team), I did these problems.\nProblems For people outside of NITP reading this blog, if you want to see the problems you can view a copy of the problem statements on the GitHub repository linked above:\nTask 1: ATM Machine Problem Statement Task 2: Task Manager Problem Statement Task 3: Trial of the Cheater\u0026rsquo;s Path Problem Statement In this blog, I\u0026rsquo;ll only be discussing about Task 3 as Task 1 and Task 2 are quite easy and standard problems.\nTask 1 Task 1 was a classical ATM vending machine problem or a coin change problem. It was just a matter of modulo and repeat, and check if we were able to dispense the money or not.\nYou can find my solution for the Task 1 here\nTask 2 Task 2 was a task manager implementation which was just a matter of using priority_queue. Since I choose to do the problems with C++, I could simply make use of std::priority_queue, but I decided not to as the problem also required me to print the queue when requested. It is not possible to get the internal queue in std::priority_queue, other implementations like that of the Boost library do allow this, but what\u0026rsquo;s the point of using a library when you can do it yourself and learn along.\nI ended up using std::vector to store the tasks which were sorted using std::less\u0026lt;\u0026gt;. This way I could insert tasks into the data structure with O(log n) time complexity and retrieve the top task with O(1) time complexity. And for getting the queue, I simply returned the internal std::vector which was already sorted.\nYou can find my solution for the Task 2 here\nTask-2/TaskManager.cc and Task-2/TaskManager.h contains the implementation for the queue/actual task manager.\nTask 3 Task 3 was a medium difficulty task, and the only one I found interesting enough. The puzzle wanted us to solve a maze such that each point had two options to navigate to, left (L) and right (R). The puzzle contained two pieces of information in the form of text files:\nInstructions: The directions to take each time. These directions are supposed to be repeated LLLRRRLRRLL.....\nThese instructions could simply be parsed with a switch statement and going over the file character by character.\nTask-3/parser.cc:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 auto parseInstruction(std::istream \u0026amp;inp) -\u0026gt; Instructions { Instructions directions = {}; for (char ch = inp.get(); !inp.eof(); ch = inp.get()) { switch (ch) { case \u0026#39;L\u0026#39;: directions.emplace_back(Instruction::Left); break; case \u0026#39;R\u0026#39;: directions.emplace_back(Instruction::Right); break; default: throw std::runtime_error(\u0026#34;Invalid character encountered in instruction file\u0026#34;); } } return directions; } Here Instruction is simply an enum class with two members Instruction::Left and Instruction::Right which are the two directions which can be specified in the instrutions.\nThe map: The other part was information about where the left and right directions take you from a particular position:\n1 2 3 4 PGQ = (QRB, MJB) JQC = (MNM, TLQ) HNP = (NKD, PJT) ... Noticing the pattern, we can store this as a std::map from std::string to struct { std::string Left, Right; }. Further to speed comparisions in future, we can convert 3-character strings to a number. Since 3-character strings containing letters A-Z have around 26*26*26 possibilities, which can fit into an int16_t.\n1 2 3 4 5 6 7 8 auto parseNode(std::string str) -\u0026gt; Node { Node node = 0; for (int i = 0; i \u0026lt; 3; i++) { node *= 26; node += (str[i] - \u0026#39;A\u0026#39;); } return node; } Similarly parseNode() can also be declared for a std::ifstream\nNow that we can parse nodes, we need to make sure that the file is parsing things correctly and holding proper syntax:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 auto parsePuzzle(std::istream \u0026amp;inp) -\u0026gt; Puzzle { Puzzle nodes; // This is intentionally an int instead of a char, as inp.get() will return an EOF which is supposed to be greater // than 255. int chr; while (true) { Node const node = parseNode(inp); Node left; Node right; chr = inp.get(); MAKESURE_ELSE_ERROR_PARSING(chr == \u0026#39; \u0026#39;); chr = inp.get(); MAKESURE_ELSE_ERROR_PARSING(chr == \u0026#39;=\u0026#39;); ... chr = inp.get(); NodeData const nodeData = { .left = left, .right = right, }; nodes.emplace(node, nodeData); if (inp.eof()) { break; } MAKESURE_ELSE_ERROR_PARSING(chr == \u0026#39;\\n\u0026#39;); } return nodes; } Here, MAKESURE_ELSE_ERROR_PARSING is just a helper macro that throws an exception if the file doesn\u0026rsquo;t match the expected format. Also Puzzle is just a typedef to std::unordered_map\u0026lt;Node, NodeData\u0026gt;. I used std::unordered_map instead of std::map as in unordered map, lookups have a time complexity of O(n)\nPart 1 We have to reach ZZZ starting from AAA. We can do this by simply following the instructions and counting the number of steps taken. steps % instructions.size() will give the number of instruction which we have to follow for that step. Then we can simply update the current node to the left or right node and repeat the process.\nFrom Task-3/partOne.cc\n1 2 3 4 5 6 7 8 9 10 11 uint64_t steps = 0; for (Node currentNode = parseNode(\u0026#34;AAA\u0026#34;); currentNode != parseNode(\u0026#34;ZZZ\u0026#34;); steps++) { switch (instructions[steps % instructions.size()]) { case Instruction::Left: currentNode = puzzle.at(currentNode).left; break; case Instruction::Right: currentNode = puzzle.at(currentNode).right; break; } } Part 2 We have to start from every node starting with the letter A and reach node starting with letter Z at the same time from all starting position. This is a simple LCM problem, we simply find the minimum steps from each such node and then find the LCM of all these steps.\nFrom Task-3/partTwo.cc\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // First find all the currentNodes that start with the letter \u0026#39;A\u0026#39; for (size_t i = 0; i != currentNodes.size(); i++) { auto node = currentNodes[i]; // This is similar to partOne(), we are just finding the step where the node ends with a \u0026#34;Z\u0026#34; for (; node % 26 != 25; steps[i]++) { switch (instructions[steps[i] % instructions.size()]) { case Instruction::Left: node = puzzle.at(node).left; break; case Instruction::Right: node = puzzle.at(node).right; break; } } ... // Return lcm of steps Building my solutions from source I have provided a CMakeLists.txt file in the root of the git repository. There are instructions on how to build from source in the README.md of the GitHub repository. Follow them to build from source after obtain the source using git:\n1 git clone https://github.com/thunder-coding/Hackslash-SigSTP2025-Solutions.git ","permalink":"https://thunders.website/posts/hackslash-sigstp-solution-to-the-trial-of-cheaters-path/","summary":"\u003cblockquote class=\"alert alert-type-note\"\u003e\n    \u003cdiv class=\"alert-heading-container\"\u003e\n      \u003csvg\n  xmlns=\"http://www.w3.org/2000/svg\"\n  viewBox=\"0 -960 960 960\"\n  class=\"alert-icon\"\n\u003e\n  \u003cpath\n    d=\"M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\"\n  /\u003e\n\u003c/svg\u003e\n\n      \n        \u003cp class=\"alert-heading\"\u003eNote\u003c/p\u003e\n      \n    \u003c/div\u003e\n    \u003cp\u003eIf you would like to see my final submitted code, you can find it on \u003ca href=\"https://github.com/thunder-coding/Hackslash-SigSTP2025-Solutions\"\u003ethunder-coding/Hackslash-SigSTP2025-Solutions\u003c/a\u003e\u003c/p\u003e\n  \u003c/blockquote\u003e\n\n\u003ch1 id=\"background\"\u003eBackground\u003c/h1\u003e\n\u003cp\u003eOne of the coding clubs at NIT Patna, HackSlash recently conducted a induction where problems were given for joining the club. I guess the idea is that people who solved the most of the problems with the best approach would be selected in the club. There were different tasks for different team. Since I wanted to join the DSA Team (also called SigSTP team), I did these problems.\u003c/p\u003e","title":"Solution to HackSlash SigSTP: Trial of Cheaters Path problem"},{"content":"Overview I often read a lot of the articles from sites like Hacker News, Lobste.rs, dev.to, some tech-related subreddits and a few others which I come across as a part of browsing the web. I love reading about what amazing things other people are doing, about how they are pushing the limits of what is possible, and how they solve their problems. As an aspiring engineer, I feel fascinated by technical writeups. Whenever I come across new blogs which I find interesting they often end up in my bookmarks. I have a few criteria which I use to decide if your site is worth bookmarking and revisiting.\nThe Criteria 1. Content Write something interesting, could be anything but share the technical details and don\u0026rsquo;t just write a PR nonsense. If I find too much PR nonsense on the site, it is very likely to be thrown away no matter how good the rest of the content is!\nAlso I like reading about emerging technologies, but if you are riding the hype train, I am not interested. I want to read about the real problems you faced and how you solved them, not about how you are using the latest and greatest tech which nobody else is. Show me how you built it, not how you\u0026rsquo;re using it.\n2. RSS Feed If you want me to come back to your site, have an RSS feed. I am not going to open up your site to find if there is something you. I let my RSS client take care of stuff I have read and left unread. If you don\u0026rsquo;t have an RSS feed, I am very likely not coming to your site unless it makes it to the top of Hacker News or any of the subreddits I follow (again using RSS). Also RSS feeds in my experience has also helped me filter out SEO crap, most sites that do have RSS feeds are either having it since a long time and are not just trying to game the SEO, or from hackers who love the simplicity of RSS. Your RSS doesn\u0026rsquo;t need to be complete, just the title and description is enough and I will open your blogs/writeups if I find them interesting enough.\n3. JavaScript, no thanks! I use NoScript Your site should be completely usable without JavaScript. No site content should be unavailable without JavaScript. I use NoScript and I am not going to enable JavaScript just to read your blog. If your site is not usable without JavaScript, I am not coming back in any way. I use NoScript as a security measure, and I am no way going to disable it anyway. If you are using third-party analytics service like Google Analytics or any other, your site is definitely going to be ranked down by me, but I may still visit it if the content is good enough.\n4. No Twitter/any other walled garden links I don\u0026rsquo;t want to visit sites like Twitter which are basically a walled garden now. If you discuss a lot about sites which don\u0026rsquo;t allow me to view content without me providing my personal details like email and phone number, then I will not be coming back to your site. I absolutely hate sites which don\u0026rsquo;t allow viewing their content anonymously and without loading a ton of JavaScript. If you are discussing about a tweet, please add a screenshot of it in your writeup as Nitter is dead now. (RIP Nitter)\n5. No Captcha just to read your mostly static site Your site should be completely usable without any captchas. I am totally find to use them to limit spam for comments, but having to solve a captcha just to read your site is a big no for me. Captchas are really inaccessible and a lot of people with special needs can\u0026rsquo;t solve them. This is the exact reason why I stopped reading phoronix.com, it requires me to solve a captcha. Although the site then loads without JavaScript, it sets a cookie in my browser which I am not comfortable with.\nThanks for reading my blog. By the way, my site supports RSS and should work perfectly fine without JavaScript (it is only needed for syntax highlighting in codeblocks), has no analytics (I have no idea how many visitors are there), and has no captchas.\n","permalink":"https://thunders.website/posts/how-i-decide-if-your-website-is-worth-a-revisit/","summary":"\u003ch1 id=\"overview\"\u003eOverview\u003c/h1\u003e\n\u003cp\u003eI often read a lot of the articles from sites like Hacker News, Lobste.rs, dev.to, some tech-related subreddits and a few others which I come across as a part of browsing the web. I love reading about what amazing things other people are doing, about how they are pushing the limits of what is possible, and how they solve their problems. As an aspiring engineer, I feel fascinated by technical writeups. Whenever I come across new blogs which I find interesting they often end up in my bookmarks. I have a few criteria which I use to decide if your site is worth bookmarking and revisiting.\u003c/p\u003e","title":"How I decide if your website is worth a revisit"},{"content":"Motivation Who doesn\u0026rsquo;t like to be cool on the internet and amongst other developer friends? Anyone? Everyone like to flex their skills. Isn\u0026rsquo;t it? It\u0026rsquo;s much more cool to listen 24x7 to Lofi on the terminal, whereas your friends may be stuck on ads every now and then. Besides looking cool, having a browser open for streaming music isn\u0026rsquo;t a good deal. Browsers are way too bloates especially if you\u0026rsquo;re low on resources.\nHacking the stream URL Before actually streaming CodeRadio from the terminal you need to have the stream URLs, which can be easily fetched if you know the correct place to look for. I started by downloading the raw HTML for the website using curl:\n1 curl https://coderadio.freecodecamp.org/ -Lo coderadio.html Upon investigating the XML, I found out that the site was most probably a React application. I quickly got the link to the main JS file loaded in by the HTML.\n1 bash https://coderadio.freecodecamp.org/static/js/min.5eb7bc98.js -Lo coderadio.js The JavaScript was highly obfuscated and minified so I prettified it using clang-format.\n1 clang-format -i coderadio.js Besides formatting the code with clang-format a part of it was still obfuscated, which was strange. I didn\u0026rsquo;t use something like Prettier as it would most probably have got Out Of Memory Killed by the kernel, and also Node.js is too slow in comparision to native. Running clang-format worked magically somehow\nNow the JavaScript was atleast readible. I looked for all sorts of URLs encoded in the JS, and finally got the websocket which gave the URLs of the streams and also the relay streams along with the list of songs in playlist:\n1 2 3 }(t.PureComponent), Oa = new (St())( \u0026#34;wss://coderadio-admin.freecodecamp.org/api/live/nowplaying/coderadio\u0026#34;), Now I just had to somehow try to conmect to the websocket. Fortunately for me, I was able to fetch the API with just HTTP(S), probably because initially websockets are initialed with http requests only.\nHere\u0026rsquo;s the raw JSON (stripped version):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 { \u0026#34;cache\u0026#34; : \u0026#34;event\u0026#34;, \u0026#34;is_online\u0026#34; : true, \u0026#34;listeners\u0026#34; : { \u0026#34;current\u0026#34; : 45, \u0026#34;total\u0026#34; : 45, \u0026#34;unique\u0026#34; : 45 }, \u0026#34;live\u0026#34; : { \u0026#34;broadcast_start\u0026#34; : null, \u0026#34;is_live\u0026#34; : false, \u0026#34;streamer_name\u0026#34; : \u0026#34;\u0026#34; }, \u0026#34;now_playing\u0026#34; : { \u0026#34;duration\u0026#34; : 179, \u0026#34;elapsed\u0026#34; : 115, \u0026#34;is_request\u0026#34; : false, \u0026#34;played_at\u0026#34; : 1663564407, \u0026#34;playlist\u0026#34; : \u0026#34;default\u0026#34;, \u0026#34;remaining\u0026#34; : 64, \u0026#34;sh_id\u0026#34; : 584950, \u0026#34;song\u0026#34; : { \u0026#34;album\u0026#34; : \u0026#34;Night Light\u0026#34;, \u0026#34;art\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/api/station/2/art/7681f2a146fcf71bf36b23cd-1586028052.jpg\u0026#34;, \u0026#34;artist\u0026#34; : \u0026#34;The Cancel\u0026#34;, \u0026#34;custom_fields\u0026#34; : [], \u0026#34;genre\u0026#34; : \u0026#34;\u0026#34;, \u0026#34;id\u0026#34; : \u0026#34;dad6680f4b224857d418c0812677c7b7\u0026#34;, \u0026#34;lyrics\u0026#34; : \u0026#34;\u0026#34;, \u0026#34;text\u0026#34; : \u0026#34;The Cancel - O.N.E.\u0026#34;, \u0026#34;title\u0026#34; : \u0026#34;O.N.E.\u0026#34; }, \u0026#34;streamer\u0026#34; : \u0026#34;\u0026#34; }, \u0026#34;playing_next\u0026#34; : { \u0026#34;cued_at\u0026#34; : 1663564534, \u0026#34;duration\u0026#34; : 110, \u0026#34;is_request\u0026#34; : false, \u0026#34;playlist\u0026#34; : \u0026#34;default\u0026#34;, \u0026#34;song\u0026#34; : { \u0026#34;album\u0026#34; : \u0026#34;Alone Journey\u0026#34;, \u0026#34;art\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/api/station/2/art/478f49a995584b4125696edd-1586028052.jpg\u0026#34;, \u0026#34;artist\u0026#34; : \u0026#34;Blazo\u0026#34;, \u0026#34;custom_fields\u0026#34; : [], \u0026#34;genre\u0026#34; : \u0026#34;\u0026#34;, \u0026#34;id\u0026#34; : \u0026#34;c828c83f107543f59abbfd76ac30ffa2\u0026#34;, \u0026#34;lyrics\u0026#34; : \u0026#34;\u0026#34;, \u0026#34;text\u0026#34; : \u0026#34;Blazo - Little Piano\u0026#34;, \u0026#34;title\u0026#34; : \u0026#34;Little Piano\u0026#34; } }, \u0026#34;song_history\u0026#34; : [ { \u0026#34;duration\u0026#34; : 328, \u0026#34;is_request\u0026#34; : false, \u0026#34;played_at\u0026#34; : 1663564082, \u0026#34;playlist\u0026#34; : \u0026#34;default\u0026#34;, \u0026#34;sh_id\u0026#34; : 584949, \u0026#34;song\u0026#34; : { \u0026#34;album\u0026#34; : \u0026#34;Love Journey\u0026#34;, \u0026#34;art\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/api/station/2/art/a060ebcb06756d64b0544725-1586028052.jpg\u0026#34;, \u0026#34;artist\u0026#34; : \u0026#34;Aso\u0026#34;, \u0026#34;custom_fields\u0026#34; : [], \u0026#34;genre\u0026#34; : \u0026#34;\u0026#34;, \u0026#34;id\u0026#34; : \u0026#34;f4ac57dcc1a126ce2fd55a12b90c482c\u0026#34;, \u0026#34;lyrics\u0026#34; : \u0026#34;\u0026#34;, \u0026#34;text\u0026#34; : \u0026#34;Aso - Summer Nights\u0026#34;, \u0026#34;title\u0026#34; : \u0026#34;Summer Nights\u0026#34; }, \u0026#34;streamer\u0026#34; : \u0026#34;\u0026#34; }, ... ], \u0026#34;station\u0026#34; : { \u0026#34;backend\u0026#34; : \u0026#34;liquidsoap\u0026#34;, \u0026#34;description\u0026#34; : \u0026#34;\u0026#34;, \u0026#34;frontend\u0026#34; : \u0026#34;icecast\u0026#34;, \u0026#34;id\u0026#34; : 2, \u0026#34;is_public\u0026#34; : true, \u0026#34;listen_url\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/radio/8010/radio.mp3\u0026#34;, \u0026#34;mounts\u0026#34; : [ { \u0026#34;bitrate\u0026#34; : 128, \u0026#34;format\u0026#34; : \u0026#34;mp3\u0026#34;, \u0026#34;id\u0026#34; : 2, \u0026#34;is_default\u0026#34; : true, \u0026#34;listeners\u0026#34; : { \u0026#34;current\u0026#34; : 6, \u0026#34;total\u0026#34; : 6, \u0026#34;unique\u0026#34; : 6 }, \u0026#34;name\u0026#34; : \u0026#34;128kbps MP3\u0026#34;, \u0026#34;path\u0026#34; : \u0026#34;/radio.mp3\u0026#34;, \u0026#34;url\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/radio/8010/radio.mp3\u0026#34; }, { \u0026#34;bitrate\u0026#34; : 64, \u0026#34;format\u0026#34; : \u0026#34;mp3\u0026#34;, \u0026#34;id\u0026#34; : 3, \u0026#34;is_default\u0026#34; : false, \u0026#34;listeners\u0026#34; : { \u0026#34;current\u0026#34; : 3, \u0026#34;total\u0026#34; : 3, \u0026#34;unique\u0026#34; : 3 }, \u0026#34;name\u0026#34; : \u0026#34;64kbps MP3\u0026#34;, \u0026#34;path\u0026#34; : \u0026#34;/low.mp3\u0026#34;, \u0026#34;url\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/radio/8010/low.mp3\u0026#34; } ], \u0026#34;name\u0026#34; : \u0026#34;freeCodeCamp.org Code Radio\u0026#34;, \u0026#34;playlist_m3u_url\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/public/coderadio/playlist.m3u\u0026#34;, \u0026#34;playlist_pls_url\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/public/coderadio/playlist.pls\u0026#34;, \u0026#34;public_player_url\u0026#34; : \u0026#34;https://coderadio-admin.freecodecamp.org/public/coderadio\u0026#34;, \u0026#34;remotes\u0026#34; : [ { \u0026#34;bitrate\u0026#34; : 128, \u0026#34;format\u0026#34; : \u0026#34;mp3\u0026#34;, \u0026#34;id\u0026#34; : 38063, \u0026#34;listeners\u0026#34; : { \u0026#34;current\u0026#34; : 7, \u0026#34;total\u0026#34; : 7, \u0026#34;unique\u0026#34; : 7 }, \u0026#34;name\u0026#34; : \u0026#34;128kbps MP3 (New York)\u0026#34;, \u0026#34;url\u0026#34; : \u0026#34;https://coderadio-relay-nyc.freecodecamp.org/radio/8010/radio.mp3\u0026#34; }, { \u0026#34;bitrate\u0026#34; : 64, \u0026#34;format\u0026#34; : \u0026#34;mp3\u0026#34;, \u0026#34;id\u0026#34; : 38064, \u0026#34;listeners\u0026#34; : { \u0026#34;current\u0026#34; : 10, \u0026#34;total\u0026#34; : 10, \u0026#34;unique\u0026#34; : 10 }, \u0026#34;name\u0026#34; : \u0026#34;64kbps MP3 (New York)\u0026#34;, \u0026#34;url\u0026#34; : \u0026#34;https://coderadio-relay-nyc.freecodecamp.org/radio/8010/low.mp3\u0026#34; }, ... ], \u0026#34;shortcode\u0026#34; : \u0026#34;coderadio\u0026#34;, \u0026#34;url\u0026#34; : \u0026#34;https://coderadio.freecodecamp.org\u0026#34; } } Now in order to stream from the terminal all you have to do is\n1 mpv https://coderadio-admin.freecodecamp.org/radio/8010/radio.mp3 Conclusion Besides the main URL, the API also returns relay stream URLs, so you can use them in case you face high latency.\nIf it\u0026rsquo;s online, everything will once be available on the terminal.\nEnjoy streaming music from your Terminal!\n","permalink":"https://thunders.website/posts/streaming-freecodecamps-coderadio-using-mpv/","summary":"\u003ch1 id=\"motivation\"\u003eMotivation\u003c/h1\u003e\n\u003cp\u003eWho doesn\u0026rsquo;t like to be cool on the internet and amongst other developer friends? Anyone? Everyone like to flex their skills. Isn\u0026rsquo;t it? It\u0026rsquo;s much more cool to listen 24x7 to Lofi on the terminal, whereas your friends may be stuck on ads every now and then. Besides looking cool, having a browser open for streaming music isn\u0026rsquo;t a good deal. Browsers are way too bloates especially if you\u0026rsquo;re low on resources.\u003c/p\u003e","title":"Streaming FreeCodeCamp's Coderadio using mpv"},{"content":"In my Hello World post, I described how I set up my site, from a simple Next.js application to a blogging site. But then I realised that I spent more time in actually getting it work than on writing some real stuff, so I started feeling that I need to change this. The best way I could do this was to switch to an Open Source blogging solution. I found Hugo extremely good, and with the PaperMod theme, it did wonders.\nI\u0026rsquo;ve also opened up the source code for this site at https://github.com/thunder-coding/CodingThunder so that all the people who want to peek into the source code can do so.\nAlso, my exams are about to end, so I hope to get back to contribute to Open Source as soon as possible\n","permalink":"https://thunders.website/posts/announcing-open-sourcing-of-my-blog/","summary":"\u003cp\u003eIn my \u003ca href=\"/posts/hello-world\"\u003eHello World\u003c/a\u003e post, I described how I set up my site, from a simple Next.js application to a blogging site. But then I realised that I spent more time in actually getting it work than on writing some real stuff, so I started feeling that I need to change this. The best way I could do this was to switch to an Open Source blogging solution. I found Hugo extremely good, and with the \u003ca href=\"https://github.com/adityatelange/hugo-PaperMod/\"\u003ePaperMod theme\u003c/a\u003e, it did wonders.\u003c/p\u003e","title":"Announcing Open Sourcing of my blog site"},{"content":" See https://thunder-coding.github.io/sponsor for an updated link including other cryptocurrencies and up to date info\nHey there, this is to announce that now I do accept sponsorships for my open source contributions and work using Bitcoin. I mostly contribute to Termux\u0026rsquo;s packaging work, porting packages and updating them. I also hope to start my own open source project soon and do some cool research.\nI hope that I continue my open source work!\n","permalink":"https://thunders.website/posts/now-you-can-sponsor-me-for-my-open-source-work-using-bitcoin/","summary":"\u003cblockquote class=\"alert alert-type-none\"\u003e\n    \u003cp\u003eSee \u003ca href=\"https://thunder-coding.github.io/sponsor\"\u003ehttps://thunder-coding.github.io/sponsor\u003c/a\u003e for an updated link including other cryptocurrencies and up to date info\u003c/p\u003e\n\n  \u003c/blockquote\u003e\n\n\u003cp\u003eHey there, this is to announce that now I do accept sponsorships for my open source contributions and work using Bitcoin. I mostly contribute to Termux\u0026rsquo;s packaging work, porting packages and updating them. I also hope to start my own open source project soon and do some cool research.\u003c/p\u003e\n\u003cp\u003eI hope that I continue my open source work!\u003c/p\u003e","title":"Now you can sponsor me for my open source work using Bitcoin"},{"content":" Note: this site has now moved to Hugo + PaperMod. This article should be considered absolute\nJust like how programmers write a hello world program first to check the functioning of their tools. In this blog, I will be testing all the features of my blog.\nAbout Me I am a student currently learning software development as a hobby with an aim to become a full time OSS contributor in the future.\nMy Skills Git GitHub Problem Solving Open Source So, if you have known enough about me let\u0026rsquo;s do what this blog was written for. So let\u0026rsquo;s test all features of the site.\nBlog Tests Code block test 1 2 3 function BlogPage(props: Props) { return \u0026lt;\u0026gt;...\u0026lt;/\u0026gt;; } Inline code blocks are cool too, aren\u0026rsquo;t they?\nHow this site was created\u0026hellip; The techstack This site is a React application. Don\u0026rsquo;t believe me? Oh I admit it\u0026rsquo;s not plain React 😁\nSo, the secret ingrediants are\u0026hellip; Oh wait are they really so secret? No. Anyways, here\u0026rsquo;s the list of ingrediants\nNext.js MDX (Markdown + React) TypeScript For MDX, I am currently using next-mdx-remote. Earlier I had almost set this site up with next-mdx-enhanced, but its unmaintained now, and had some problems with latest Webpack, so I decided to just move away.\nSo that\u0026rsquo;s all, that might not be so cool as other blogs but I assure you that I will add new features here soon 😉\n","permalink":"https://thunders.website/posts/hello-world/","summary":"\u003cblockquote class=\"alert alert-type-none\"\u003e\n    \u003cp\u003eNote: this site has now moved to Hugo + PaperMod. This article should be considered absolute\u003c/p\u003e\n\n  \u003c/blockquote\u003e\n\n\u003cp\u003eJust like how programmers write a hello world program first to check the functioning of their tools. In this blog, I will be testing all the features of my blog.\u003c/p\u003e\n\u003ch1 id=\"about-me\"\u003eAbout Me\u003c/h1\u003e\n\u003cp\u003eI am a student currently learning software development as a hobby with an aim to become a full time OSS contributor in the future.\u003c/p\u003e","title":"Hello World: Welcome to my blog"},{"content":"Open Source work by me Mostly I contribute to the Termux project both as a contributor and a maintainer reviewing PRs. For Termux, I maintain Node.js and also help in updating critical libraries to which a lot of packages make use of.\nWays to sponsor GitHub Sponsors I accept sponsors using GitHub Sponsors. You can pay by visiting my sponsors profile on GitHub\nCryptocurrencies Note that these addresses are static. In case you want private addresses for payment mail me at yakshbari4@gmail.com\nMonero/XMR XMR Address: 8ATnKUTz78ZVT6NbrorgCX5mMZ9H4FaPsVDHU5FPFTw9PoF4kbGd4736pFYUS74DU8BvBTzaRfQVJTWW5aMRHsTt4eAwN8A\nBitcoin BTC Address: bc1qqsk2wamk340n28fxxdm44hge080wd6vshm9249\nEthereum ETH Address: 0x17e39E09cd82C04b8a3232a556D754126569300F\nLitecoin LTC Address: ltc1q2g36hdwsjtfakpp5zntuykydw4c82f494kj7du\nSolana SOL Address: 8CL6nL2rxsULp238m4mdEHazczL6ekmJbTnwoF7F8RBA\n","permalink":"https://thunders.website/sponsor/","summary":"\u003ch2 id=\"open-source-work-by-me\"\u003eOpen Source work by me\u003c/h2\u003e\n\u003cp\u003eMostly I contribute to the \u003ca href=\"https://termux.org\"\u003eTermux project\u003c/a\u003e both as a contributor and a maintainer reviewing PRs. For Termux, I maintain \u003ca href=\"https://nodejs.org\"\u003eNode.js\u003c/a\u003e and also help in updating critical libraries to which a lot of packages make use of.\u003c/p\u003e\n\u003ch2 id=\"ways-to-sponsor\"\u003eWays to sponsor\u003c/h2\u003e\n\u003ch3 id=\"github-sponsors\"\u003eGitHub Sponsors\u003c/h3\u003e\n\u003cp\u003eI accept sponsors using GitHub Sponsors. You can pay by visiting my \u003ca href=\"https://github.com/sponsors/thunder-coding\"\u003esponsors profile on GitHub\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"cryptocurrencies\"\u003eCryptocurrencies\u003c/h3\u003e\n\n\n  \u003cblockquote class=\"alert alert-type-none\"\u003e\n    \u003cp\u003eNote that these addresses are static. In case you want private addresses for payment mail me at \u003ca href=\"mailto:yakshbari4@gmail.com\"\u003eyakshbari4@gmail.com\u003c/a\u003e\u003c/p\u003e","title":"Sponsor Me"}]