Open source core with room for hosted and SaaS layers on top.
HTMLess architecture for builders who want clarity
This page turns the architecture document into a readable system map: three API surfaces, a PostgreSQL source of truth, Redis for cache and queues, Docker-first deployment, and a monorepo split across admin, API, and worker packages.
Architecture Tabs
Explore the system from the angle you care about
The tabs below are pulled from the architecture document itself, then reorganized into overview, APIs, data model, infrastructure, and codebase views.
The API runtime is in the monorepo core package, backed by PostgreSQL.
The editing interface lives as a separate app instead of being mixed into the API.
Used for caching, queueing, retries, and rate-limit state.
Shared database with scope-based isolation around spaces, roles, and content.
Admin, API, worker, PostgreSQL, and Redis are all part of the deployment story.
UX over feature count
HTMLess is inspired by WordPress-style authoring ergonomics, but without server-rendered theme assumptions. The architecture is meant to make the path from schema to published API feel clean, not overloaded.
- Schema
- Content
- API
- Preview
- Publish
Separate concerns, clearer operations
The architecture avoids a one-bucket CMS model. Editors hit CMA, public clients hit CDA, preview uses short-lived access, the worker owns retries, and the database remains the canonical truth.
- Cleaner mental model for developers and agencies
- Safer publish semantics than a single all-purpose API
- A solid base for Phase 9 delivery optimization
/cma/v1
Write and read surface for editors, admin UI, and automation. Returns drafts, metadata, and versions.
Write / Read/preview/v1
Draft-inclusive reads protected by preview tokens so teams can review unpublished changes.
Draft Reads/cda/v1
Read-only, published-only, cacheable delivery for public websites, apps, and frontends.
Read-onlyThree external surfaces, strict behavior
The architecture doc treats the three APIs as contract boundaries, not just route prefixes. That is why preview can be draft-inclusive, CDA can stay public-facing and cacheable, and CMA can remain editor-first.
- CMA returns draft state, metadata, and versioning detail
- Preview focuses on safe draft review with scoped access
- CDA stays published-only and CDN-friendly by design
Where the APIs connect
CMA writes into the content store and emits events. Preview reads drafts. CDA serves published state. The worker consumes async jobs and pushes signed webhooks with retries.
- Content store and entry state sit behind the APIs
- Event bus decouples publish actions from side effects
- Webhook dispatcher owns delivery attempts and status
Spaces, users, roles, bindings
Spaces define the tenant boundary. Users, roles, and role bindings scope access within each space.
Content types + fields
Content models live in the database so schemas can evolve without requiring code redeploys.
Entries, versions, state pointers
Stable entry identity, immutable snapshots, and draft/published pointers define content history and publishing.
API + preview tokens
Machine tokens and short-lived preview tokens enforce scoped access across public and editorial workflows.
Assets and asset usages
Media metadata is tracked separately from content, with references recorded for better visibility and control.
Webhooks and audit logs
Outbound integrations and security-sensitive actions are captured instead of disappearing into the background.
Traefik
SSL termination and routing sit in front of the admin and API services.
htmless-admin
Next.js admin application served separately from the backend runtime.
htmless-api
Node.js service exposing CMA, CDA, and Preview APIs.
htmless-worker
Handles jobs, retries, and webhook dispatch without blocking the API layer.
PostgreSQL
Relational datastore for tenancy, schema, content, assets, webhooks, and audit logs.
Redis
Cache, queues, invalidation, and rate limiting all live here.
packages/core
Route handlers, auth, spaces, schema registry, content lifecycle, media handling, events, webhooks, cache helpers, and utilities.
packages/worker
Jobs, queues, and runners that keep retryable background work out of the request path.
packages/admin
App router, components, feature modules, and client helpers for the editorial interface.
Top-level project layout
htmless/ ├── docs/ ├── packages/core/ ├── packages/worker/ ├── packages/admin/ ├── docker-compose.yml ├── Dockerfile.api ├── Dockerfile.admin ├── Dockerfile.worker ├── package.json └── pnpm-workspace.yaml
Important source folders
src/ ├── api/ cma/, cda/, preview/ ├── auth/ login, tokens, middleware ├── spaces/ tenant scoping ├── schema/ type registry, validation ├── content/ CRUD, versions, publish flow ├── media/ uploads and metadata ├── events/ internal emitters ├── webhooks/ signing and dispatch policy └── cache/ keys and invalidation
Flows
Slides for the parts that matter most
These horizontal flows turn the architecture doc into scan-friendly steps for editors, developers, and anyone trying to understand how the system actually behaves.
Notes
Architecture principles worth remembering
PostgreSQL is the source of truth
The architecture is intentionally SQL-first even as Phase 9 pushes delivery closer to document-style speed.
The APIs are product boundaries
CMA, CDA, and Preview are not just technical routes. They represent different behaviors, trust levels, and cacheability rules.
Worker isolation matters
Webhooks and retries belong in the async layer so API responsiveness is not tied to downstream services.