Architecture
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:
| Mode | URL | Content |
|---|---|---|
| prod | /getting-started/introduction | pinned production SHA |
| tree | /tree/main/getting-started/introduction | latest content commit on the branch |
| blob | /blob/<sha>/getting-started/introduction | immutable 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 server render, comark-docs first checks a connected Vercel Global Config store for a contentSha value. When the value exists, every production content read is pinned to that commit. You can use this override to hold production on a reviewed version or roll content back without changing the production branch. See Pin production content for setup and cache timing.
Without a contentSha value, 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. If Global Config is unavailable, comark-docs also falls back to this branch-based resolution.
The Global Config pin applies only to the production Vercel environment. Preview deployments continue to follow their target branch, and local development reads from your working tree.
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.
When no Global Config pin is active, code-only commits don't move the content SHA, so they don't invalidate anything.
Cache tiers
- ISR — rendered HTML is cached at the edge. Pages expire after
comarkDocs.isrseconds (default 300) or when a push purges them. - 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.
- 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.
Markdown for agents
Every production documentation page is mirrored as raw Markdown at /raw/<path>.md (versioned previews serve HTML only). The mirrors carry the same ISR caching as the HTML pages.
On Vercel, agents don't need to know the mirror URLs. The layer injects rewrites into the build output, ahead of the ISR cache:
- A request for any page URL with
Accept: text/markdown(or a curl user-agent) is rewritten to its/raw/**mirror. - A request for
/is rewritten to/llms.txt.
Because the rewrite happens at the routing layer, the HTML and Markdown variants are cached under different paths and can't poison each other's cache entries.
A request for a page that doesn't exist returns a real HTTP 404 with a short Markdown body pointing at /llms.txt, /llms-full.txt, and the sitemap, so an agent that guesses a URL wrong can recover.
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.