Malla is a Turborepo monorepo of six Cloudflare Workers and a set of shared packages. Everything runs on Cloudflare: Workers, Durable Objects, R2, KV, Queues, Workflows and Hyperdrive in front of Neon Postgres.
text
┌── Cloudflare for SaaS (customer domains + SSL)
visitor ──► sites Worker ──── KV (host → site, publish id)
│ R2 (snapshots, compiled CSS, assets)
│ Cache API (rendered pages)
└─ /api/*, /_auth/* ──► runner Worker ── SiteDatabase DO (tables, visitors)
editor ──► web Worker (Nuxt) ──► api Worker (Hono) ── Hyperdrive ──► Neon Postgres
│ ├─ R2 (uploads) ├─ AI Gateway
├─► canvas frames (sandboxed) └─► sites (publish), runner (test runs)
└─► realtime Worker: Durable Object per page (Yjs + presence)
The Workers
webThe Nuxt editor and marketing pages. Proxies /api/** to the api over a service binding.
apiHono REST API: auth, sites, pages, CMS, forms, integrations, billing, domains, AI.
realtimeOne Durable Object per page: Yjs sync, presence, checkpoints to Postgres.
sitesPublishes snapshots and serves every published site, form and client query.
runnerServer workflows, crons, durable runs, native tables and visitor auth.
mcpThe MCP server for agents, authenticated with workspace API keys.
Six principles
- One document format everywhere. Pages, components and the canvas scratch area share the
PageDocshape. - One renderer. The canvas and the published site run the same
@malla/renderer, so what you see is what ships. - One workflow engine. Client and server workflows share a JSON format and
@malla/engine, with different adapters. - Built-ins are plugins. REST, GraphQL and the CMS use the same plugin SDK as every other first-party connector.
- Never
evaluser input. Expressions are interpreted from an AST; custom code runs in isolates. - Secrets never reach the browser. Authenticated integration calls are proxied server-side.
From a gesture to the database
GestureA drag, a field edit, an AI tool call.
OpsTyped, validated operations.
YjsApplied to the Y.Doc, undoable.
RoomSynced through the page’s Durable Object.
PostgresCheckpointed as JSON + Yjs state.
See The page document and Realtime.
Where data lives
| Data | Store |
|---|---|
| Users, workspaces, sites, pages, CMS, forms, integrations, workflows, billing | Neon Postgres (Drizzle, via Hyperdrive) |
| Live page documents and edit logs | Durable Object SQLite per page, checkpointed to Postgres |
| Native tables, visitors, sessions, form submissions | SiteDatabase Durable Object per site |
| Uploaded assets, published snapshots, rendered pages | R2 |
| Host routing, published server workflows, entitlements cache | KV |
| Usage and analytics | Analytics Engine |
Shared packages
| Package | Role |
|---|---|
@malla/schema | Types and Zod schemas: PageDoc, ops, styles, data, workflows, CMS, tables, plans |
@malla/expr | Expression parser and interpreter, built-in functions and autocomplete metadata |
@malla/engine | Workflow engine and action registry for the browser and the server |
@malla/renderer | Document → HTML/CSS, canvas frame runtime, interactions |
@malla/runtime | The islands runtime published sites load for bindings, forms and workflows |
@malla/publish | Snapshot publishing and on-demand rendering |
@malla/ydoc | Yjs mapping of documents |
@malla/plugin-sdk, @malla/plugins | Plugin definitions and the first-party plugins: REST, GraphQL, CMS, Postgres, Supabase, Airtable, Google Sheets, Resend, Anthropic and OpenAI |
@malla/bundle | Site bundles for templates, sections and copy between sites |
@malla/ai-tools | AI and MCP tool schemas that compile to ops |
@malla/db, @malla/auth-token | Drizzle schema and migrations; signed tokens between Workers |