unbuffered: the publishing pipeline

Executive abstract

This site deploys itself: a git push triggers a pipeline that validates every document against a typed schema, compiles diagrams from code, generates a full-text search index, and ships static HTML to a global edge network. There is no server and no database. Measured on the first production build: 14 pages in 1.6 s, homepage 1.7 kB gzipped over the wire, site-wide JavaScript 766 bytes gzipped (a theme toggle and a diagram detector — nothing else).

Environmental constraints

  • Publishing cadence beats polish. The pipeline must make a 10-line tip as cheap to ship as a 5,000-word case study, or the site dies of friction.
  • Free-tier hosting only. No fixed costs; everything must fit Cloudflare Pages’ free plan and a private GitHub repo.
  • The author is the only operator. Anything that can rot silently (broken frontmatter, dead internal links) must instead fail loudly at build time.

Architecture blueprint

flowchart LR
    A[Markdown in editor] -->|git push main| B[GitHub]
    B -->|webhook| C[Cloudflare Pages build]
    subgraph C_[astro build]
        direction TB
        C1[Zod schema check<br/>every frontmatter] --> C2[remark: wikilinks + mermaid]
        C2 --> C3[Shiki dual-theme<br/>highlighting]
        C3 --> C4[Static HTML + RSS + sitemap]
        C4 --> C5[Pagefind WASM<br/>search index]
    end
    C --> C_
    C_ --> D[(Edge network<br/>300+ PoPs)]
    D --> E[Reader]

Bottleneck engineering

Diagram rendering. Build-time Mermaid rendering (rehype-mermaid) needs a headless browser in CI — a heavyweight dependency for a static site. Instead, a 20-line remark plugin rewrites ```mermaid fences into <pre class="mermaid"> at build time, and a page-level script dynamically imports the renderer only when such a block exists. Pages without diagrams ship zero JavaScript; pages with diagrams pay the cost lazily, after first paint.

Search without a search server. Full-text search is usually the excuse for a backend. Pagefind indexes the built HTML at compile time and serves a chunked WASM index as static files — the browser fetches only the index fragments matching the query prefix.

Decision lineage

  • Framework, hosting, and styling decisions: ADR-001

Post-mortem honesty

  • The unified stream on the homepage re-sorts every collection on each build — O(n log n) over all entries. Irrelevant at n < 10,000 documents; noted here so future me doesn’t pretend it was designed for scale it doesn’t have.
  • Mermaid’s renderer is heavy when it does load: a ~660 kB core chunk plus per-diagram-type chunks (measured in dist/_astro/). Vite code-splits it so non-diagram pages never fetch a byte of it, but diagram pages pay real weight after first paint. The right long-term fix is build-time SVG rendering; the current trade favors CI simplicity while diagram pages are rare.