Quickstart: a minimal Worker
This page reproduces Core's minimal Worker reference as a from-scratch walkthrough. It is for engineers who want a running Mantle service on their machine in a few minutes, with no visitor frontend and no Cloudflare account.
Version scope The install below uses the published
0.1.0-alpha.17packages. That release supports this minimal Worker flow, but still includesmantle createandmantle updateand does not contain this handbook. The rest of this handbook describes a later development snapshot; see Versions before applying its CLI and migration guidance to a registry installation.
Prerequisites
- Node.js 22 or newer.
- pnpm 9 or newer. The reference is tested with pnpm; see the npm note at the end of this page.
wrangleris installed as a project devDependency below. No Cloudflare account, D1 database or secret is needed for the local loop.
1. package.json
Pin every @aotter/mantle* package to the same exact release and add the peers the Cloudflare adapter needs.
{
"name": "mantle-minimal-consumer",
"private": true,
"type": "module",
"scripts": {
"generate": "mantle generate",
"validate": "mantle validate",
"typecheck": "tsc --noEmit",
"dev": "wrangler dev --local",
"check": "mantle generate && mantle generate --check && mantle validate && mantle skills && mantle skills --check && tsc --noEmit"
},
"dependencies": {
"@aotter/mantle": "0.1.0-alpha.17",
"@aotter/mantle-cloudflare": "0.1.0-alpha.17",
"better-auth": "1.7.2",
"hono": "^4.13.3",
"zod": "^4.5.4",
"aws4fetch": "^1.0.20"
},
"devDependencies": {
"@cloudflare/workers-types": "^5.20260907.1",
"typescript": "^6.0.3",
"wrangler": "^4.125.0"
},
"packageManager": "pnpm@9.15.0"
}The reference's own check script ends with && node smoke.mjs, a test that starts the Worker and asserts the three probes in step 6. This walkthrough runs those probes by hand instead.
Add a tsconfig.json that includes the generated module:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"types": ["@cloudflare/workers-types"]
},
"include": ["src/**/*.ts", ".mantle/generated/**/*.ts"]
}2. manifests/site.yaml
One publishing Schema and one public View. mantle generate never invents a Schema; this notes model is example business data.
apiVersion: cms.mantle.aotter.net/v1
kind: Schema
metadata:
name: notes
spec:
title: Notes
schema:
type: object
required: [title]
properties:
title: { type: string }
lifecycle: publishing
---
apiVersion: cms.mantle.aotter.net/v1
kind: View
metadata:
name: published-notes
spec:
surface: public
from: notes
fields: [id, title]
filter:
eq: { field: status, value: published }
limit: 203. src/index.ts
The conventional Worker entry hands the sealed plan to the Cloudflare adapter.
import { createMantleWorker } from "@aotter/mantle/cloudflare";
import { plan } from "../.mantle/generated/mantle.js";
export default createMantleWorker({ plan });createMantleWorker owns the D1 and assets bindings, Auth, Admin, View REST, HTTP Triggers, OAuth, MCP and cache policy. Application handlers and extra routes go through its extend option; see the conventional Worker.
4. wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "mantle-reference",
"main": "src/index.ts",
"compatibility_date": "2026-09-08",
"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
"observability": { "enabled": true },
"vars": { "MANTLE_AUTH_MODE": "self-managed" },
"d1_databases": [
{ "binding": "DB", "database_name": "mantle-reference-local" }
]
}Both compatibility flags are required by the adapter. MANTLE_AUTH_MODE must be explicit; self-managed without GitHub credentials is a deliberate partial configuration for this local reference, so Auth-owned routes fail closed while public routes work. Local wrangler dev creates the D1 database on demand; configure a real database_id and the full auth matrix before any remote deploy (Authentication).
5. Install, generate, validate, run
pnpm install
pnpm exec mantle generate
pnpm exec mantle validate
pnpm exec wrangler dev --localmantle validate prints OK no issues (root: manifests, phase: preview). Wrangler prints the local origin, normally http://localhost:8787; use whatever it prints in the next step.
6. Probe the Worker
curl -s http://localhost:8787/api/views/published-notes{ "ok": true, "data": { "rows": [], "page": 1, "show": 20, "hasMore": false } }The View is served with no data because the local D1 is fresh. show follows the View's limit when the request carries no ?show=; ?page= and ?show= are the reserved pagination params (Reads: Views, REST and MCP).
curl -i http://localhost:8787/GET / returns 404. No visitor frontend is installed or rendered; mantle-web is optional composition and never owns an implicit home route. Add your own routes or templates when the product needs them (Public web, SEO and cache).
curl -i http://localhost:8787/mcp/staffGET /mcp/staff returns 503 with the error code setup_incomplete until MANTLE_AUTH_MODE is backed by a complete configuration. This is the expected fail-closed state; a working public endpoint is not evidence of a working Admin or MCP login.
What mantle generate wrote
.mantle/generated/mantle.ts— one module with the sealedplan, generated types (MantleHandlers<Env>),createMantleandbindMantle. The Worker entry above imports onlyplan.public/_mantle/admin/— the Admin SPA, synced only when@aotter/mantle-admin-uiis installed. This project did not install it, so nothing is written there and/adminhas no assets.
generate fails on missing or invalid manifests and never creates a project, a default Schema or a home route. mantle generate --check reports stale output without writing. The reference keeps .mantle/, .agents/ and .claude/ out of git and regenerates them in check; see Project layout and the CLI loop.
npm and
ERESOLVEWith npm 11.16.0 a cold Cloudflare install can fail withERESOLVE: Better Auth/Drizzle selects optional@libsql/client@0.18.0while this release declares the tested^0.17.4peer. If that exact conflict occurs, merge{ "overrides": { "@libsql/client": "0.17.4" } }intopackage.jsonand rerunnpm install. Do not use--forceor--legacy-peer-deps. Commit the lockfile and usenpm ciafterwards. Recheck the peer range when upgrading; the workaround is specific to these versions.
Next steps
- Project layout and the CLI loop — the files you own, every CLI flag, the daily check loop.
- The four atoms — add a Procedure and a Trigger to accept writes.
- Authentication — complete
MANTLE_AUTH_MODEso Admin and/mcp/staffopen. - Public web, SEO and cache — give the service a rendered public surface.
Source
docs/examples/minimal-worker/README.mddocs/examples/minimal-worker/package.jsondocs/examples/minimal-worker/tsconfig.jsondocs/examples/minimal-worker/manifests/site.yamldocs/examples/minimal-worker/src/index.tsdocs/examples/minimal-worker/wrangler.jsoncdocs/examples/minimal-worker/smoke.mjsdocs/direct-authoring.mdpackages/mantle-runtime/src/domain/service/Pagination.ts