Vercel Deployment Runbook

The engineering wiki is live as a static Next.js site (site/) on Vercel at https://madsenjake0.wiki. Every push to main triggers an automatic rebuild of the public docs surface.

Design Rationale

The wiki is deployed as a fully static export (output: "export" in site/next.config.js) rather than a server-rendered Next.js app. This choice eliminates the need for a serverless runtime on Vercel, reduces cold-start latency to zero, and means the deployment is just a directory of HTML files served from a CDN. Since wiki content only changes on pushes to main, there is no benefit to server-side rendering — the content is known at build time.

The alternative of serving wiki content through the FastAPI backend was considered and partially implemented (the server/wiki.py API), but it requires a running server and cannot serve the public audience without infrastructure beyond a static CDN. The two surfaces coexist: Vercel serves the public-facing read-only wiki, while the FastAPI API provides richer capabilities (health checks, commit history, source viewing, staleness detection) for local engineering workflows.

Using vercel.json with explicit installCommand, buildCommand, and outputDirectory instead of Vercel's framework auto-detection was a deliberate workaround for monorepo structure. Vercel's auto-detection looks for next in the root package.json, but Lorewright's root has no package.json — the Next.js app lives in site/. Explicit configuration avoids this mismatch entirely and makes the build contract visible in version control.

Assumptions & Constraints

  • Build-time-only wiki reading. site/lib/wiki.ts reads wiki/*.md files at build time using Node.js fs — there is no runtime data fetching. If the wiki directory is missing or empty at build time, the site builds with no pages and no error. This is a silent failure mode worth watching for.
  • Single-branch deployment model. The custom domain (madsenjake0.wiki) only follows the production deployment, which tracks main. PR preview deployments get Vercel-generated URLs but not the custom domain. This means you cannot preview wiki changes on the real domain before merging.
  • GitHub blob links are hardcoded to main. The GITHUB_BLOB constant in site/lib/wiki.ts and site/app/wiki/[slug]/page.tsx assumes the default branch is main. If the repository's default branch were ever renamed, all @file: reference links would break silently (they'd 404 on GitHub, not at build time).
  • No incremental static regeneration. Because the export is fully static, every deploy rebuilds every page. For the current wiki size (~30 pages) this is fast (<30 seconds), but it means build time scales linearly with page count.
  • Sanitization is the security boundary. The rendering pipeline uses rehype-sanitize with a customized schema that only allows specific CSS classes (wiki-link, broken-link, source-ref, hljs-* for syntax highlighting). Raw HTML in wiki markdown is processed through rehype-raw but then sanitized — this permits <a> tags from wiki-link resolution while blocking injection.

Conceptual Model

The deployment architecture draws a clear boundary between build-time content resolution and runtime serving:

Author time:     wiki/*.md  (frontmatter + markdown + double-bracket links + @file: refs)
                      ↓
Build time:      site/lib/wiki.ts resolves links, renders HTML
                      ↓
                 site/out/  (static HTML, CSS, JS)
                      ↓
Deploy time:     Vercel CDN edge cache (global distribution)
                      ↓
Runtime:         Browser fetches pre-built HTML (no server logic)

All complexity lives at build time. The wiki.ts module is effectively a compiler: it reads a custom markup language (markdown with wiki-link and file-reference extensions), resolves references against the current page set, and emits HTML. Broken links are rendered with a broken-link CSS class rather than failing the build, which means the build always succeeds but may produce pages with visually flagged broken references.

The relationship between the two wiki surfaces (Vercel static site vs. FastAPI API) is read-only fork — they read the same wiki/*.md source but render independently. The Vercel surface is optimized for public browsing (pre-built HTML, CDN delivery, syntax highlighting). The FastAPI surface is optimized for engineering tooling (structured JSON responses, health checks, backlink graphs, staleness detection). Neither surface writes to wiki files; authoring happens through direct file edits and git commits.

Architecture

wiki/*.md  →  site/ (Next.js build)  →  site/out/  →  Vercel CDN  →  madsenjake0.wiki
  • site/lib/wiki.ts reads all wiki/*.md at build time, resolves double-bracket wiki links and `@file:` references, renders HTML via unified (remark + rehype + rehype-highlight).
  • site/next.config.js uses output: "export" + trailingSlash: true for pure static HTML with no serverless runtime.
  • vercel.json at the repo root sets the install command, build command, and output directory explicitly and avoids both framework auto-detection and legacy builders.
  • @file: references link to https://github.com/icosahedron10/lorewright/blob/main.

Public Surface vs API-Only Surface

The Vercel deployment is the public, static browsing surface only. It does not expose FastAPI-only wiki capabilities such as:

  • GET /api/wiki/health
  • GET /api/wiki/commits
  • GET /api/wiki/source with historical file and line-range viewing
  • page toc, backlink/source metadata, and staleness fields returned by GET /api/wiki/pages/{slug}

Those capabilities remain available from the FastAPI wiki API in server/wiki.py for local engineering workflows and automated checks.

Current Deployment State (as of 2026-04-15)

ItemStatus
vercel.json configured
site/ Next.js app
trailingSlash: true
framework: "nextjs" removed from vercel.json✅ (was causing "No Next.js version detected" error)
highlight.js CSS imported
Vercel project created + GitHub connected
Domain madsenjake0.wiki serving production traffic
Automatic deploys from main

Routine Validation Before Shipping Docs

Run the static-site build locally before pushing documentation changes:

npm --prefix site run build
pytest tests/test_wiki_api.py -q

Triggering a Redeploy

Redeployment is automatic on every push to main. To force a redeploy without a code change:

  • Vercel dashboard → project → Deployments → pick latest → Redeploy

Domain or Project Reconfiguration

If the domain or Vercel project needs to move, keep the current production behavior:

  1. Import icosahedron10/lorewright in Vercel.
  2. Leave the root directory at /; vercel.json handles install/build/output for the monorepo.
  3. Reattach madsenjake0.wiki in Settings → Domains.
  4. If DNS must be recreated, use the Vercel-provided apex/domain records and wait for TLS reprovisioning.

Adding or Updating Wiki Pages

  1. Edit or create wiki/{slug}.md with required frontmatter (see Decision: Wiki Maintenance Contract)
  2. Update <a href="/wiki/index" class="wiki-link">Lorewright Wiki</a>, <a href="/wiki/log" class="wiki-link">Wiki Log</a>, and source_paths on any touched pages when the wiki contract requires it
  3. Commit and push to main
  4. Vercel rebuilds automatically — the public page is usually live in under a minute

No server restart or manifest rebuild step is needed; the Next.js build reads wiki/ fresh every time.

Troubleshooting

Build fails with "No Next.js version detected" vercel.json must NOT contain "framework": "nextjs". Vercel looks for next in the root package.json when this key is present; our root has no package.json. Remove the key.

404 on the deployment root (/) even when Vercel marks the deploy ready Use explicit top-level installCommand, buildCommand, and outputDirectory in vercel.json. Avoid legacy builds-based configs for this monorepo static export — they can produce a "ready" deployment that does not serve the generated site/out files at the project root.

404 on direct page load (e.g. /wiki/architecture) Ensure trailingSlash: true is set in site/next.config.js. Without it, Next.js emits flat .html files that Vercel cannot resolve from a bare path.

Preview works but the production domain still 404s The custom domain only follows the production deployment, not the PR preview deployment. Merge the production fix or switch the Vercel project's Production Branch to the branch containing the fixed vercel.json, then trigger a redeploy.

Wiki content stale after push Check the Vercel deploy log. If the build passed but content looks old, hard-refresh the browser. Vercel CDN caches are invalidated on each deploy.

Broken wiki links (red strikethrough in UI) Run the FastAPI-only GET /api/wiki/health route locally to get a health report listing broken links, orphan pages, and stale source references. Fix the offending markdown and push.

@file: links point to wrong branch GITHUB_BLOB is hardcoded to main in both site/lib/wiki.ts:42 and site/app/wiki/[slug]/page.tsx:5. If the default branch changes, update both constants.