Epiktistes

Epiktistes is my home in the Fediverse. It is an instance of Ktistec, a single-user ActivityPub server like Mastodon, but with fewer users and fewer commits. Here's my introduction (last updated early-2025).

I wrote a series of posts about optimizing the performance of the Ktistec server, its build time, and its executable size: part 1, part 2, part 3, part 4, and part 5.

Some things I regularly write about, organized by hashtag:

I also wrote some #pointfreeverse.

Todd Sundsted

I need to use a prefix to namespace status IDs vs. boost IDs in the #ktistec Mastodon-compatible API. In Mastodon, a boost is just a status and they share the same ID namespace. Ktistec predates its Mastodon-compatible API, so statuses and boosts are maintained in different tables. I wanted to use an emoji (✍️ vs. 📣) to distinguish them, but that breaks too many clients.

Shame...

Todd Sundsted

hung up three sets of curtains… hit studs on every hole. it’s surprising what makes me happy.

Todd SundstedWeek in Fediverse :fediverse_light:
Todd Sundsted
Release v3.5.0 of Ktistec

I really enjoy optimization. Release v3.5.0 of Ktistec doesn't drop significant new features, but it does deliver a ~15% smaller executable and significantly faster queries on anonymous endpoints. The two are intertwined.

The size reduction comes from replacing a poorly designed, custom rules engine with a materialized view layer that uses SQL to define membership in a collection. The rules engine worked well enough but required a lot of supporting code to present rules as a DSL (Domain Specific Language) over the domain objects in ktistec. The driving realization was that SQL is a DSL and membership in a collection is just a query and domain objects are just rows. Voilà!

Query performance improvements came from using this new view layer to materialize two very popular but expensive-to-query views: the instance's public timeline and public hashtag pages. Because both are public pages they receive more traffic than internal pages.

The problem with the original queries was that performance was not uniform. Querying for posts with popular tags was okay. Querying for posts with sparse tags was very slow. I could have added more indexes, but that's its own cost. After the change, endpoints all respond in a consistent ~10msec timeframe and the CPU barely registers when a crawler hits. (I don't want to make things easier for bots, but I don't want to pay a tax for their activity either—ask me about my new nginx configuration.)

Here is the full changelog:

Added

  • Lightweight probe endpoint for authenticated sessions.
  • max-id and min-id pagination links on web pages.

Fixed

  • Correct the notifications collection's JSON representation.
  • Accept both single-value and array forms of JSON-LD properties.
  • Handle variation in schema.org property mapping.

Changed

  • Faster timeline, public, hashtag, and notification collections.
  • Adjust the layout of actor profile properties.

Removed

  • The school dependency; replaced by activity processors and materialized views.
  • The openssl_ext dependency; vendored in.

There are still a few slow queries. In the next release I'm going to see if I can get everything under 10msec, and maybe release a new feature, too. 🚀

#ktistec #crystallang #activitypub #fediverse

Todd Sundsted

i predict there will be a day in 2026, in the npm ecosystem, in which it will be impossible to simultaneously update a package to fix a critical zero day without also downloading a required transitive dependency that introduces another...

Todd Sundsted

i think the defining characteristic of a "grey beard" (other than having an actual grey beard) is stories like: "decades ago, a guy i worked with wrote a script called bup that saved changes to a named file, regardless of the version control system in use. git? no problem! rcs? no problem! cvs? no problem!"

i still have the script. 👈

Todd Sundsted

I just finished working on improvements to #ktistec that cut about 15% off the built executable size, and speed up some of the more common public queries by 2x to 5x (they were already fast, so this is headroom).

It does this by replacing a poorly designed, feature poor, custom rules engine with a materialized view layer that uses SQL as its DSL (domain specific language).

I am about to smoke test it on my own site. If it’s not available—well, you know why! 😀

Todd Sundsted
photo of an old, wooden Victor record player with a Roy Rogers record on top

I’m thinking of buying a turntable…

Todd Sundsted
Todd SundstedWill Richardson
Slide apparently from some Anthropic post, showing the architecture of Claude Code with lots of boxes and arrows and stuff. Originally they all said "Claude" but now they all say "tmux"

At the bottom it says "agentic teams" and "dynamic workflows"

Finally mapped out my dev workflow