See More on GitHub

Want to see what I’ve been working on recently? Check out my GitHub!

Citadel

Citadel is a desktop ebook management library app which can work from an existing Calibre library. I built it to be fast and easy to understand.

I’ve long found that Calibre has a ton of features, many more than I would ever use. I love powerful tools! I appreciate that Kovid Goyal built the first great, wide-spread ebook management tool. It’s one I used for many years. But around the time of the Python 2 to 3 conversion, I found that Mr Goyal’s attitude towards software did not match my own. My impression is that Mr Goyal sees Calibre as being his software — which I fully respect. It’s his own software for his own use. It seems he occasionally adds features for other people but, when it comes to visual design, or the language that Calibre uses, Mr Goyal stands by his own opinions. It’s better to fork Python 2.7 and maintain it than have to upgrade. It’s better to keep Calibre ugly, but powerful, than refine the UI.

Blender showed us it doesn’t have to be this way: the release of Blender 2.8 came with an entirely overhauled UI, to great fanfare. And, soon, to great adoption. The single fact that Calibre-Web exists indicates to me that Mr Goyal has one impression of what his software should be and is uninterested in changing that for others.

I respect that. I think he’s right in his conviction, and it’s that same conviction that gave me the spirit to start building Citadel. After all: why build some piece of software for yourself when a better, more powerful version already exists?

But I found my reason. Because I wanted to. I want excellent desktop ebook library management software. I don’t want ebook publishing software, or a news server built in. I don’t need a desktop ebook reader built-in. There are better single-purpose tools for any of those use cases. But none of those cases are issues for me.

So, again, I built Citadel.

A screenshot of desktop software Citadel. In the center, a grid of book covers. Where a cover is missing is a calm oil painting of a pastoral scene, with the books title and author labelled over top. On the left side is a navigation rail, including links to 'All Books', 'Authors', 'Series', as well as custom queries. The UI is minimalist.

Citadel is minimalist software. A simple grid of book covers, a few options for custom queries (which can be saved as “shelves”).

I want to make Citadel the fastest, easiest desktop software to manage my own ebook library. Now that it’s set up, I use it often. I wish I used it more!

The process of building Citadel has been tumultuous (1, 2), however. I did not expect, when I wrote my first few thousand lines of code, that I would rewrite it. and rewrite it. and rewrite it again. The core of persistence in Citadel, libcalibre, has gone through many architectures as I figured out the design that made the most sense to me. Now I feel that I’ve built something I can work off of. libcalibre — named for the desktop software whose database files it knows how to manage — is a core that can easily be swapped out one day, when Citadel is ready to move past Calibre. Just the same, the library is nearing enough stability + testing that I would be comfortable breaking it out into a separate project so that others can work with it for their own projects.

So that’s Citadel. I still feel that it’s early days for the project — two years on. It’s the software I use to manage my ebook library, but I doubt others are using it yet. Soon, I’ll add a new blog article with all the new features I’ve added in the last month. A Claude Max 20x plan has done wonders for executing on all the changes I have planned.

Last updated July 1, 2026.


🪵☁️ logseq-raindrop

In 2022, I created logseq-raindrop, a plugin for the Logseq note-taking app that syncs your Raindrop.io bookmarks and annotations into your Logseq graph.

A screenshot of Logseq with the logseq-raindrop plugin window open. A Raindrop, with its highlights, has been imported into Logseq as a new page.

logseq-raindrop provides a window for importing Raindrop bookmarks and their annotations into Logseq one-at-a-time.

logseq-raindrop is written in TypeScript. Plugins run as iframes within the Logseq app, so the UI is built using Svelte.

In 2023, I began an overhaul of how the plugin works. First, by abstracting the Logseq API behind a façade (which allowed me to write a mock Logseq client for testing), and then by adding new functionality to support syncing all bookmarks to a single page. This is similar to how Omnivore’s Logseq plugin works, and was heavily requested by users.

I continue to chip away at logseq-raindrop, and I’m excited to see where it goes. I have a sense that what I’ve been building is much more than one plugin; I’ve started to think of it as a library for building connections between read-it-later apps and note-taking tools. Why have one plugin for Raindrop.io and one for Pocket, when you could have one plugin that supports both? Or even just support a generic API for syncing bookmarks and annotations?

Of course, I best be careful — talk like this is dangerously close to Readwise’s product.

Last updated June 10, 2023.


🌊 bsflw/harbour

I created harbour to act as a proxy so that Git can use Keybase to sign git commits, instead of using GnuPG2.

It’s written in Go, and is mostly a straightforward proxy — Git calls harbour, Harbour talks to Keybase, translates back to something Git will accept. Getting Git to accept the responses was the most difficult part, as I generally had to reverse engineer the “accepted values” for different requests.

Not long after I started working on the project, Keybase was acquired by Zoom. I have no interest in trusting my secrets to a company that has proven to have a poor track record with security, so I stopped using Keybase and archived the project.

However, I’m glad I had the chance to work with Git’s internals. It was a fun challenge to translate the responses from Keybase into something Git would accept. This project, in part, was what led me to explore some other version control projects, including Gitless and jj.

Last updated June 10, 2023.


🔗 shorturl & domainator-nim

Inspired by an article I read about the Nim programming language, I decided to rewrite my existing Python project domainator in Nim as domainator-nim.

domainator generates a list of valid domain names that you could register that, using subdomains or paths, contains the string you provide. It’s automated domain hacking.

For example, if you wanted to sell a product alled “Chuckles”, you might know that “chuckl.es” is a valid domain. However, you could instead register “chu.ck” and redirect your traffic to the /les path, giving you “chu.ck/les” as the homepage. domainator generates a number of these domains for you. It’s a silly project, but a fun way to learn about Nim.

After working on domainator-nim, I decided to try rewriting @xmonader’s Day 7 of Nim days, which I called shorturl.

shorturl is like any other URL-shortening service, except — since it was intended to be a learning project — it has the fatal flow that all of the URLs are entirely in-memory.

Last updated June 10, 2023.