A while ago I was using Ghost for this blog, but it always felt like overkill. It was focused on serious publishers, not on me pushing minor technical notes and hobby projects once in a while. Being a developer, I love the git-first approach: everything is versioned and can be checked out and edited locally in a code editor. PRs for drafts. So I went with Hugo. While the rendering experience and flexibility are great, it turned out that this made it much harder to post. The fact that I had to have a local git checkout meant the friction was high enough that posting became something that needed a lot of dedication and preparation. So I posted less often than I would have liked to.

Today I realized that I’ve been living under a rock for almost a decade. There is a whole family of headless CMSes

which allow posting from a browser using git as a backend. NetlifyCMS/DecapCMS, first released in 2016, popularized the approach, but when development of Netlify CMS stalled in 2022, SveltiaCMS (obviously based on Svelte) appeared. It’s less popular than Decap, but it has one extremely valuable feature - Gitea (and Forgejo) support. I store most of my personal projects, including this blog, in a self-hosted Gitea instance, so it’s a match.

Installation is trivially simple. It uses the browser OAuth2 flow, so on Gitea I created an OAuth2 app, then added just one tiny static HTML file to my blog at the /admin/ path:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="robots" content="noindex" />
    <title>Content Manager | kumekay</title>
  </head>
  <body>
    <script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js"></script>
  </body>
</html>

and a config.yaml describing the structure of my blog, which git repo it’s stored in, and the ID of the OAuth app in Gitea. That’s it. Five minutes of configuration and I’m typing this blog post in a beautiful UI.

It changes my workflow a bit - instead of working on new articles in PRs, I work right in the main branch with draft set to true and just flip the setting to publish. Given that I’m the only contributor, this approach is completely fine.