Architecture

How content is served at request time, pinned to a commit, cached, and revalidated on push.

Most docs frameworks parse Markdown at build time and bundle the result. comark-docs does it at request time instead: pages are fetched from GitHub, parsed on demand, and cached aggressively. This page explains the moving parts.

Serving modes

Every page exists in three modes, selected by the URL:

ModeURLContent
prod/getting-started/introductionpinned production SHA
tree/tree/main/getting-started/introductionlatest content commit on the branch
blob/blob/<sha>/getting-started/introductionimmutable commit preview

Production and previews use the same rendering pipeline — see Versioned previews for the tree and blob modes.

In development, none of this applies: content is read straight from your working tree with hot reload.

Pinned to a commit

Production doesn't read "whatever is on main right now." On each request, the server resolves the latest commit touching the content directory on the production branch (a shared, 60-second-TTL cache keeps this to about one GitHub call per minute) and pins every read to that immutable SHA.

Pinning buys two things:

  • Consistency — a request never mixes files from two commits, even mid-push.
  • Cacheability — content at a SHA can never change, so parsed pages are cached hard.

Code-only commits don't move the content SHA, so they don't invalidate anything.

Cache tiers

  1. ISR — rendered HTML is cached at the edge. Pages expire after comarkDocs.isr seconds (default 300) or when a push purges them.
  2. Runtime cache — parsed Markdown bodies and the content manifest, keyed by parser version and content SHA. Because SHAs are immutable, these entries survive deployments and are shared across preview modes.
  3. GitHub — the source of truth. Only hit on cold caches, and always at a pinned SHA.

A cold page costs one GitHub lookup for the SHA, one manifest read, and one page parse. A warm page costs nothing — it's served from the edge.

Revalidation on push

When you push to the production branch, a GitHub webhook calls /api/revalidate:

The handler verifies the webhook signature with WEBHOOK_SECRET, resolves the new content SHA, diffs the file manifests to find affected pages, and purges exactly those from the ISR cache. The next request renders from the new commit — typically live within seconds of the push.

Without the webhook, the site still updates: ISR entries expire on their own after the isr window. The webhook just makes it immediate.

No redeploys for content

Since content never ships in the build, a content-only push doesn't need a deployment at all. On Vercel, an Ignored Build Step cancels builds for pushes that only touch content/ — the webhook handles those. Code pushes build and deploy as usual.