aboutsummaryrefslogtreecommitdiff
path: root/content/blog
diff options
context:
space:
mode:
authorArslaan Pathan <[email protected]>2026-07-28 13:19:38 +1200
committerArslaan Pathan <[email protected]>2026-07-28 13:19:38 +1200
commit838c1361b8da074937991ee0a057fb73d0122d29 (patch)
treeba60e226ee7f55411bc0cd4fa15f53587ae1fc60 /content/blog
parent29e11a34c1ebe2335398912bcc9643f5afc1288a (diff)
downloadarslaancodes-838c1361b8da074937991ee0a057fb73d0122d29.tar.xz
arslaancodes-838c1361b8da074937991ee0a057fb73d0122d29.zip
Finish it off!
Diffstat (limited to 'content/blog')
-rw-r--r--content/blog/browser-in-c.md143
-rw-r--r--content/blog/free-custom-email.md105
-rw-r--r--content/blog/free-custom-emails-improved.md101
-rw-r--r--content/blog/guide-self-hosting.md263
-rw-r--r--content/blog/linux-tv-box.md65
-rw-r--r--content/blog/python-091.md122
-rw-r--r--content/blog/systemd-age-verification.md57
-rw-r--r--content/blog/test.md17
-rw-r--r--content/blog/test2.md17
9 files changed, 856 insertions, 34 deletions
diff --git a/content/blog/browser-in-c.md b/content/blog/browser-in-c.md
new file mode 100644
index 0000000..5274254
--- /dev/null
+++ b/content/blog/browser-in-c.md
@@ -0,0 +1,143 @@
++++
+title = "Why I wrote my own browser in C (and why it still failed me)"
+date = 2026-04-20
+extra.image = "/assets/images/post-image/browser-in-c.png"
++++
+
+Chrome, Firefox, Edge, Brave, Safari, Opera.
+These are the most mainstream browsers in use today. Chances are, you probably use one of these browsers on your computer too.
+But what if you wanted to go *further?*
+
+## Vim-like browsers
+
+For people like me, who live in the terminal, normal browsing feels slow. You have to keep reaching for your mouse, clicking buttons, and the only thing close to keyboard-based navigation is "spam tab" and "scroll with the arrow keys".
+
+To combat this problem, there are a multitude of browsers with keybinds inspired by Vim, which, in short terms, is a terminal-based text editor focused around modal editing. You're never reaching for the mouse, because everything is simply a keybind. It takes a while to learn, but in the long run, it makes your workflow ten times faster.
+
+There are many Vim-like browsers, but the most popular one is **qutebrowser**.
+
+Written in Python, qutebrowser is easily configurable, and has support for most modern web features due to being based on QtWebEngine, which, in turn, is a wrapper around Chromium.
+
+## Random Access Memory (RAM)
+
+For almost a year, I was a long-time user of qutebrowser. Coming from normal Firefox, I loved how efficiently I was able to read documentation and navigate pages compared to your average browser, and, simply put, I never thought I'd use another browser again.
+
+Oh, was I so, so wrong.
+
+To put it simply, Chromium uses *RAM*. Lots, and lots of RAM. It'll easily use over 500MB+ *per tab* and take 500MB more for just the runtime.
+Because qutebrowser is based on Chromium, it has the same RAM issues as Chromium, except it's actually **MULTIPLIED**, because it has to run Qt, which is already quite heavy, and on top of that is written in Python, which adds tons of overhead.
+On my RAM-constrained system (8GB), I would see system slowdowns, lockups, and OOM kills on the daily, which, is not ideal for a browser that claims to "improve productivity".
+
+After a few months of this, I ended up getting sick of the constant lockups. A solution was to allocate more swap space, but that's not ideal in the long run: you still get tons of system slowdowns and other issues like SSD wear after utilising swap for too long.
+
+## The Search(TM)
+
+"Alright then," I thought.
+"I'll just search for another vim-like browser that doesn't use Chromium! It can't be *THAT* hard," I thought.
+
+I tried **EVERY SINGLE ONE**. Well, every one that I could find, but I'm pretty sure it's an exhaustive list.
+- Nyxt
+- vimb
+- wyeb
+- surf
+- Glide
+- Luakit
+- Tridactyl (an extension, I'll get to that)
+
+The problem was, coming from qutebrowser, which is on the more featureful side, they all had their own issues and quirks that made them break my workflow.
+
+I'll try to keep this short:
+
+- Nyxt? Too emacs-y, and also, I dislike Lisp's syntax
+- vimb? I'm not actually sure why I didn't use this one, there was some issue with compilation that made it not work properly or something along the lines of that
+- wyeb? I don't remember this one either
+- surf? Too minimal, I need at least tabs
+- Glide? This one was interesting, it's a fork of Firefox with Vim keybindings. I actually daily-drove it for a month, and it was a great browser, but after switching to Gentoo, it would keep freezing after 2 minutes of use and it kept consuming copious amounts of file descriptors. After a bit of troubleshooting, I just gave up on it.
+- Luakit? I had really tried to get this one to work. The concept was great: a lightweight browser, easily scriptable in Lua. But it had one missing feature: bookmark-style quickmarks. Luakit's quickmarks were single-letter, but I was looking for multi-character ones like in qutebrowser. To keep this short, I spent about 2 hours trying to make a Lua plugin that added one, but the docs were so bad I gave up.
+- Tridactyl? After trying it for just a little while, it just didn't work well enough for me. Browser extensions don't have as deep integration into the browser, so sometimes if things are focused wrong or if you are in the browser settings UI, it won't work, along with other quirks.
+
+## The Dilemma(TM)
+
+After all of that, I had 2 options:
+- Go back to Firefox
+- Make my own browser
+
+So obviously, I chose the sane and pragmatic one: go back to Fi-
+**NO!!!!!!!!!!!!!!!!!!!!! WEBKITGTK TIME!!!**
+
+all roads lead to writing code
+
+## cinnamon-browser
+
+In the end, I decided to make my own browser in C, powered by GTK3 and WebKit2GTK-4.1 (the simpler, less bloated API compared to GTK4 and WebKit2GTK-6.0). The source code is available [here](https://git.arslaancodes.com/cinnamon-browser.git/about/).
+
+It has support for Vim keybindings, link following, hint mode, and is configurable in C with a config.h. (yes, you compile your config, suckless style. Why you ask? less work for me, and it loads 2 seconds faster)
+The sky is the limit for customization, config.h has a user_start function that runs before gtk_main, and you can have keybinds/commands that just run C code.
+
+Some crazy ideas I've had:
+- A keybind/command to `git clone` the current URL and open it in Neovim
+- A keybind/command to run a userscript on the current URL from a file
+- A keybind/command to inject YouTube adblock userscripts on the current page
+- A command that downloads Wii homebrew of your choice by simply searching and saves then in `/mnt/wii`
+- And tons more...
+
+## The Downfall(TM)
+
+But, there's one issue with my custom browser:
+
+None of those features matter if the browser eats RAM just like Chrome does.
+You might ask, "but didn't you use WebKitGTK? WebKit is a lot lighter than Chromium!"
+
+And you're right---in theory.
+
+But in execution? WebKitGTK is a horrible piece of software. Horrible, horrible browser engine.
+
+While, yes, WebKitGTK has less RAM usage *on paper*, it leaks RAM. And when I say it leaks RAM, I mean it *REALLY* leaks RAM.
+
+This is WebKitGTK's RAM usage with 5 tabs just after launch. The only other apps I have open are a few terminal windows (Neovim, nchat, profanity), Mumble, and Thunderbird.
+```
+arslaan@m2-mbp-gentoo:~
+$ free -h
+ total used free shared buff/cache available
+Mem: 7.3Gi 5.9Gi 549Mi 3.1Gi 4.5Gi 1.4Gi
+Swap: 15Gi 0B 15Gi
+```
+Already not great. Accounting for overhead of around 1GB of shared RAM, WebKitGTK is already taking around 2GB in shared RAM.
+
+This is WebKitGTK RAM usage with the same tabs and same background apps after about an hour of use.
+```
+arslaan@m2-mbp-gentoo:~
+$ free -h
+ total used free shared buff/cache available
+Mem: 7.3Gi 6.1Gi 621Mi 3.7Gi 4.8Gi 1.2Gi
+Swap: 15Gi 1.7Gi 14Gi
+```
+This is on a GOOD day. Use the browser for 3-4 hours and you'll get swap usage ballooning to 3.5Gi+ and available memory going down to as low as 200Mi and less.
+I have to restart the browser many times per day, even though I shutdown after using my computer.
+
+## The final question: What browser do I use now?
+
+With all this trouble, you might ask the question: what browser do I use now?
+
+And the answer is: cinnamon.
+
+You might be confused, after watching me complain about RAM leaks for a while.
+
+But all the alternatives are worse. I mean, not really, technically Chromium browsers are more stable, even if they eat more RAM. Technically, Firefox is the only engine that has acceptable RAM usage and still doesn't leak. (Mozilla, if you see this, PLEASE make an embeddable version of Firefox's engine/Gecko. PLEASE!!! I know you discontinued it in 2015 but BRING IT BACK!!!! IT WILL SOLVE ALL MY PROBLEMS)
+
+But it's not *my* browser.
+That might sound like a stupid justification, and you know what, it might be.
+
+But no browser can reach that level of customizability and personality than one you made yourself.
+
+It's not perfect. I have to restart the browser at times. WebKitGTK leaks, it's sad to see a project with such potential be neglected. But I still like it, because I wrote it myself. The browser still works fine. I bet you that vimb, wyeb or any other WebKitGTK-based browser, like Epiphany, also have memory leaks. In fact, they do, I tested it.
+
+And honestly? Even with the leaks, just restarting the browser occasionally is still better than using Chromium on my 8GB RAM system.
+
+With how bloated and horrible the modern web is, the question isn't "Which browser works well on RAM-constrained systems?"
+The question is "Which browser works good enough for my use case?"
+And for me, that browser is cinnamon.
+
+---
+
+Thank you so much for reading my blog post! I'm a 13-year-old developer writing blog posts and making YouTube videos in my free time, so I'd really appreciate that you share this to others if you enjoyed it. Hopefully, I'll be coming out with a corresponding YouTube video for this blog post too, and when I do, it will be linked here.
diff --git a/content/blog/free-custom-email.md b/content/blog/free-custom-email.md
new file mode 100644
index 0000000..2c0dc14
--- /dev/null
+++ b/content/blog/free-custom-email.md
@@ -0,0 +1,105 @@
++++
+title = "A 12-year-old's guide to free custom emails"
+date = 2025-12-16
+extra.image = "/assets/images/post-image/free-custom-email.jpg"
++++
+
+Are you a small business looking for a custom email?
+A software developer wanting a more professional inbox?
+Or just a hobbyist flexing their custom email to friends?
+
+If so, this article is for **you!**
+
+I'm Arslaan Pathan, and as of 2025, I'm 12 years old.
+
+Today, you'll learn exactly how to make your business email **completely free** — no payment required!
+
+If a 12-year-old can do it, then you definitely can too.
+
+*(Note: This does require you to have a domain name. It might work with a shared subdomain from FreeDNS or DuckDNS, but your mileage may vary.)*
+
+This guide will be primarily focusing on Cloudflare Email Routing + Gmail SMTP.
+If you want to use another provider or different services, please refer to another article.
+
+## Step 1: Receiving emails
+
+The first step in creating a custom business email is receiving emails.
+This guide assumes you are using Cloudflare nameservers with your domain.
+If not, please follow [this guide.](https://developers.cloudflare.com/dns/zone-setups/full-setup/setup/){: target="_blank" }
+
+1. Open the Cloudflare dashboard and select your domain if you haven't already.
+
+2. On the left sidebar, click on `Email`, then `Email Routing`.
+
+3. It should bring you to a `Getting started with Email Routing` page.
+
+4. Enter the desired custom address in the `Custom address` field.
+
+5. Enter the desired destination email (in this case, your current email) in the `Destination` field.
+ It may take you through to another page that makes you verify the destination email, but simply follow the steps and you should be good to go.
+
+6. Press `Continue`.
+
+7. Make sure email routing is enabled and add other desired addresses if you would like.
+
+Done! You've setup email routing so you can receive emails in your normal Gmail inbox. It wasn't that hard, right?
+
+## Step 2: Sending emails
+
+Now comes the tricky part, sending emails. Don't leave just yet --- it isn't very hard, just a bit more complicated compared to receiving emails.
+
+### 1. Create a Google App Password
+
+First, you need to create a Google App Password so we can login to Gmail SMTP without being blocked by 2-Factor Authentication.
+
+To create an app password for Gmail, go to [this link](https://security.google.com/settings/security/apppasswords).
+
+Give it a memorable name, generate the app password, and store it in a safe place.
+
+### 2. Add your Cloudflare Email as an alias in Gmail
+
+In Gmail, press the Settings icon and click on `See All Settings`.
+
+From there, go to `Accounts and Import` -> `Add another email address`.
+
+In the first field that shows up, type your desired Cloudflare email address that you setup in the `Receiving emails` section.
+Check the `Treat as an alias` box, and press `Next`.
+
+It will now ask you to setup an SMTP server.
+Enter the following configuration:
+- **SMTP Server**: `smtp.gmail.com`
+- **Port**: `587`
+- **Username**: Your gmail username before the `@`. If your email is `[email protected]`, this will be `johnsmith`.
+- **Password**: The app password you generated earlier (not your Google account password!)
+- **Encryption**: Secured connection using TLS
+
+Once saved, you will receive a confirmation email in your inbox from Gmail. Press the link in the email to confirm your newly added email.
+
+## Step 3: Adding DNS records
+
+In your Cloudflare account, go to the DNS/Records page for your domain (located in the left sidebar).
+
+Add another record with the following values:
+- **Type**: `TXT`
+- **Name**: `@`
+- **TTL**: `auto`
+- **Content**: `v=spf1 include:_spf.mx.cloudflare.net include:_spf.google.com ~all`
+
+Finally, add one last record with the following values:
+- **Type**: `TXT`
+- **Name**: `_dmarc`
+- **TTL**: `auto`
+- **Content**: `v=DMARC1; p=none; rua=mailto:[email protected]`
+
+Make sure to replace [email protected] with your chosen email address.
+For multiple addresses, comma-separate them like this:
+
+## Enjoy!
+
+Good job! You've just set up your own custom email address with a custom domain for completely free!
+You'll be able to send about 500 emails a day, which is more than enough for most small business or individuals.
+
+It may take a couple days for all the DNS records to apply, so please be patient if your emails are being marked as spam or if emails aren't sending/receiving.
+
+Thanks for reading!
diff --git a/content/blog/free-custom-emails-improved.md b/content/blog/free-custom-emails-improved.md
new file mode 100644
index 0000000..39fe190
--- /dev/null
+++ b/content/blog/free-custom-emails-improved.md
@@ -0,0 +1,101 @@
++++
+title = "A 13-year-old's (improved) guide to free custom emails"
+date = 2026-02-20
+extra.image = "/assets/images/post-image/free-custom-emails-improved.png"
++++
+
+Are you a small business looking to get a free custom email?
+A software developer wanting a more professional inbox?
+Or a hobbyist just wanting to flex a custom email to their friends?
+
+If so, this article is for **you**!
+
+I'm Arslaan Pathan, and as of 2026, I'm 13 years old. This is an improved version of my [previous guide](/blog/free-custom-email/). If you've already set that up, I'd recommend switching --- I'll explain why in a second. It only takes 30 minutes to an hour, and it's much more secure than the previous method.
+If you're new and don't want to hear me yap about why the old method was broken, skip to the section titled "Getting Started".
+
+## Why the old method was fundamentally broken
+
+The previous method used Gmail SMTP to send the emails, which works, but it has a fundamental flaw that most tutorials don't tell you about.
+
+The problem is that Gmail SMTP is made to send emails only through a Google-managed email, for example, a normal Gmail account, or if you pay for Google Workspace (the fact that you're here trying to find free emails shows you probably don't), that account.
+
+When you send from the Gmail SMTP, SPF passes, because we place the correct records in our DNS, but DMARC still fails because of misalignment (and so does DKIM because DKIM is signed with Gmail's servers). Gmail SMTP will always, no matter what, set the Envelope-From to your Gmail address in the email headers, even if you have a different From address. This makes the DMARC alignment fail, which in turn causes DMARC itself to fail.
+
+The only reason our emails were still even sending was that we had 'p=none' set in our DMARC TXT records, which tells the mail servers to accept our emails even if checks fail. This makes it extremely easy for an attacker to spoof our emails, because our DMARC records are set to accept everything. Setting it to 'p=quarantine' or 'p=reject' with the old method was impossible, because that would make our check-failing legitimate emails undeliverable.
+
+The new method uses Resend SMTP to send emails, which is a mail-sending service that has a free tier for developers and hobbyists. Resend explicitly supports custom domains and passes all of the SPF/DKIM/DMARC checks, avoiding our issues from earlier.
+
+## Getting Started
+
+### Setting up Cloudflare Email Routing
+
+The first step in creating a custom business email is receiving emails. This guide assumes you are using Cloudflare nameservers with your domain.
+If not, please follow [this guide.](https://developers.cloudflare.com/dns/zone-setups/full-setup/setup/){: target="_blank" }
+
+1. Open the Cloudflare dashboard and select your domain if you haven't already.
+
+2. On the left sidebar, click on `Email`, then `Email Routing`.
+
+3. It should bring you to a `Getting started with Email Routing` page.
+
+4. Enter the desired custom address in the `Custom address` field.
+
+5. Enter the desired destination email (in this case, your current email) in the `Destination` field.
+ It may take you through to another page that makes you verify the destination email, but simply follow the steps and you should be good to go.
+
+6. Press `Continue`.
+
+7. Make sure email routing is enabled and add other desired addresses if you would like.
+
+Done! You've setup email routing so you can receive emails in your normal email inbox. It wasn't that hard, right?
+
+### Setting up Resend SMTP
+
+This method of getting free custom emails uses Resend SMTP for sending emails. Resend is an email API made for developers and hobbyists alike, which also happens to support sending via SMTP.
+
+1. Go to `resend.com` and create a new account.
+
+2. It will show you the onboarding process. It's fairly straightforward, just follow the instructions to create an API key and send a test email and you should be all set. Remember to save the API key somewhere safe, you'll need it.
+
+3. From the Resend dashboard, go to Domains and add your domain. It will determine your DNS provider (in this case, Cloudflare) and ask if you want to use automatic setup. In the case that it doesn't, it will tell you exactly which DNS records to add. Simply follow the instructions and you should be up and running soon enough.
+
+4. In your preferred email client, add another identity for the email you'd like using the following details:
+ Host/Server: `smtp.resend.com`
+ Port: `587`
+ Username: `resend`
+ Password: `<YOUR_API_KEY>`
+
+ This process varies by email client, but it shouldn't be that hard. Just search up "how to add another identity in <Gmail/Thunderbird/your email client>" and you should get some results.
+
+There you go! You've just setup Resend SMTP with your custom email address. Just one last thing...
+
+### Adding DMARC records
+
+Almost done! The last step is to add our DMARC records which tell mail servers what to do if our SPF/DKIM checks fail and act as a layer of security for our emails.
+
+1. In the Cloudflare dashboard, go to your domain and then DNS.
+
+2. Add the following DNS record:
+ Type: `TXT`
+ Name: `_dmarc`
+ TTL: `Auto`
+ Content: `v=DMARC1; p=reject; rua=mailto:[email protected]`
+
+ If you want to add multiple emails, comma separate them in the rua field:
+ For example: `v=DMARC1; p=reject; rua=mailto:[email protected],mailto:[email protected]`
+
+ You can also omit the rua field entirely, as it just tells email servers to send you aggregate reports of your emails so you can see if your SPF/DKIM/DMARC are passing or failing on average.
+ For example: `v=DMARC1; p=reject`
+
+## Enjoy!
+
+Great job! You've just setup your own custom email address for completely free! You'll be able to send 100 emails per day, and a total of 3,000 emails per month, which is plenty for most people.
+
+If your emails aren't sending or are being marked as spam, the DNS records probably haven't propagated yet. Just wait a few days and you'll be fine.
+
+---
+
+Thanks for reading my blog post!
+I'm a 13-year-old developer making niche tech content here on my website and also on YouTube, so I really appreciate the support!
+I'll (probably, if I get time) be coming out with a YouTube video corresponding to this blog post soon, and this page will be updated to show that when the time comes.
+Feel free to [subscribe to my YouTube channel](https://youtube.com/@DevWithArslaan) and follow me on other socials!
diff --git a/content/blog/guide-self-hosting.md b/content/blog/guide-self-hosting.md
new file mode 100644
index 0000000..664080b
--- /dev/null
+++ b/content/blog/guide-self-hosting.md
@@ -0,0 +1,263 @@
++++
+title = "A 12-year-old's guide to self-hosting"
+date = 2025-11-05
+extra.image = "/assets/images/post-image/guide-self-hosting.jpg"
++++
+
+Have you ever wanted to self-host your own homelab but didn't know how?
+
+Let me show you how I, a 12-year-old, built my own setup --- and how you can too.
+
+### What even *is* self hosting?
+
+Self-hosting is when you run services on a computer at home that you control instead of using other people/organization's servers such as Google Drive or Discord.
+
+You can use an old laptop, a [Raspberry Pi](https://raspberrypi.com/) (like me), or even a PC to run things like:
+- A website (like this one, which also happens to be self-hosted!)
+- A file server for sharing stuff at home --- good if you have an old SSD or hard drive laying around!
+- A Minecraft server for you and your friends
+- A media server to stream movies, TV shows, and Live TV (as long as you have the media)
+- A YouTube downloader
+- And tons more!
+
+### Why self-host?
+
+The main reasons you'd want to self-host is because:
+- You want to learn Linux, networking, and sysadmin
+- You want full control over your data
+- You don't want Google/other companies tracking you (e.g. self-hosting your own Google Drive alternative to stay away from Google)
+- You can make something *yours* --- no subscriptions, no ads
+
+### What do you need?
+
+- A computer (even an old laptop or [Raspberry Pi](https://raspberrypi.com/) will work fine)
+- An operating system --- preferably Ubuntu for compatibility (or Pi OS if you are using a Pi)
+- A network connection (preferably Ethernet, but Wi-Fi will work fine too)
+- Access to your router settings (ideally, if you want to make things public)
+
+## Getting started
+
+**Warning**: This guide assumes you have some knowledge of computer networking and shell commands.
+If you don't, I recommend learning how to interact with SSH and the Linux terminal before reading this guide.
+
+Alright, enough blabbing --- here comes the actual setup.
+
+### Installing an OS
+
+The first step is to install an operating system on your computer of choice.
+
+If you are using a Raspberry Pi, follow instructions [here](https://www.raspberrypi.com/documentation/computers/getting-started.html#installing-the-operating-system) to install an OS. Make sure to install Raspberry Pi OS **Lite** if you don't plan to use any GUI apps --- which for this setup, we won't.
+
+If you’re **not** using a Raspberry Pi, you can install [Ubuntu Server](https://ubuntu.com/download/server) on your old laptop or PC.
+Just download the ISO, then flash it to a USB stick using [Rufus](https://rufus.ie) (on Windows) or [balenaEtcher](https://etcher.balena.io) (on macOS/Linux) and follow the setup prompts.
+
+After installing your OS, remember to run the following command to update all packages:
+
+`sudo apt update -y && sudo apt upgrade -y`
+
+### Installing Docker (and Portainer for easy management)
+
+*Technically*, this step is optional - but it's very important and almost required to follow this tutorial.
+
+Docker is a piece of software that allows you to place different services in containers/sandboxes. This allows you to easily spin up services and keep them isolated from others, protecting your system installation and providing another layer of security.
+
+If you are using a Raspberry Pi, I recommend following the instructions from the [pi-hosted GitHub repo](https://github.com/pi-hosted/pi-hosted) to get Docker and Portainer up and running with specialized app templates for Pi devices. This script *may* work on other non-Pi devices, but your mileage may vary and it is not recommended.
+
+(**Disclaimer**: The repository mentioned above, pi-hosted, is not affiliated nor endorsed by the Raspberry Pi Foundation.)
+
+If you're **not** using a Raspberry Pi, you can install Docker and Portainer quickly with the following guide(s).
+
+#### Install Docker on non-Pi devices
+
+**Note**: This guide and the following do work for Raspberry Pi devices, but it is easier to use the script from the repo above to get up and running quicker.
+
+1. Refresh the package list:
+
+ ```shell
+ sudo apt update
+ ```
+
+2. Install the necessary packages:
+
+ ```shell
+ sudo apt install ca-certificates curl
+ ```
+
+ When prompted, press Y and Enter to begin the installation.
+
+3. Set the necessary permissions for the */etc/apt/keyrings* directory:
+
+ ```shell
+ sudo install -m 0755 -d /etc/apt/keyrings
+ ```
+
+4. Use the *curl* command to download the GPG key:
+
+ ```shell
+ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
+ ```
+
+5. Change the permissions of the key file:
+
+ ```shell
+ sudo chmod a+r /etc/apt/keyrings/docker.asc
+ ```
+
+6. Add the Docker repo to the APT sources list:
+
+ ```shell
+ echo \
+ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
+ $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
+ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+ ```
+
+7. Refresh the package list:
+
+ ```shell
+ sudo apt update
+ ```
+
+8. Install Docker packages
+
+ ```shell
+ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-buildx-plugin -y
+ ```
+
+9. Create the `docker` group for non-root user access:
+
+ ```shell
+ sudo groupadd -f docker
+ ```
+
+10. Add the current user to the docker group:
+
+ ```shell
+ sudo usermod -aG docker $USER
+ ```
+
+ To add a user other than the current user, add their username instead of $USER like so:
+ ```shell
+ sudo usermod -aG docker [username]
+ ```
+
+11. Apply the new group membership changes:
+
+ ```shell
+ newgrp docker
+ ```
+
+12. Reboot your device to ensure all changes have been applied:
+
+ ```shell
+ sudo reboot
+ ```
+
+#### Install Portainer on non-Pi devices
+
+Next, we need to install Portainer.
+Don't worry, this will be much less complicated than the docker install.
+
+1. Create the Docker volume for Portainer's data:
+```shell
+docker volume create portainer_data
+```
+
+
+2. Fetch and start the Portainer Server container:
+```shell
+docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:lts
+```
+
+Portainer has now been installed. You can check to see whether it has started by running the following command:
+
+```shell
+docker ps
+```
+
+The output should look somewhat like the following:
+
+```
+root@server:~# docker ps
+CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
+7963585688a9 portainer/portainer-ce:lts "/portainer" 8 seconds ago Up 8 seconds 0.0.0.0:8000->8000/tcp, [::]:8000->8000/tcp, 0.0.0.0:9443->9443/tcp, [::]:9443->9443/tcp, 9000/tcp portainer
+```
+
+If the container's status is marked as "Up", congrats! Docker and Portainer are both up and running!
+
+You can now access the dashboard & admin console at `https://<your-server-ip>:9443`.
+
+## First apps to try
+
+Now that you've got Docker and Portainer installed, you're ready to start installing services/apps on your new home server.
+
+Here's a list of the first apps you should install:
+
+1. [Pi-hole](https://pi-hole.net/)
+
+ An ad blocker that blocks ads on the network-level, before they even reach your device.
+ (**Note**: This does not block YouTube ads.)
+
+ Installation instructions: [https://docs.pi-hole.net/docker/](https://docs.pi-hole.net/docker/)
+
+2. [docker-minecraft-server](https://github.com/itzg/docker-minecraft-server)
+
+ A docker container that contains an easy configurable Minecraft Server with full support for Fabric/Forge/Paper and other server versions. Great for hosting a small server with friends.
+ (**Note**: It is recommended to use this with an official Pi 5 Active Cooler if using a Pi to make sure it doesn't get too hot.)
+
+ Installation instructions: [https://docker-minecraft-server.readthedocs.io/en/latest/](https://docker-minecraft-server.readthedocs.io/en/latest/)
+
+3. [MeTube](https://github.com/alexta69/metube)
+
+ A simple YouTube downloader webapp based on yt-dlp.
+
+ Installation instructions: [https://github.com/alexta69/metube](https://github.com/alexta69/metube)
+
+4. [wg-easy](https://github.com/wg-easy/wg-easy)
+
+ An easy way to get a WireGuard VPN/tunnel up and running. Useful to access your home network/local services when away from home.
+
+ Installation instructions: [https://wg-easy.github.io/wg-easy/latest/getting-started/](https://wg-easy.github.io/wg-easy/latest/getting-started/)
+
+5. [Caddy](https://caddyserver.com/)
+
+ A simple server/reverse proxy for hosting web apps, sites, and much more. Comes with HTTPS support (via Let's Encrypt) out of the box, making it easier to get up and running fast.
+
+ Installation instructions: [https://caddyserver.com/docs/install](https://caddyserver.com/docs/install)
+
+6. [Navidrome](https://www.navidrome.org/)
+
+ A lightweight music streaming service for your personal media collection, kind of like a Spotify alternative.
+
+ Installation instructions: [https://www.navidrome.org/docs/installation/docker/](https://www.navidrome.org/docs/installation/docker/)
+
+7. [Plex](https://plex.tv)
+
+ A media streaming service that allows you to stream your own collection of videos, music, and more content.
+
+ Installation instructions: [https://github.com/plexinc/pms-docker](https://github.com/plexinc/pms-docker)
+
+8. [Jellyfin](https://jellyfin.org/)
+
+ An open-source media streaming service that allows you to stream your own collection of videos, music, and more content.
+
+ Installation instructions: [https://jellyfin.org/docs/general/installation/container](https://jellyfin.org/docs/general/installation/container)
+
+9. [Code-Server](https://github.com/coder/code-server)
+
+ A server that allows you to access a full VS Code development environment in your browser. Useful for coding on-the-go, especially when paired with a WireGuard VPN.
+
+ Installation instructions: [https://coder.com/docs/code-server/install#docker](https://coder.com/docs/code-server/install#docker)
+
+## Wrapping up
+
+Finally, you're done setting up your home server!
+It wasn't as hard as you thought, right?
+
+Now that you've got a home server up and running, experiment with it!
+Have a try installing more apps, or setting up more advanced containers!
+
+There are so many more apps, containers, and other things to explore on your home server beyond what I've mentioned here.
+Just give it a try!
+
+Happy self-hosting!
diff --git a/content/blog/linux-tv-box.md b/content/blog/linux-tv-box.md
new file mode 100644
index 0000000..bb899e3
--- /dev/null
+++ b/content/blog/linux-tv-box.md
@@ -0,0 +1,65 @@
++++
+title = "Linux on a TV Box—Raspberry Pi Alternative?"
+date = 2026-01-14
+extra.image = "/assets/images/post-image/linux-tv-box.png"
++++
+
+Raspberry Pis are quite popular—and for good reason.
+They have quite a lot to offer, from running home servers and retro gaming consoles to making mini robotics projects in your bedroom. They’re compact, affordable, and quite powerful, making them great for computer enthusiasts, like you (hopefully) and me.
+
+But recently, due to the RAM shortages and other supply chain issues, the prices for Raspberry Pis have gone up quite a bit. A Raspberry Pi 5 with 16GB RAM now costs $300 New Zealand dollars, over $100 dollars more than it’s previous price.
+
+Even with the price increases, it’s been hard to get a Pi at MSRP in some countries nowadays.
+
+And that’s where the TV box from AliExpress comes in!
+
+## The Chronicles of AliExpress: Prince T95
+
+When you first look at one of these TV boxes, it may just look like another streaming device---but under the hood, it’s still a somewhat capable computer! Although some can ship with questionable firmware, and usually have bootloaders that make you want to bang your head against the wall, these boxes still have a decent processor, a mediocre but still functional amount of RAM, and a good amount of I/O and ports, which makes them *seemingly* quite suitable to act as a Raspberry Pi alternative.
+
+The best part? These boxes are dirt cheap on AliExpress! I can purchase one right now for about $17.70 from AliExpress here in NZ, which is about $10.16 in US dollars. Way cheaper than a even a Raspberry Pi Zero.
+
+So, that’s it, right? We’ve found a cheaper alternative to Raspberry Pi products?
+Don’t get your hopes up, because as we know, if it’s too good to be true, then it’s… too good to be true. Yes.
+
+So what’s the catch with these TV boxes, you may ask?
+
+## "Mum, I downloaded malware off LimeWire again"
+
+CopyCat malware was found preinstalled on hundreds of these boxes commonly sold on AliExpress and Amazon. That alone should deter you from purchasing one of these and even coming close to plugging one of them into your network.
+
+"But aren't we going to install Linux on them anyway? The malware wouldn't survive a clean Linux install!"
+Good question.
+
+## Five Nights at Allwinner's
+
+While technically, yes, you can install Linux on these boxes, that doesn’t mean it’s going to be easy. Most of the time, buying one of these boxes is a CPU roulette. You’ll buy a TV box off of AliExpress that claims it has a Rockchip SoC, but instead you’ll open it up and find an Amlogic SoC instead, or even worse, an *Allwinner*.
+
+“But once you find the CPU model it’s not that hard, right?”
+Don’t get your hopes up yet, for there is a lot more to come.
+
+## Nightmare on DTB Street
+
+Figuring out how to get Linux to boot in the first place is not the best experience. You’ll find yourself in a rabbit hole of questionable tools, weird USB-A to USB-A cables, and flashing random SD card images until the bootloader decides it wants to have mercy on you.
+Some boxes will only boot custom images if tilted just at the right angle, or if it’s at specifically 11:23am on a Tuesday. Of course I’m exaggerating here, but it really does matter. I’ve had a TV box refuse to boot from eMMC unless I tilted it towards the corner of the room while it was booting and plugged into a specific power supply.
+
+There have been countless times that I’ve got something to boot on the TV box, ran to my mum shouting “MUM IT WORKED!!!!” and then came back to see it refuse to boot again.
+
+And when you think that you’ve finally got it to boot, you realize that the Wi-Fi doesn’t work, the USB ports only work on Thursdays, and that drivers and documentation are either non-existent or all in Chinese.
+
+## Conclusion
+
+By the end of the process, you’ll realize that this $10 bargain cost you several hours wasted, your sanity, and your patience. Working with these TV boxes, ESPECIALLY ones that use an Allwinner SoC, are the kind of project that teach you more about patience than actual computing work.
+
+So, do they work? Technically, yes.
+Are they worth it? No. Never again. We don’t talk about my experiences with the “Trivision TV Box”.
+
+---
+
+Thanks for reading my blog post!
+I'm a 12-year-old developer making niche tech content here on my website and also on YouTube, so I really appreciate the support!
+Feel free to watch the YouTube video linked below if you prefer to watch content in video format, and remember to follow me on all my socials!
+
+<div class="video-container">
+ <iframe src="https://www.youtube.com/embed/h3BHi7Ohl1M" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
+</div>
diff --git a/content/blog/python-091.md b/content/blog/python-091.md
new file mode 100644
index 0000000..de1ff6a
--- /dev/null
+++ b/content/blog/python-091.md
@@ -0,0 +1,122 @@
++++
+title = "Python 0.9.1 in 2026: The minimal scripting language that feels like a better Bash"
+date = 2026-01-21
+extra.image = "/assets/images/post-image/python-091.png"
++++
+
+In the modern world of programming, Python is almost unavoidable.
+No, not the snake. The programming language.
+You'll find it everywhere, and I mean everywhere.
+
+AI applications? They use Python.
+Your computer? It uses Python.
+Robotics? They use Python.
+Embedded systems? They use Python.
+Your microwave? Probably uses Python.
+That random Wii homebrew toolchain from 2010? It uses---
+Okay, you get it.
+
+My point is, that over the years, Python has become an increasingly popular language.
+But what if we went back to a time when Python wasn't so popular?
+A time when Python was still in beta?
+
+Today I present to you, Python 0.9.1!
+
+## A blast from the past
+
+Python 0.9.1 is a vintage programming language by today's standards.
+Released in early 1991, it's almost 35 years old!
+Do you know what **else** released in 1991?
+That's right, the freaking **WORLD WIDE WEB**.
+Python 0.9.1 is older than the internet as we know it!
+
+With this version of the programming language being that old,
+you might think it doesn't have any practical purposes or that it's just a gimmick in today's modern world.
+
+But what if I told you that you're wrong?
+
+## The experiment begins
+
+The other day, when I was reading about retro computing, I thought it would be a fun idea to try out Python 1.0!
+But when I went to the Python releases page, I saw that there was a "pre-1.0/Ancient Releases" section. Out of curiosity, I clicked on it, to see what I would find.
+Once I saw that they had found an archived version of Python 0.9.1, I knew that I had to try it.
+
+They said they had a pre-built version on conda, so I tried using that first.
+Though, unsurprisingly, it said it wasn't available (probably because I'm on Linux ARM64 and not x86).
+
+Therefore, I decided I'll just compile it myself!
+After cloning the [GitHub repo for Python 0.9.1](https://github.com/smontanaro/python-0.9.1){: target="_blank" } and compiling it (tip: if you want to compile it yourself, use make with CFLAGS="-std=c89"), I ran the outputted ./python binary and tried a couple basic things, like declaring variables and printing them out.
+
+After a bit of tinkering and testing some things, I was immediately fascinated by the language design and how featureful this language was, even in 1991.
+
+## Surprisingly useful scripting?
+
+Despite being 35 years old, Python 0.9.1 is quite... useful?
+
+The entire interpreter is **only 288KB** when statically compiled with musl-gcc and stripped! You could probably get it down even more with some optimizations, but I won't cover those in this article.
+Even better, the standard library is **only 364KB**!
+The total footprint? A little over 652KB or **just over half a megabyte** for a complete scripting language with classes and OOP.
+
+Compare that to modern Python installations with 100-300MB, and you'll understand why this feels refreshing.
+Also, did I mention that it compiles in mere seconds?
+
+The syntax is quite familiar to anyone who's worked with Python before, which is most if not all developers.
+
+For a pre-1.0 release of the language, it's got quite a few features!
+- Classes and OOP
+- Exception handling
+- Modules and imports
+- range() from modern Python
+- Run commands and capture their output/status codes
+- and more untested!
+
+It feels a lot like Python but... before it became bloated!
+Guido really had the fundamentals right from the start.
+I could totally see this language being used as a Bash alternative for scripting, or maybe in some embedded systems/IoT applications!
+
+## Debugging a 35-year-old bug
+
+While experimenting with Python 0.9.1, I noticed that the random number generator (whrandom) wasn't working! After looking at the code of `whrandom.py`, I had a theory.
+"Hmmm, maybe the epoch timestamp seed from 2026 is too large?"
+
+So, that's it, right? Just seed the random number generator with your own values?
+It turned out, no, seeding it with smaller values didn't work either!
+
+So I looked at what was failing again, and saw that it was the multiplication.
+I tried to reproduce the steps myself, and BOOM, it overflowed!
+
+"Why would 171 * 21 be failing? That shouldn't be overflowing at all!" I thought to myself.
+As a joke, I tried 1 * 1 to see if it would fail, and to my surprise, it did!
+
+That's when I realised something fishy was going on. Apparently, all multiplication was causing an integer overflow!
+
+So, I spent some time searching through the codebase and found the function that handles multiplication: `int_mul` from `intobject.c`. Now all I needed to do was find the issue. Luckily, the function was only a couple lines, and the issue was quite apparent.
+
+One of the conditions that checks if the multiplication result overflowed was checking if the integer was less than 0x80000000. On a 32-bit system, this would be correct: it's checking if the product was less than -2147483648.
+But on a 64-bit system, suddenly, bit 31 is no longer the sign bit (the sign bit is now bit 63) and that 0x80000000 gets padded to 0x0000000080000000, which is **positive 2147483648**. This would make the check "if the result is less than 2147483648, then crash because we think it overflowed."
+
+As you can see, this is not ideal. To fix it, I patched the code to use the hardcoded numbers 2147483647 and -2147483648 in the overflow checks instead of hex, solving our overflow issue.
+
+After patching, the code ran! Multiplication worked fine, and the random number generator was also working!
+
+## Conclusion
+
+Python 0.9.1 is surprisingly quite a useful language and could possibly have some real-world applications! It seems well fit for a Bash/shell scripting alternative, or some embedded systems applications where you need a simple language with minimal footprint.
+
+Although there were some hiccups with multiplication along the way, I have made a [patched version](https://github.com/RealArslaanYT/python-0.9.1-patched-QoL){: target="_blank" } that fixes this issue and a couple more, and also adds some QoL improvements that make it easy to get up and running on any modern system.
+
+The improvements/fixes are as follows:
+
+- Fixed int_mul integer overflow bug in src/intobject.c on 64-bit systems
+- Added -std=c89 to CFLAGS in Makefile for compatibility with GCC 15+
+- Added a `make install` target that installs to /opt/python091 by default (python is at /opt/python091/bin/py091), this can be changed with the INSTALL_DIR env variable.
+- Fixed PYTHONPATH to be relative to the INSTALL_DIR used in `make install` target
+
+More information about how to get up and running with Python 0.9.1 is in the `README.md` of the GitHub repo for the patched version linked above. Feel free to open any GitHub issues or contact me if you have any further questions.
+
+---
+
+Thanks for reading my blog post!
+I'm a 12-year-old developer making niche tech content here on my website and also on YouTube, so I really appreciate the support!
+I'll be coming out with a YouTube video corresponding to this blog post soon, and this page will be updated to show that when the time comes.
+Feel free to [subscribe to my YouTube channel](https://youtube.com/@DevWithArslaan) and follow me on other socials!
diff --git a/content/blog/systemd-age-verification.md b/content/blog/systemd-age-verification.md
new file mode 100644
index 0000000..db6c33f
--- /dev/null
+++ b/content/blog/systemd-age-verification.md
@@ -0,0 +1,57 @@
++++
+title = "Age verification in Systemd"
+date = 2026-03-23
+extra.image = "/assets/images/post-image/systemd-age-verification.png"
++++
+
+Recently, the systemd project has merged Pull Request #40954, introducing a `birthDate` field to the userdb JSON.
+You might be asking, "Why is this bad?" or "What does this mean?", and as there is a lot of misinformation out there, it may be hard to get a clear answer on this topic.
+
+This blog post aims to explain it clearly in words even a non-techie person can understand.
+
+## The issue
+
+To give some context for the non-techies, systemd is a core part of most Linux systems. It manages most of the apps that run in the background that are needed for your computer to work. While there are alternatives to systemd, almost all distros use it, and once it's on your computer (it usually comes preinstalled), it's incredibly difficult to take its "digital tentacles" out of your system.
+
+Recently, systemd merged a pull request ([#40954](https://github.com/systemd/systemd/pull/40954){:target="_blank"}) to add a `birthDate` field to userdb from systemd-homed.
+In simple terms, systemd added support for tracking the user's birth date inside of the users database, in response to the new laws requiring operating systems to verify the ages of users in California (AB-1043), Brazil (Lei 15.211), and Colorado (SB26-051).
+
+## Why this is controversial
+
+This pull request being merged has caused much controversy in the FOSS community as of recent times. Many people believe that this violates the "free" and "libre" nature of Linux, and that there was no need for systemd to do this.
+
+Although the systemd project claims it is an 'optional field' and that it will 'not be required for use', it's starting to become a lot less optional than we thought.
+GNOME, a popular desktop environment for Linux (in simple terms, the desktop of your computer) is starting to rely a lot more on the new `birthDate` field to the point that it might be required to use GNOME.
+Along with this, xdg-desktop-portal (in simple terms, the file picker/screenshare manager) is working on adding an age verification portal that integrates directly with the new systemd `birthDate` field.
+
+The more insane part of this actually comes from where the PR (pull request. simple definition: a snippet/addition of code written by someone else requested to be added to the mainstream codebase) came from. The creator of the PR, `dylanmtaylor`, stated himself that this field was, and I quote, "hilariously pointless and ineffective" at verifying anyone's age, then continued to push for it to be merged into the codebase anyway. Even more ironic? The people who merged the PR into the mainstream codebase were engineers at Microsoft. Microsoft, of all companies. The company that makes the piece of spyware we call "Windows 11".
+
+## My take
+
+I'll be honest for a minute.
+
+Yes, it's an 'optional' field. And yes, systemd didn't write the laws that started this all in the first place. But that doesn't make this okay.
+
+Systemd has NO right storing your birthdate at all. It's an init system. While yes, systemd-homed stores PII (Personally Identifiable Information) like full names, that's in relation to user accounts, not an init system. This also ties into another problem with systemd that's been discussed for ages: scope creep. The fact that systemd, an init system, made to manage background services, now has infrastructure for age verification? That's an example of systemd expanding its scope into places it shouldn't even need to be. Network connections? Somehow that's systemd's job? DNS resolver? That's... also systemd's job somehow, despite better tools existing like dnsmasq? And now it's got infrastructure for AGE VERIFICATION??
+
+Putting the scope creep aside, let's be real. As a 13-year-old, even I was taught in primary school that rule #1 of the internet is "DO NOT SHARE YOUR PERSONAL INFORMATION." And yet, now we have the same companies and organisations and lawmakers who told us not to share our information merely 13 years ago asking us to share our IDs, videos of ourselves, our names, phone numbers, hell, at this point Google probably knows your Social Security Number.
+
+The cherry on top? The PR was merged by Microsoft engineers. The same company that built Windows 11, an operating system that treats you less like a user and more like a product. An operating system that spies on your every move and forces their Microsoft accounts and other spyware onto you (ahem, Windows Recall).
+Make of that what you will.
+
+If you care about Linux staying free,
+If you care about Linux staying libre,
+If you care about digital sovereignty,
+Pay attention to this one.
+
+---
+
+### Sources
+
+- [https://ostechnix.com/systemd-userdb-birthdate-age-verification/](https://ostechnix.com/systemd-userdb-birthdate-age-verification/){:target="_blank"}
+- [https://itsfoss.com/news/systemd-age-verification/](https://itsfoss.com/news/systemd-age-verification/){:target="_blank"}
+- [https://news.ycombinator.com/item?id=47436240](https://news.ycombinator.com/item?id=47436240){:target="_blank"}
+- [https://www.sambent.com/the-engineer-who-tried-to-put-age-verification-into-linux-5/](https://www.sambent.com/the-engineer-who-tried-to-put-age-verification-into-linux-5/){:target="_blank"}
+- [https://agelesslinux.org/distros.html](https://agelesslinux.org/distros.html){:target="_blank"}
+- [https://linuxiac.com/systemd-introduces-birth-date-support-for-upcoming-linux-desktop-age-controls/](https://linuxiac.com/systemd-introduces-birth-date-support-for-upcoming-linux-desktop-age-controls/){:target="_blank"}
+- [https://github.com/systemd/systemd/pull/40954](https://github.com/systemd/systemd/pull/40954){:target="_blank"}
diff --git a/content/blog/test.md b/content/blog/test.md
deleted file mode 100644
index 1cd3ca6..0000000
--- a/content/blog/test.md
+++ /dev/null
@@ -1,17 +0,0 @@
-+++
-title = "Blog post test"
-date = 2026-07-17
-extra.image = "/assets/images/Arslaan Pathan.svg"
-+++
-
-# Test blog post
-
-```shell
-echo "Codeblocks"
-```
-
-`smaller codeblocks`
-
-Testing testing 123 chicken nuggets not 3
-
-<span class="yellow">Yellow text.</span>
diff --git a/content/blog/test2.md b/content/blog/test2.md
deleted file mode 100644
index a79fa9b..0000000
--- a/content/blog/test2.md
+++ /dev/null
@@ -1,17 +0,0 @@
-+++
-title = "Blog post test 2"
-date = 2026-07-18
-extra.image = "/assets/images/Arslaan Pathan.svg"
-+++
-
-# Test blog post 2
-
-```shell
-echo "Codeblocks"
-```
-
-`smaller codeblocks`
-
-Testing testing 123 chicken nuggets not 3
-
-<span class="yellow">Yellow text.</span>