How this is built

Wiki page for the production architecture of this catalog — crosslinked into technologies, taxonomy, and the rest of the site. Companion to Why I do this….

What this is

This site — Happy Overlord — is a research catalog, not a multi-tenant SaaS. What you browse under technologies, devices, taxonomy, design, vibe, and the blog is the same working inventory described on Why I do this….

The runtime is a single Next.js app on one Hetzner host. Truth for catalog and blog rows lives in SQLite files edited on localhost and shipped whole on deploy. Embeddings for recall live in corpus.sqlite (sqlite-vec) — see the sketch at RELATED-VEC and taxonomy under Matryoshka embeddings / Vector DBs.

Ops pages under /intern are not public: middleware allows loopback only; operators open them through an SSH tunnel. See Privacy for the first-party visitor trace.

Frontend

Frontend request path

Stack

  • Next.js (App Router) + React — server components read SQLite and Markdown; client islands only where the browser is required (Mermaid diagrams, newsletter Altcha, visitor-trace beacon).
  • TypeScript end to end.
  • Tailwind CSS + shared layout helpers (siteContainer / siteProse / siteCopy) so article, legal, and wiki pages share one reading chrome.
  • marked turns wiki Markdown into HTML after slug / slug resolution (wiki-links).
  • Mermaid renders architecture and pack How-diagrams client-side (MermaidDiagram).
  • pnpm for installs; production build runs on the server after rsync.

Why this frontend (not a static site or a SPA)

  1. Catalog pages need a server. Entity lists, blog visible = 1 filters, sitemap, and content-retrieval checks all open SQLite with Node’s node:sqlite. A pure static export would force a second publish pipeline for every pack change.
  2. SEO is first-class. Titles, canonicals, Open Graph, JSON-LD (WebSite, SoftwareApplication, BlogPosting, BreadcrumbList), and a live sitemap are generated in the App Router — see the contract in docs/SEO-AUDIT-PROMPT.md.
  3. Wiki prose stays Markdown. Packs and essays keep [RAG](/taxonomy/tech-rag)-style links; the same wikifyMarkdown path used on technology articles powers this page and todo sketches.
  4. One deployable. No separate frontend CDN app talking to an API gateway. The Next process is the site — matching the single-host SQLite model below.

What we do not run in the browser: sqlite-vec, or raw corpus embedding search. Recall stays server-side (or offline scripts); the UI only shows declared catalog edges today, with Suggested neighbours still a sketch (RELATED-VEC).

Storage — SQLite, not Postgres

SQLite stores vs server-owned files

What lives where

  • catalog.db — entity graph, edges, tags. Owned by localhost → deploy.
  • blog.db — posts + topics; public reads visible = 1. Localhost → deploy (server copy backed up first).
  • corpus.sqlite — full-text / embedding corpus (sqlite-vec). Localhost → deploy.
  • visitor-trace.db — first-party path + surface + db_hint (hashed IP). Server only — deploy never overwrites. See Privacy.
  • newsletter JSONL — opt-in addresses. Server only.

Markdown packs under technologies/, devices/, design/, vibe/, and taxonomy essays remain on the filesystem; the DBs index and publish what the loaders expose.

Why SQLite — not Postgres

This was locked in the blog design (docs/plans/2026-09-13-blog-db-design.md): remote DB (Postgres / Turso) is a non-goal.

  1. Localhost is source of truth. deploy.sh rsyncs the .db files and runs PRAGMA integrity_check. Editing research locally and shipping the artifact is the loop — not migrate-up against a live cluster.
  2. One writer shape. A single systemd unit serves the catalog. There is no fleet of app replicas needing a shared network database.
  3. Embeddings already in SQLite. corpus.sqlite holds sqlite-vec tables. Standing up Postgres + pgvector would split recall from the catalog without solving a concurrency problem we do not have. (We still document pgvector, Milvus, Qdrant, Chroma, FAISS, Weaviate as research packs — that is inventory, not the production store.)
  4. File-shaped ops. Backup = copy a file. Server-owned traces stay out of the rsync set. Postgres would add credentials, a network listener, and a second failure mode for a research catalog.

Postgres (and pgvector) remains the right answer for multi-writer products and shared OLTP. This site is read-heavy catalog + occasional publish flag flips — SQLite matches that load.

Content model and wiki links

Surfaces

  • Technology / feature / stack / company / DM vendor packs — filesystem profiles + catalog.db edges; wiki bands via tech-article / friends with slug autolinks.
  • Taxonomy — vocabulary pages under taxonomy; essays deepen terms such as Matryoshka embeddings, ANN indexes, RAG, Vector DBs.
  • Devices — manifests + images; linked from taxonomy device families.
  • Design × AI / vibe — article + practice trees; hubs are indexable (robots allow; sitemap lists them).
  • Blogblog.db bodies; covers under public/blog/; only visible = 1 is public (blog).
  • Infographics — published posters with wiki cites into packs and taxonomy.

Wiki linking rule

Authors write slug or backticks around a catalog slug. wikifyMarkdown resolves those to internal hrefs before marked runs. Unknown slugs stay plain text — no silent inventing of routes.

Related research in the catalog (not dependencies of this site): mxbai-embed-large, LlamaIndex, and the vector / RAG taxonomy above.

Deploy and health gates

Pipeline (deploy.sh)

  1. Rsync Next sources to the Hetzner project tree (happyoverlord).
  2. Rsync research corpus directories (technologies, devices, design, vibe, taxonomy, document management, …) with fail-closed checks for space-in-path trees.
  3. Ship blog.db, catalog.db, corpus.sqlite from localhost (blog.db backed up on the server first).
  4. pnpm install + pnpm build on the host; systemctl restart happyoverlord.
  5. Health gates: public home / robots / sitemap 200; public /intern 403; loopback intern health JSON must show retrieval_fail = 0 and smoke_fail = 0.

Intern monitor

Operators run scripts/intern-monitor.sh — SSH tunnel to 127.0.0.1:XXXX — then open content-retrieval and visitor-trace views. Public internet must keep seeing 403 on /intern (middleware).

Observability

  • Google Analytics — traffic overview (see Privacy).
  • Visitor trace — first-party SQLite: path, catalog surface, which store that page typically reads (blog.db, catalog.db, …); day-salted IP hash, no tracking cookie for that log.
  • Content retrieval check — every public entity kind must load real content (not link-only shells); counted in intern health.
  • HTTP smoke — representative public routes return expected status codes on loopback after deploy.

Legal context: Privacy · Terms. Purpose of the catalog: Why I do this….

Architecture diagram

End-to-end: authoring on localhost, deploy, single Next.js runtime, public site, server-owned traces, and the SSH tunnel into /intern.

Happy Overlord architecture

In this wiki

Catalog packs, taxonomy terms, and site surfaces linked from this page.