9d9

Concepts

The two halves of 9d9: static hosting and headless CMS. How they fit together.

9d9 has two halves. The static-site host is the boring one — you build with any framework, push the files, we serve them. The headless CMS is the interesting one — it's the place to keep content that changes between builds (pages, navigation, brand) so your AI agent (or you) can edit it without redeploying.

Static hosting

Every deploy is an atomic snapshot of a dist/ directory. Files live in R2 under a deploy_id prefix. A small KV entry per hostname points at the current deploy_id. When you activate a deploy we flip that pointer; old files stay around for a week so rollback is one tool call.

Request path

  1. Browser hits your-slug.9d9.dev/about.
  2. Render Worker reads host:your-slug.9d9.dev from KV — gets {org_id, current_deploy_id}.
  3. Tries R2 keys in order: about/index.html, about.html, then about.
  4. First hit is served. If none, returns the deploy's 404.html (or a generic).

Cache headers

File classcache-control
HTML (*.html, /)public, max-age=0, must-revalidate + ETag
Hashed (/_astro/, /_next/static/, *.HASH.*)public, max-age=31536000, immutable
Everything elsepublic, max-age=300, stale-while-revalidate=86400

Headless CMS

Your built site is static, but most sites have some content that changes more often than your codebase deploys. A page edit. A nav reorder. You shouldn't have to push a new build to fix a typo.

The CMS API is read-mostly from your site's perspective — you fetch content at build time, runtime, or both, and render it however you want. Writes happen from the dashboard, the CLI, the MCP server, or directly via REST with a Bearer token.

Content types

  • Pages — slug, title, markdown body, structured blocks, optional pub_date + tags for time-ordered content (blog, changelog, news).
  • Navigation — a primary nav list and a footer nav list. Strings + URLs.
  • Brand — name, tagline, palette, fonts, logos, social, address.
  • Media — images and other assets, with on-the-fly resize.

Read endpoints (no auth)

Your static site fetches these. They're CORS * and edge-cached for 60s.

GET /v1/sites/{org_id}/public/pages
GET /v1/sites/{org_id}/public/pages/{slug}
GET /v1/sites/{org_id}/public/navigation
GET /v1/sites/{org_id}/public/brand

Two flows for "deploy"

What changedWhere to push
Markup, styles, JS, build pipeline9d9 deploy ./dist
A typo on a page, a nav reorder, a new blog entryDashboard or CMS API — no rebuild required

If your site fetches at build time, content changes need a rebuild + deploy. If it fetches at runtime, content changes are instant. Both patterns work — pick whichever fits the freshness budget.