Portfolio/content/posts/2026-09-08-hello.md
2026-09-08 19:54:06 +02:00

2.1 KiB

title description date tags draft
how this blog works markdown in a git repo, a webhook, and no build step. here is the whole thing. 2026-09-08
meta
nuxt
gitea
false

This post is a file. It lives in a git repo, it has YAML frontmatter, and pushing it is the entire publishing pipeline. No CMS, no rebuild, no npm run deploy.

the shape of a post

Everything above the second --- is frontmatter:

---
title: how this blog works          # required-ish; falls back to the filename
description: one line, used in the  # optional; falls back to the first paragraph
  list view and the og tags
date: 2026-09-08                    # optional; falls back to a YYYY-MM-DD filename prefix
updated: 2026-09-09                 # optional
tags: [meta, nuxt, gitea]           # optional; also accepts "meta, nuxt"
slug: how-this-blog-works           # optional; falls back to the filename
draft: false                        # true hides it in production
---

The filename becomes the slug, with a leading date stripped: 2026-09-08-hello.md → /blog/hello.

how it gets here

  1. You push to the content repo.
  2. Gitea fires a webhook at /api/webhook/content with an HMAC signature.
  3. The server checks the signature, drops its cache, and re-reads the tree.
  4. The next request renders from the fresh copy.

If the webhook never fires — network hiccup, misconfigured secret — the cache still expires on its own after CONTENT_TTL seconds. And if the repo is down when a refresh is due, the last good copy keeps being served instead of a 502.

code and images

Fenced blocks are highlighted server-side by Shiki, in both themes at once:

fn main() {
    let payload = std::fs::read_to_string("post.md").expect("a post");
    println!("{} bytes, zero javascript", payload.len());
}

Relative image paths resolve against the post's own directory in the repo, so ![a diagram](./diagram.png) works if you commit the png next to the markdown.

Blockquotes, tables, and lists all render. Anything markdown-it understands.

That's it. Write a file, push, done.