How it is built
Boring infrastructure, arranged carefully
Postgres, a Next.js app, object storage and a forked editor. What makes it hold together is the direction the dependencies are allowed to point, and the fact that a script checks.

The seam
The platform knows nothing about your content model
There is no page, post or callout anywhere in the platform packages. What lives there is everything true of any schema: the transport, the cache tags, the image URL arithmetic, the Portable Text primitives.
The example schema and the queries that match it live in the app, because that is where yours would live. Replacing the example is a matter of deleting one package's contents, not of unpicking the platform.

The packages
| Package | What it owns | May import |
|---|---|---|
content-core | Pure domain: draft semantics, patches, transactions, constraints, GROQ planning, text extraction, the environment contract | Nothing at all |
content-store | Postgres: documents, transactions, revisions, GROQ execution, realtime | content-core |
content-auth | Supabase Auth, profiles, roles, grants, RLS identity | content-core |
content-assets | Blob bytes, metadata, transform URLs | content-core |
content-api | The Sanity-compatible HTTP surface, as pure functions | the four above |
content-next | The Next.js adapter — the only package that may import next | content-api and friends |
content-studio | The forked Studio | nothing of ours |
demo-studio | The example schema, structure and templates | content-studio |
cli | init and login | content-core |
Four rules are enforced by a script rather than by convention: content-core has no dependencies at all, content-api never imports next, only content-next does, and the Studio packages may not import a server package. A new package with no entry in the rule table fails the build — so the graph cannot drift quietly.
What a request actually does
A read from the public site, end to end. Every step is in the repository and none of it is hidden behind a service.
browser → GET /docs/content-api/api-overview (Next.js route, Node runtime)
→ contentQuery() (cached fetch, tagged with sync tags)
→ GET /api/v2026-07-26/data/query/production (this same deployment)
→ content-api router (pure Request → Response)
→ GROQ planner (predicates Postgres can evaluate)
→ Postgres (RLS applies; anonymous ⇒ published only)
→ groq-js (full expression, DataLoader for ->)
→ response envelope { result, ms, syncTags }With the cache warm, the middle six lines do not happen at all: the read is served from Next’s data cache until a publish purges the tag it was stored under.
Read the specification
The repository carries a full specification and a decision log that records why each choice was made — including the ones that were reversed.
