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

One of the nice benefits of working on an open source project is that you can scratch an itch for as long as you feel like scratching. A game I like to play while scratching is called "what invalid states can I make unrepresentable" using the type system.

ktistec uses a template language for views and partials. In its original form, it allowed a programmer to use = to escape an untrusted value or == to render it unescaped. You might want to escape an actor's name property because a name should never contain HTML but you might want to sanitize an object's content property and then render it without escaping because the body of a post can contain HTML.

The problem is you have to remember the rules and never make a mistake. If you accidentally type == actor.name you've just created a potential cross-site scripting (XSS) vulnerability!

ktistec's template language now makes it much more difficult to screw up.

There's now only = syntax and by default it escapes everything. The only way to get it to emit a string without escaping is to wrap it in SafeHTML. The sanitize helper sanitizes HTML and then wraps it for you. Other common helpers (e.g. path construction helpers) do the same.

Importantly, if you interpolate a safe value into a string, it is demoted back to a string and will be escaped unless it is explicitly wrapped again.

Unescaped HTML is still possible to construct, but it's now much harder to do so accidentally. You can't just concatenate some strings together鈥攆orgetting that one comes from an untrusted source鈥攁nd render that as unescaped HTML.

#ktistec #crystallang

Todd Sundsted
Release v3.3.7 of Ktistec

Release v3.3.7 of Ktistec fixes several bugs and introduces two enhancements.

Security is a focus in this release. Every gap in input sanitization or escaping is a potential vulnerability, and I've been systematically closing them. I am also carefully, and maybe conservatively, restricting things like supported URL schemes and uploaded file types.

The two enhancements improve compatibility with Mastodon-compatible clients. Mastodon's OAuth tokens don't expire, and Mastodon clients don't know how to handle tokens that do. Sliding expiration ensures that tokens in active use stay alive, while unused tokens eventually expire.

Here's the full changelog:

Added

  • Sliding token expiration for OAuth2 access tokens.
  • Mastodon-compatible API: /api/v1/accounts/update_credentials endpoint.

Fixed

  • Prevent pinning of (and auto-unpin) private objects.
  • Don't save a quote if the quoted actor cannot be dereferenced.
  • Fix rendering of federated actor profile attachment values.
  • Remove href attributes with unsafe schemes from sanitized HTML.
  • Escape interpolated values in view helpers and the actor icon streaming refresh.
  • Restrict upload extensions and serve uploads with X-Content-Type-Options: nosniff.
  • Escape publicKey and scrub Tag.href.
  • Sanitizer no longer permits single-quote attribute injection.
  • Ensure bearer-token sessions cannot reach the web UI.
  • Require client authentication on the OAuth token endpoint.

I'm working on performance improvements for the next release. A rewrite of the Slang template library looks like it will cut both build time and executable size by around 10%!

馃摗 Stay tuned!

#ktistec #crystallang #activitypub #fediverse

Todd Sundsted

east village, manhattan, nyc
floodgates

view of the large floodgate door along the east river of manhattan
Todd Sundsted

i鈥檝e been writing lexers and parsers for most of my career. i still don鈥檛 feel i鈥檓 very good at it.

Todd Sundsted

east village, manhattan, nyc
still one of my favorite places
surprisingly quiet evening

1st avenue, looking east along 10th street at trees, cars, people, and stores at twilight
Todd SundstedCrystalLanguage

A new patch release Crystal 1.20.1 fixes some regressions and disables Kernel TLS (added in 1.20.0), due to instability.

Read more at crystal-lang.org/2026/04/29/1.

#crystallang

Todd Sundsted

i learned to program so that i could write text-based adventure games, so i'm particularly excited about this: 50 Years of Text Games

Todd SundstedCrystalLanguage

We released Crystal 1.19.2 to fix the request smuggling vulnerability (already fixed in 1.20.0) and a regression in Range#sample that could eventually lose randomness.

crystal-lang.org/2026/04/27/1.

Todd Sundsted

I鈥檓 working on handling OAuth token expiry as part of #ktistec Mastodon API support. Is my understanding that Mastodon issues OAuth tokens with no expiration correct?!?

Todd Sundsted

does anyone use GNU Cash? any free (software) alternatives anyone recommends?