hetzel.net

Untitled Blog Engine

untitledgophergame.jpg

Recovery

Die alten Posts waren sicherlich nicht weltbewegend, aber sie haben doch einige schöne alte Screenshots vom frühen iOS und dem Web 2.0. Alte Podcast-Workflows, Spaß mit der Telekom und so.

Die Recovery der alten Blog Posts aus der Wayback Machine hat ein paar Stunden gedauert, hauptsächlich um die letzten paar Bilder zu finden, die dort gefehlt haben. Die waren tatsächlich noch in dem unvollständigen Dropbox-Backup, das die ganze Zeit intakt ausgesehen hat, aber tatsächlich größtenteils leer war und zu diesem ganzen Schlamassel geführt hat.

HTML umgewandelt in Markdown mit YAML Front Matter mit den Metadaten der Wordpress-Datenbank. Datum, Post-ID, Slug.

Bilder zusammengesammelt, Embeds rausgeworfen, ein paar Links zu alten Podcasts berichtigt.

Untitled Blog Engine

Mein favorisierter Stack für solche kleinen und größeren Seiten ist Golang + SQLite. Zwei Dateien und die Website läuft.

In 6 Stunden habe ich mit Codex diese Seite in gut 3000 Zeilen Code gegossen.

Features:

  • One Go executable: server, templates, CSS, admin JavaScript are all embedded. Pure-Go SQLite keeps builds free of a C toolchain dependency.
  • Server-rendered HTML: standard Go templates, small local stylesheets, no public JavaScript or external font/framework downloads. Very little for the browser to load.
  • SQLite for persistent state: content, settings, passkeys, and sessions in one database. WAL mode, one serialized writer, and four read-only connections allow concurrent reads and edits.
  • Render when content changes: HTML, excerpts, and social descriptions are stored transactionally. Public requests read prepared content; renderer versions trigger backfills when rendering rules change.
  • An archive that survives migration: roughly 250 recovered posts, original URLs, German/English versions, and stable RSS identifiers that preserve old WordPress IDs.
  • A usable publishing workflow: posts and pages, drafts, private previews, scheduled publication with timezone support, admin search/pagination, and browser-local draft recovery.
  • Passkey authentication: WebAuthn login, one-time initial setup, and signed cookies backed by revocable database sessions.
  • S3-compatible image storage: uploads insert Markdown into the active language editor; UUID filenames support long-lived immutable caching.
  • Straightforward operations: Git-push deployment to server, Caddy for HTTPS/compression, systemd, health checks, Monit, and daily offsite SQLite backups.
  • Caching at the edge: Cloudflare caches public HTML/RSS for five minutes while admin responses stay uncached. Publishing changes can therefore take five minutes to appear.
  • Performance guided by measurements: narrow queries and covering indexes keep Markdown out of listing reads. Recorded Linux handler benchmarks put the homepage around 0.9 ms, excluding networking, TLS, and browser work. The useful lesson: moving rendering work to saves and keeping the public page lightweight got us further than adding infrastructure.

Die ganze Engine ist auf zweisprachige Posts ausgelegt, die meisten Posts haben eine automatische Übersetzung bekommen.

Deploy auf den Server via git push prod, baut Linux-Binary auf dem Server und zieht vorher ein Backup für mögliches Rollback. Vor dem Blog sitzt ein Caddy, der noch viele andere Dienste für andere Dinge bereitstellt.

Medien werden direkt auf R2 hochgeladen, der S3-kompatiblen Object Storage von Cloudflare. Und dort sitzt auch der Cache vor der ganzen Seite. Caching Header sollten so gut wie keinen Traffic wirklich bis zum Blog durchreichen, die Geschwindigkeit sollte von einer statisch gebauten Site kaum zu unterscheiden sein.

Design ist sicherlich noch ausbaubar, aber besser als nix. Backend ist nichts für schwache Nerven, aber ich bekomme das hin.