What is Satz, anyway?

2026-09-16 5 min read

Satz? As in the sentence?

Yes, exactly. satz is my new tooling: among other things it powers this blog, lets me write posts and makes up the entire backend behind the scenes. All of it follows my conventions, which I have been updating, refining and reworking for a few weeks now, and which are slowly taking shape. The name fits rather well, by the way: Satz is German for "sentence", and in printing it also means typesetting, turning text into finished pages. That is exactly what happens to my posts here.

The satz wordmark

Conventions?

Yes! Whenever I build a new tool, I now use this approach:

Name What is it?
satz The CLI tool I use on the command line
satzd The server in the background that answers every API request, optionally with an MCP component so AI models can talk to it too
satz-gui A GUI, meaning a graphical application that runs on the desktop
satz-core The core logic, the so-called domain logic, meaning everything the CLI, the server and a GUI all need
satz-web The website that fetches its data from the server (and where you are reading this post right now)

A small note: satz-gui does not exist. But my convention reserves the names up front, in case I ever extend a project with a GUI.

The upside: whichever project I open, I know right away which part does what and what it is called. And the CLI only ever talks to the server through the API, never directly to the files behind it.

Why?

Because I can? Jokes aside: the old projects had a few problems that could no longer be fixed nicely.

The old and bold

Pyblog, Sveblog and blogli, the old projects I used to run the blog with, were okay in principle, but terrible to maintain. They also no longer matched the stack I work with, at least Pyblog and blogli (both in Python) did not. The three lived in separate repos, and the bugs usually sat right between them: blogli, for example, dropped a post's language when publishing, and Pyblog then overwrote the wrong file. I used Sveblog as the template for this redesign, and since the stack is the same (Svelte/SvelteKit/Vite), that was not much of a hassle; some things could be recycled.

New and shiny

satz and the other components are written in Rustrust-lang.org. The upside that comes with it: every tool is a single binary I can install anywhere. Yes, it has to be compiled first, but that makes it much easier to build a finished image from it that is always the same. Besides, I wanted to dive (back, or finally properly) into the world of compiled languages anyway, so I went straight for a modern, actively developed language with a large community. On top of that, all the parts now live together in one repo. When something changes between them, the compiler notices, not me while writing.

...also, Rust's mascot is a crab 🦀...

This is Ferris, the crab

So what can it do?

Apart from what you can see on the website, the site itself is faster now, because the text processing happens up front: satzd turns the Markdown straight into HTML, including the table of contents, reading time and syntax highlighting. In practice that means: the links you see as chips in a post (the round things) are built in by the builder, a component of satz-web, while the site is being built, and no longer in your browser. Images sit right next to the finished page instead of having to be loaded through the API first. And because every page is delivered fully built, its title and description are right there in the HTML, so Signal, Mastodon and friends show a proper preview when you share a link.

satz itself is easier to use, and it behaves consistently now. That matters less to you as a reader, but for me as the writer it makes a huge difference.

# This is how I created this very post!

$ satz post add was-ist-satz --lang de
...
...

# Upload the picture of the crab; whatever comes out with a ! goes into this post.
$ satz asset add was-ist-satz ferris-the-crab.png
![ferris-the-crab.png](assets/ferris-the-crab.png)

# Quickly edit something...
$ satz post edit was-ist-satz --lang de
...
...

# Copy the German post as a template for the English one
$ satz post translate was-ist-satz --lang en --from de

# Publish the German post
$ satz post publish was-ist-satz --lang de
...
...

# Oh right, the English one too
$ satz post publish was-ist-satz --lang en

This may look like mumbo jumbo at first, but it is actually very intuitive if you are used to working in a terminal! Every command follows the same pattern: first the thing (post, asset), then what should happen to it (add, edit, publish).

And now?

What do you mean, and now? Happy fun reading! And I'm having happy fun writing! Next on the list for satzd is the MCP component, so I can work on my posts with AI models too.

Built in Germany with ♥︎ powered by satz v0.8.2