Hosted headless CMS + managed renderer for KOOMPI Cloud. OpenClaw is the AI head that drives it.
A restaurant owner in Phnom Penh sends /start to a Telegram bot. They answer a few questions. Their website is live at salabanh.metta.koompi.ai before they finish their coffee. That's the product.
→ Full product vision, roadmap, and architecture: PRODUCT.md
OpenClaw agent (Telegram bot / AI)
↓ 15 MCP tools at /api/mcp (JSON-RPC 2.0)
Metta — multi-tenant CMS
├─ Block renderer → /p/[slug] (public site)
├─ Visual editor → /admin (point-and-click)
├─ REST API → /api/v1/ (headless reads)
└─ Billing / Auth / Media
↓ published instantly
{slug}.metta.koompi.ai (KOOMPI Cloud — no separate deploy)
Metta's job: store pages, serve blocks, handle billing, manage media. Dumb on purpose.
OpenClaw's job: understand user intent, call MCP tools, return human-readable responses. Stateless.
The MCP server is the contract between them. Break the contract, break the integration.
The visual editor in /admin is a second head — same data model, same publishing pipeline, different interface.
bun install
bun run dev # Dev server (Turbopack) → http://localhost:3000
bun run build # Production build + type check
bun run lint # ESLintbunx prisma migrate dev # Run migrations
npx tsx ./prisma/seed.ts # Seed database (npx tsx, not bun — better-sqlite3 compat)
bunx prisma studio # DB browser| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) + TypeScript |
| Database | Prisma 7 + SQLite (dev) / PostgreSQL (prod) |
| Styling | Tailwind CSS 4 (uses @plugin directive, not @import) |
| Auth | NextAuth.js + KOOMPI KID (OAuth v2) |
| Rich text | TipTap v3 |
| Payments | Stripe (global) + Baray.io (Cambodia: ABA, ACLEDA, Wing) |
| AWS SES v2 | |
| Storage | KConsole Object Storage |
| AI agents | OpenClaw on KOOMPI Cloud (skill.koompi.ai) |
| Agent protocol | MCP at /api/mcp |
| Rate limiting | Upstash Redis (in-memory fallback for dev) |
| What | Where |
|---|---|
| MCP server (15 tools) | src/app/api/mcp/route.ts |
| Block renderer (public site) | src/components/blocks/BlockRenderer.tsx |
| Visual editor | src/components/blocks/InlineEditor.tsx |
| Starter templates | src/lib/starter-templates.ts |
| Multi-tenant routing | src/proxy.ts |
| Permissions + trial enforcement | src/lib/permissions.ts |
| Plan limits | src/lib/plans.ts |
| Theme system | src/lib/themes/ |
| Baray.io payments | src/lib/baray.ts |
| Agent provisioning | src/lib/agent-provisioner.ts |
| Bot token security | src/lib/bot-tokens.ts |
| Per-org Telegram webhooks | src/app/api/webhooks/telegram/[botToken]/route.ts |
| Admin pages | src/app/admin/ |
| API routes | src/app/api/admin/ |
The 15 MCP tools exposed at /api/mcp are the full interface between OpenClaw and Metta:
| Tool | What it does |
|---|---|
list_pages |
List all pages for the org |
get_page |
Get a page with its blocks |
create_page |
Create a new page |
update_page_content |
Update a field on a page (heading, subtitle, etc.) |
publish_page |
Set page status to PUBLISHED |
draft_page |
Set page status to DRAFT |
delete_page |
Delete a page |
list_templates |
List available starter templates |
apply_template |
Scaffold a full site from a template (with pre-fill support) |
upload_media |
Fetch an image URL and store it in KConsole |
get_site_stats |
Pages, plan, storage usage |
update_settings |
Update site name, tagline, contact info |
list_themes |
List available themes |
apply_theme |
Apply a theme to the site |
- Tenant isolation: every DB query scoped by
organizationId— neverfindUnique({ where: { id } }) - Auth middleware:
withPermission("resource", "action")on all admin routes — nevergetServerSessiondirectly - Trial enforcement: mutating actions blocked when trial expired and plan = FREE
- Bot token hashing: SHA-256 hash stored in DB (
telegramBotTokenHash), plaintext never persisted - Baray HMAC: webhook signatures verified before any payload processing
- Rate limiting: Upstash Redis sorted-set pipeline; in-memory fallback for dev
- Multi-tenant routing:
src/proxy.tsresolves tenant from subdomain or?org=slug(dev only) - Block storage: pages store blocks as JSON array on
SitePage.blocks; renderer is a server component - Tailwind 4: uses
@plugindirective, not@importfor plugins - Dev seed:
npx tsx ./prisma/seed.ts(bun can't run better-sqlite3) - OpenClaw skill files:
openclaw/skills/— 8 markdown files the agent reads to drive onboarding, editing, billing