Recent Notes


# Ansible Pays Dividends -- 2026-02-22

I use Ansible to maintain a Miniflux instance which is running inside a private Wireguard network. The website has TLS (even though it is running inside Wireguard; I know this is pointless) and certificates are issued automatically using CertBot through LetsEncrypt. These certificates are also supposed to be renewed automatically. Since the beginning of this setup (roughly 2 years ago), this has not been working properly, because once the certificate is renewed the Nginx process needs to be reloaded (systemctl reload nginx) in order to serve the new TLS certificates to the user. CertBot supports running arbitrary commands after a certificate renewal through pre, post and deploy hooks. I set up a deploy hook but that was not working because the script was not executable. (I created the script with the permission bits set to 0600 reflexively!)

The fix was simple: Update the Ansible role to set the mode on the containing directory /etc/letsencrypt/renewal-hooks/deploy to 0755 and the permission bits on the script /etc/letsencrypt/renewal-hooks/deploy/01-restart-nginx to 0744. The script used /bin/bash which was failing (or at least throwing a warning) because the locale was not set appropriately in the environment where the script was running. So, I updated it to use /bin/sh instead:

#!/bin/sh

systemctl reload nginx

Whether this hook will run during a certificate renewal can be tested by running sudo certbot renew --dry-run and following the /var/log/letsencrypt/letsencrypt.log file. The log file contains this line, which indicates that the deploy hook would have run if the dry-run flag had not been supplied:

2026-02-21 11:52:46,997:INFO:certbot._internal.hooks:Dry run: skipping deploy hook command: /etc/letsencrypt/renewal-hooks/deploy/01-restart-nginx

# Faster Unified TODO List in Emacs -- 2026-02-06

Over the past year, I have successfully combined all my TODO lists into a single Org-roam notebook, which is just a bunch of .org files, stored inside a single directory. Simple: I can back them up by running rsync. I can version them using Git. I can move them around safely using Tomb. I can navigate to any of them or add a new note using the org-roam.el Emacs package. I can use usual plaintext search tools such as ripgrep to find words or phrases in files. I really like this setup, and use it at work as well. Org-agenda is Org-mode’s native capability of showing a daily or weekly TODO list called an Agenda: It contains the tasks that are scheduled for today or have their deadline in a few days. The time that it takes to build the agenda using native Org-mode is too long: For my notebook with about 80-90 files with TODO items, it takes 150 seconds! This makes the whole setup unusable. Emacs is single-threaded and is not able to opening 100s of files and look through them in a performant manner. Two packages, org-super-agenda and org-ql, helped me solve this issue, and reduced the time taken to open the daily agenda to under 10 seconds.

org-ql provides a flexible query language to fetch the tasks which will be displayed in the agenda. And it provides a helpful wrapper that allows the user to dynamically define the list of files to search during runtime. Org-mode’s native agenda accepts a list of files and directories to search for TODO items. It will look through all files. Even the files that don’t have any TODO item in them. Excruciatingly slow! One quick optimization is to use ripgrep to condense this full list of files into just the ones that contain a TODO item.

org-super-agenda focuses on grouping the tasks that are to be displayed as part of the agenda. For instance, I have four groups:

  1. Tasks with a deadline: Important, urgent
  2. Periodic tasks: Important, not time-consuming (Getting Things Done!)
  3. Everything else that is scheduled
  4. Everything else which is in the WAITING state
    1. I don’t have anything to do on these tasks, but usually, if I see that a task has been here for a long time, I may want to check in on what’s going on.

org-super-agenda groups tasks that are gathered by org-ql. This is great: I can use both independently as well, and I used only org-super-agenda for some time at work. But adding org-ql was a logical next step.

The changes for this in my Emacs configuration are in these two commits:

  1. feat: Start using org-ql to see daily agenda
  2. feat: Use org-ql instead of native org-agenda for performance

It would be great if someone would wrap all this up by creating a new package. I am sure this would be useful for other people who are on slow laptops but have a lot of files in their org-roam notebook, only a small subset of which have TODO items.


# USB-C Power Delivery: Good and Bad -- 2026-01-26

Charging through the USB-C port has had a tangible impact on the amount of chargers I carry when going outside. The last time that I traveled, I took a single charger with me, even though I had three devices. (All of them supported the USB-C Power Delivery (PD) protocol. The charger was also a USB-C Power Delivery - Standard Power Range capable device.) The devices were able to negotiate whatever power they wanted with the charger. This is a great improvement to having multiple cables for multiple devices. This is not the full picture though and has probably lead to more confusion for many people.

Not all devices that have a physical USB-C port support the USB-C PD protocol. I am repeating what many articles online have already explained. I ran into this firsthand a few days ago, when I attempted to charge a device that was completely discharged. I tried charging it with multiple power sources: a wall socket adapter, a laptop’s USB-C port, a laptop charger with a maximum voltage of 100W. In all these cases, despite being plugged in for many hours, the device did not show any sign of being alive. I guessed that the battery was dead and I would have to dispose the device.

A day before throwing it out though, something bugged me about the USB-C port on the device, and I decided to try to charge it with a non-USB-C PD enabled charger: a simple wall socket USB-A adapter and a USB-A to USB-C cable. Voila! The device turned back on after about 15 minutes!

The chargers I used do not seem to have supplied the minimum power that that was required to turn the device back on, or perhaps as this device does not have USB-C PD support, they probably just gave up and did not supply any power at all. This is not how the specification is worded, with this technical note saying that chargers must deliver the pre-Power Delivery protocol power on the USB-C connection upon initial connection. The gulf between USB specifications and their implementation has once again come back to bite us.


# AC Supply Voltage and Frequency Differences -- 2026-01-26

Often, when I am about to charge an electronic device that I bought in one country in a different country, I look at the label which lists the voltage and frequency for which the adapter is designed. Most adapters are usually marked 100-240V, 50/60Hz, which covers everywhere around the world. So, the only thing that is required is a physical plug converter. But sometimes, you might run into devices that are marked 220-240V, 50 Hz (AC supply in India) or 100-110V, 50/60Hz (AC supply in Japan) With such single voltage only devices, one must use a step-up or step-down transformer when using the appliance in a country with a different supply voltage. (There is no converter for frequency itself and appliances with motors will probably suffer when the frequency changes.)

One of the questions I started thinking about recently was whether one might be able to put some Solar panels outside, connect it to a battery, and run a small generation system at home which would supply power directly at 110 V (or 220 V if you are in the opposite situation). Is that a viable option? Solar panels are becoming more and more prevalent in both India and Japan and I am seeing a lot of individual houses that are making the capital investment, which is supposed to be recovered in 18-36 months.


# Tax Season and Technology -- 2026-01-18

Every year around this time, I have to file a tax return which (more or less) specifies all my income, all my deductions, and the tax that was deducted at source already. The process that I use for this is to convert my GnuCash accounts book into a Ledger file, and then use Ledger for verifying the various amounts. I like GnuCash because it has an intuitive user interface, that I have gotten quite comfortable with over the years. I like Ledger because it is a command line tool that I can use to create “repeatable” commands.

For instance, this command in Ledger gives me a total for the Income tax that has already been deducted from all my income this year:

$ hledger --file book.dat bal --begin 2025-01-01 --end 2026-01-01 'Taxes:Income tax'

The output of this command did not match initially. I was not exactly sure why. My guess was that the difference was due to a single transaction. So, I calculated the difference (Say: 2178) and then, I used ripgrep to search the text file:

$ rg -C5 2178 book.dat

This pointed me towards a single transaction that matched this amount. If there were multiple results, or no results, I could ask hledger to output the Income tax deduction on a monthly basis, and eyeball the result for anomalies.

Now, the conversion to Ledger for analysis is not necessarily a requirement. In fact, GnuCash has the Reports feature which can be used quite effectively for this purpose. Beware though, because the Report configuration is not stored as part of a GnuCash file, or anywhere that I know of which can be stored in a backup somewhere. So, every time that I update my computer or re-install the operating system, I lose all my existing reports configuration, which is a shame.


# Round Robin Database -- 2026-01-14

I have been interested in using RRDtool to monitor some numeric data for a while now. The main reason is because the graphs which are created by RRDTool look quite good: They are simple, but convey useful information succinctly. I have seen them around in a few places. They look like this:

source: GSM Signal Strength

Today, I spent about 40 minutes downloading the NIFTY 50 historical data for the year 2025, transforming it into a format that can be used to executed rrdtool update commands, and eventually come up with a Nifty 50 12 month graph, with 12 monthly averages. That may be useful, if I want to calculate the value of my portfolio over time. (This is also something that GnuCash sort-of does already, though I don’t understand “Value-over-time” graphs.)

These kind of graphs would be useful for timings of various requests that are being performed on the network, or for other kinds of data (like packet counters). I was looking at USB temperature sensors, which might be an interesting project. I don’t know if will work well with Linux though. This kind of data would also just be interesting to look at over a long period of time. For instance, the number of packets sent/received by a router over a period of 10 years would be cool to see.

I saw a YouTube video where the sensor was plugged into an Android device, a Notes app was opened, and once a button was pressed on the sensor, it started acting like a keyboard and sending data to the Android device as text (pretending to be a USB keyboard, I presume).


# Fourth Note -- 2026-01-12

This note was created by calling a single function using the keybinding C-c n n! It is surprisingly hard. Despite all the flexibility that is built into Org capture, the one thing that it cannot do is to take a string from the user, and use that string both in the captured file’s name and within the captured file’s content.

So, I had to write a bit of Elisp to get around this pesky limitation, that I have run into in other places as well:

(defun kannan/capture-new-note ()
  (interactive)
  (let* ((note-title (read-string "Note title: "))
         (note-file (expand-file-name (format "%s.org" (string-to-slug-with-date note-title)) local/notebook-location))
         (note-template '"#+title: %s
#+date: %%U
")
         (note-content (format note-template note-title)))
    (with-temp-buffer
      (insert (org-capture-fill-template note-content))
      (write-file note-file))
    (find-file note-file)
    (end-of-buffer)))

This function reads a note title from user input, converts it into a file name by replacing spaces with hyphens and converting everything to lower-case, writes the content of the capture template into a temporary buffer and saves it to said file, and then opens the created file and moves to the end of that new buffer.

The only part that org-capture is not able to do is select a dynamic filename. The solution suggested by ox-hugo is to use a single file and put multiple posts inside that file. I don’t like this solution much, because it ties me down to using Emacs exclusively to work with the list of posts.

For instance, I am thinking of creating a pre-commit hook that will fail when a .org file in content-org/ does not have a corresponding .md file in content/note/. This would have to be implemented within Emacs if I used a single file.

(Converting Org to Markdown in CI or in a pre-commit hook would be much better. But I would have to invoke Emacs from a pre-commit hook which would increase commit time significantly. I don’t much like pre-commit hooks anyway.)


# Second Note -- 2026-01-10

This note checks whether note ordering works properly when I use the huge new content CLI command.


# Hello, World! -- 2026-01-10

Let’s begin!

$ mkdir -p notebook
$ hugo new site notebook

All this was dry land

Yes, you have just always had your band

But you get sad enough because it’s all you seek

Day One - Bon Iver