fogserv.cloud Architecture Overview

Status: Active
Last Updated: 2026-08-26
Category: fogserv - Project Reference
Prerequisites: ../frontend/tanstack.md, ../databases/prisma-connections.md
Time: 30 min read
Tags: fogserv, architecture, tanstack-start, prisma, sqlite, k3s, bun, react19

Summary

How the fogserv.cloud codebase is put together: a TanStack Start (React 19 + Vite) SSR site with all server logic centralized in src/server/api.ts, a Prisma + SQLite data layer, Bun as the runtime and package manager, shipped as a single container image (ghcr.io/fogserv/fogserv-cloud) to a k3s cluster behind Caddy ingress.

Context / Why This Matters

This is the map for everything else in this section: before touching code, deploying, or debugging, know which layer owns which responsibility. The repo lives at git.shire.one/fogserv/website; generic patterns are covered by the linked KB sections, while this article records only what's true of this stack.

Implementation / Core Content

Stack at a glance

Layer Choice Where
Runtime / package manager Bun 1 Dockerfile, bun.lock
Framework TanStack Start (SSR) + React 19 app.config.ts, src/routes/
Styling Tailwind CSS v4 + PostCSS postcss.config.js, tailwind.config.ts
Data Prisma ORM → SQLite prisma/schema.prisma, DATABASE_URL
Server logic Single module src/server/api.ts auth, posts CRUD, email, webhooks
Email Mailgun send + signed webhook ingestion src/server/api.ts
CI/CD GitHub Actions → GHCR → cluster .github/workflows/, Enterprise/apps/
Hosting k3s Deployment + Caddy ingress Enterprise/apps/fogserv-cloud.yaml

Request flow

browser ──► Caddy ingress (TLS) ──► Service :80 ──► pod :8080 (bun preview, SSR)
                                                        │
                                                        ├─ src/routes/*.tsx  (UI)
                                                        ├─ src/server/api.ts (auth/CMS/email logic)
                                                        └─ Prisma ── SQLite (file DB)

Key directories

Build & run pipeline

  1. bun install --frozen-lockfile
  2. bunx prisma generate
  3. bun run build — Vite build + prerender; KB markdown loaded via import.meta.glob('/kb/**/*.md', { query: '?raw' })
  4. Production image runs bun run preview --host 0.0.0.0 --port 8080

Note vite.ssr.external: ['@prisma/client'] in app.config.ts — Prisma must stay external to SSR bundling or the generated client breaks at runtime.

Security posture (already hardened)

Practical Examples

"Where do I add an API endpoint?"src/server/api.ts. Keep route components thin; anything touching Prisma, email, or secrets belongs there.

"Where does content come from?" — Blog/changelog posts live in SQLite via the CMS; KB articles are static markdown under kb/, compiled in at build time. Changing KB content requires an image rebuild + redeploy, not just an edit.

Common Pitfalls & Troubleshooting

Next Steps / Ops Actions

Sources & Related Articles

Change Log

2026-08-26

Choose Theme

Your selection is saved locally.

Neural Cacophony
Aperture v2
Flux v1
Mosaic Chaos
Nexus v1
Nexus Zest
Prism v2
Synapse