The repository is a pnpm workspace driven by Turborepo. Every app is a Worker you can run locally with Wrangler.
Layout
| Path | What | Local port |
|---|---|---|
apps/web | Nuxt editor, Panda CSS design system | 3000 |
apps/api | Hono API | 4000 |
apps/realtime | Yjs rooms | 8788 |
apps/sites | Published sites, <slug>.localhost:8790 | 8790 |
apps/runner | Server workflows, tables, visitor auth | 8791 |
apps/mcp | MCP server | 8792 |
packages/* | Shared libraries, imported from source | — |
docs/ | Architecture, deploy guide, agent process | — |
Set up
Install
Use the pnpm version pinned in
package.json.bashpnpm install # also runs nuxt prepare + panda codegen for web cp apps/api/.dev.vars.example apps/api/.dev.vars cp apps/sites/.dev.vars.example apps/sites/.dev.vars cp apps/realtime/.dev.vars.example apps/realtime/.dev.vars cp apps/mcp/.dev.vars.example apps/mcp/.dev.vars # fill in DATABASE_URL (a Neon dev branch) and the shared keysMigrate the database
Apply
packages/db/migrationsto your dev branch withpnpm --filter @malla/db db:migrateandDATABASE_URLset.Run
bashpnpm dev # every Worker and the editor through turbo pnpm --filter web dev # just the editor pnpm --filter api dev # just one Worker
Local sandbox
pnpm sandbox runs the whole stack (Postgres, api, realtime, sites, runner and the editor) with no .dev.vars and no DATABASE_URL to set up. Postgres is PGlite, the migrations in packages/db/migrations are applied on start, and dev-only secrets are generated once. Data persists between runs; press Ctrl+C to stop.
pnpm sandbox # start; data persists between runs
pnpm sandbox --reset # wipe the sandbox data first, then start
| Service | URL |
|---|---|
| Editor | http://localhost:3200/login |
| API | http://localhost:4200/health |
| Realtime | ws://localhost:8988 |
| Sites | http://<slug>.localhost:8990 (after publishing) |
| Runner | http://localhost:8991/health |
| Postgres | postgres://postgres@127.0.0.1:5599/postgres |
Sign in as demo@malla.local with the password demo-password-123. The first boot creates that user, its workspace and a site called "Demo site" with the slug demo. The ports stay clear of pnpm dev and the e2e stack, so all three can run at once. The sandbox does not start the MCP Worker, and it has no email or AI keys: it sends no email (so no sign-in links) and the built-in AI is off. Everything it generates lives in scripts/sandbox/.data/; scripts/sandbox/README.md has the port overrides and the details.
Quality gates
pnpm check-types # TypeScript across the monorepo
pnpm test # Vitest unit tests in every package
pnpm --filter web build # production build of the editor
pnpm e2e # Playwright against the full local stack
graphify update . # refresh the code knowledge graph
The e2e suite starts its own stack in a per-checkout state directory, so parallel worktrees never collide, and keeps a screenshot per test in apps/web/e2e/.screenshots/.
Conventions
- Vue 3
<script setup lang="ts">, strict TypeScript, explicit imports (auto-imports are off). - Editor UI is styled with Panda CSS using semantic tokens only; motion goes through
app/motion(GSAP). - All document changes go through ops and
editor.document.transact; never mutate the doc directly. - Small Conventional Commits, one logical change each. Full rules in
docs/frontend-conventions.md.