Skip to content

KB Site Implementation Log

Created 2026-02-27
Updated 2026-02-27
Status in-progress
Tags projectinfrastructurekb

Implementation log for the Baseworks Knowledge Base web publishing system.

Architecture: Obsidian vault → Git → Quartz v4.5.2 → Cloudflare Pages Previous architecture: Astro + Starlight (migrated away 2026-02-27 — see Phase 1 below for history) Plan doc: See the full architecture plan in the Claude Code session transcript from 2026-02-27.


The static site generator is fully configured and builds successfully.

Location: /site/ directory inside this vault repo

Key files created:

FilePurpose
site/astro.config.mjsAstro + Starlight config — sidebar, branding, remark plugins
site/package.jsonDependencies and build scripts
site/scripts/sync-content.mjsPre-build script: copies vault content into Astro, injects missing frontmatter
site/plugins/remark-obsidian-wikilinks.mjsConverts [wikilinks](/wikilinks/) to standard Markdown links
site/plugins/remark-auto-title.mjsExtracts # Heading as title if frontmatter lacks one
site/src/content/docs/index.mdxHomepage with card grid (Projects, Areas, Resources)
site/src/styles/custom.cssBaseworks brand colors, callout styling
site/src/assets/logo-light.svgLight mode logo (placeholder)
site/src/assets/logo-dark.svgDark mode logo (placeholder)
.github/workflows/deploy.ymlGitHub Actions: build + deploy to Cloudflare Pages

Build results:

  • 500 pages generated from 525 vault Markdown files
  • Build time: ~11 seconds
  • Output size: ~90MB (includes Pagefind search index)
  • Full-text search working via Pagefind

How the sync script works:

  1. Copies 00-inbox/src/content/docs/inbox/, 01-projects/projects/, etc.
  2. For each .md file without a title in frontmatter, derives one from the first # Heading or filename
  3. Converts <img src="image.png" alt="image.png" /> embeds to HTML <img> tags (avoids Astro asset pipeline errors)
  4. Runs before every astro build and astro dev

Remark plugins installed:

  • remark-obsidian-callout — renders > [!note] / > [!info] callouts
  • Custom wikilinks plugin — converts [Alias](/page/) to standard links
  • Custom auto-title plugin — fills missing frontmatter titles at build time

Vault syntax analysis (from exploration):

  • 651 wikilink instances (handled by plugin)
  • 39 callout instances (handled by plugin)
  • 2 image embeds (handled by sync script)
  • Frontmatter: ~90% of files have it, common fields: created, updated, tags, status

Deployment & Infrastructure (2026-02-27, session 2)

Section titled “Deployment & Infrastructure (2026-02-27, session 2)”

Phase 1: Deploy to Cloudflare Pages — COMPLETE

Section titled “Phase 1: Deploy to Cloudflare Pages — COMPLETE”

All deployment infrastructure was set up via Cloudflare API using the Global API Key from the existing cloudflare-auto-purge.php mu-plugin.

Cloudflare Pages project:

  • Project name: baseworks-kb
  • Default URL: baseworks-kb.pages.dev
  • Custom domain: kb.baseworks.com
  • Production branch: master

DNS:

  • CNAME record: kbbaseworks-kb.pages.dev (proxied, Cloudflare)

GitHub Actions workflow (.github/workflows/deploy.yml):

  • Triggers on push to master + manual dispatch
  • Auth: Global API Key via CLOUDFLARE_API_KEY + CLOUDFLARE_EMAIL env vars
  • Three GitHub repo secrets configured:
    • CLOUDFLARE_ACCOUNT_ID5248b6291f647a77fa6852b2640a1899
    • CLOUDFLARE_API_KEY — Cloudflare Global API Key
    • CLOUDFLARE_EMAILaccounts@baseworks.com
  • Build time: ~1m10s (includes npm install, content sync, Astro build, Pages deploy)

Cloudflare Access (Zero Trust):

  • Application: “Baseworks KB” (self-hosted)
  • Protected domain: kb.baseworks.com
  • Policy: Allow accounts@baseworks.com via email OTP
  • Session duration: 30 days

The homepage links (/areas/, /projects/, etc.) returned 404 because no index pages existed for section roots. Fixed by updating sync-content.mjs to auto-generate index.md for each section with links to all subdirectories and pages within.

Overrode Starlight’s ThemeProvider component (site/src/components/ThemeProvider.astro) to default to light mode instead of dark. Users can still toggle to dark via the theme selector. The only change from the original: flipped the fallback from 'dark' to 'light' when no stored preference and no OS dark-mode preference.

Additional files created/modified this session:

FilePurpose
site/src/components/ThemeProvider.astroLight-mode-default override of Starlight’s ThemeProvider

Migration: Astro/Starlight → Quartz (2026-02-27, session 3)

Section titled “Migration: Astro/Starlight → Quartz (2026-02-27, session 3)”

Astro/Starlight had fundamental compatibility issues with Obsidian:

  • Broken wikilinks (Starlight can’t resolve vault-aware shortest paths)
  • No frontmatter properties display
  • No backlinks or graph view
  • Sidebar structure didn’t match Obsidian folder hierarchy
  • Required custom sync script, wikilink plugin, auto-title plugin, callout package

Quartz v4 handles all of these natively — it’s purpose-built for Obsidian vaults.

Approach: CI-only Quartz (vault stays clean)

Section titled “Approach: CI-only Quartz (vault stays clean)”

The vault repo stays exactly as-is. Only 4 small config files are added to the root. In CI, Quartz is cloned fresh, vault content is symlinked in, and the site is built and deployed. No Quartz source code lives in the vault.

New files added to vault root:

FilePurpose
quartz.config.tsSite config: title, colors, plugins, ignorePatterns
quartz.layout.tsLayout: sidebar explorer, graph, backlinks, dark mode toggle, search
index.mdHomepage with wikilinks to PARA sections
scripts/dev-quartz.shLocal dev helper: clones Quartz, copies config, symlinks vault, starts dev server

Modified files:

FileChange
.github/workflows/deploy.ymlReplaced Astro build with Quartz CI pipeline
.gitignoreAdded .quartz-dev/, public/

What Quartz replaces (no custom code needed)

Section titled “What Quartz replaces (no custom code needed)”
Was custom in AstroQuartz handles natively
sync-content.mjs (copy + inject frontmatter)Reads vault directly, derives titles
remark-obsidian-wikilinks.mjsObsidianFlavoredMarkdown plugin
remark-auto-title.mjsBuilt-in title derivation
remark-obsidian-callout packageObsidianFlavoredMarkdown plugin
Auto-generated section index pagesFolderPage emitter
ThemeProvider overrideDarkmode component + post-build localStorage script
None (not possible)Backlinks, Graph view, hover previews
  • 499 files parsed, 784 files emitted
  • Build time: ~11-14 seconds
  • Output size: ~30MB
  • Quartz version: v4.5.2 (pinned in workflow env and dev-quartz.sh)
  • Node.js: 22 (upgraded from 20)
  • ignorePatterns: templates/, .obsidian/, site/, patrick-oancia/, setup docs, config files themselves
  • Brand colors: Ported from existing custom.css (#3a5bd9 accent, #182654 dark headings)
  • Light mode default: Post-build localStorage injection (sets theme=light only if user hasn’t toggled yet)
  • noindex: Post-build <meta> injection (belt-and-suspenders with Cloudflare Access)
  • Link resolution: shortest (matches Obsidian’s default behavior)
  • fetch-depth: 0: Full git history for accurate “last modified” dates
  • Config files copied (not symlinked): TS imports use relative paths that must resolve from Quartz root
TagDescription
backup/pre-quartz-migrationState before any Quartz changes
backup/quartz-local-verifiedAfter local build verified
backup/quartz-workflow-readyAfter deploy.yml replaced
Branch: archive/astro-starlightFull Astro setup preserved (created before site/ deletion)

After confirming Quartz deploy works on kb.baseworks.com, delete the old Astro/Starlight site/ directory. The archive/astro-starlight branch preserves it.

Phase 2: Tina CMS (web editing) — NOT STARTED

Section titled “Phase 2: Tina CMS (web editing) — NOT STARTED”
  • Tina CMS works with any static site generator that reads Markdown from git
  • Vault markdown files stay at same git paths, so Tina compatibility is unchanged
  • Set up self-hosted Tina backend on Agents VPS (167.235.236.99)
  • Docker container with GitHub App/PAT for repo access
  • Reverse proxy at tina.baseworks.com or similar

Phase 3: Agent integration — NOT STARTED

Section titled “Phase 3: Agent integration — NOT STARTED”
  • GitHub fine-grained PAT for n8n
  • n8n workflow templates for doc operations
  • OpenClaw GitHub API access for doc updates

Terminal window
cd ~/Obsidian/baseworks-kb-shared-brain
# First time (clones Quartz, installs deps, starts dev server)
bash scripts/dev-quartz.sh
# Subsequent runs (Quartz already cloned)
bash scripts/dev-quartz.sh

Dev server runs at http://localhost:8080


  1. CI-only Quartz: Quartz source code is never committed to the vault repo. In CI and local dev, Quartz is cloned fresh and vault content is symlinked in. This keeps the vault clean.

  2. Config files copied, not symlinked: The quartz.config.ts and quartz.layout.ts files use relative TS imports (./quartz/plugins) that must resolve from the Quartz repo root. Symlinks resolve imports from the link target (vault root), so files are copied instead.

  3. Light mode via localStorage injection: Quartz’s prescript checks localStorage("theme") then falls back to OS preference. A post-build script injects localStorage.setItem("theme","light") only if no preference is stored, so first-time visitors see light mode while returning users keep their choice.

  4. Global API Key for GitHub Actions: Reuses the same Cloudflare credentials from the cloudflare-auto-purge.php mu-plugin.

  5. Full git history checkout: fetch-depth: 0 ensures Quartz can read actual git commit dates for “last modified” metadata.


Quartz v4.5.2 (cloned at build time, not committed to repo). No package.json in the vault itself — all dependencies are Quartz’s.