A Philosophy for Personal Media File Storage

If you've been taking photos and videos for more than a few years, you've probably accumulated a mess. Files from phones named IMG_20230415_143022.jpg, Pixel cameras producing PXL_20230415_143022.mp4, screenshots labeled Screenshot_2023-04-15, and whatever your old point-and-shoot decided to call things. They're scattered across devices, cloud services, and backup drives — some with correct dates in their metadata, some without, some with dates that are just wrong.

At some point you try to organize them. You create folders: "Vacation 2023", "Family", "Work Events". It feels productive for a week. Then a photo belongs in two folders, or you can't remember whether that dinner was "Family" or "Friends", or you find a folder called "Misc" with 400 unsorted files from three years ago.

I went through this cycle a few times before stepping back and rethinking the problem.

Eventium: Design Decisions and Internals

In the previous post, I walked through building a banking system with eventium — covering projections, command handlers, process managers, and read models from the user's perspective. This post goes one layer deeper: how the event store is structured, what guarantees it provides, and why things are built the way they are.

Each section opens with a problem that arises when you try to build a production event store, then shows how eventium addresses it and what tradeoffs that involves.

Event Sourcing in Haskell with Eventium

I've been building a Haskell library called eventium — a typed, composable event-sourcing and CQRS library. It started as a fork of the abandoned eventful project, modernized for GHC 9.10+ and reshaped around a cleaner set of abstractions.

Event sourcing is one of those ideas that sounds straightforward until you try to implement it properly. State is derived from a sequence of events, not stored directly. That constraint forces clarity — but it also raises a lot of questions about how projections, commands, and aggregates should fit together.

This post walks through building a small banking system using eventium v0.2.1, covering each abstraction as we need it.

GADTs in Haskell

If you've spent any time reading Haskell code, you've probably run into GADTs — Generalized Algebraic Data Types. Maybe you've seen the {-# LANGUAGE GADTs #-} pragma at the top of a file and moved on. Maybe you've read an explanation or two and thought "okay, but when would I actually need this?"

This post builds up to GADTs step by step. We'll start with a problem, try the obvious fix, watch it fail, and then see how GADTs solve it cleanly. By the end, we'll also peek under the hood to see what GHC is actually doing.

Managing MikroTik RouterOS with Nix

My home network runs on a MikroTik router. It's a capable piece of hardware with a powerful but deeply imperative configuration system. RouterOS gives you a CLI, a GUI (WinBox), and a scripting language that feels like it was designed for a world where "just SSH in and change it" is a valid operations strategy.

For a while, that was exactly what I did. Then I tried to bring the same declarative thinking I use for everything else to my router. It took three attempts to get it right.