Releases
Mantle publishes to npm under the @aotter/* scope. A stable release is a plain X.Y.Z version on the latest dist-tag and is the only kind intended for production. Prereleases (alpha, rc) let a release be prepared in the open; installing one means choosing an exact version, not a channel. GitHub Releases is the canonical change history; this page is the narrative one.
From 0.2.0-alpha.7, Mantle publishes three packages together at one version: @aotter/mantle (native SDK, compiler and CLI), @aotter/mantle-ui (pure UI), and @aotter/mantle-admin (optional Admin server, SPA and MCP App). Pin every package you use to the same exact version and upgrade them together.
0.2.0 (2026-10-19)
The first stable native 0.2 release compiles closed v2 manifests to SQL and TypeScript for Bun SQLite/PostgreSQL and Cloudflare D1/PostgreSQL. It ships three matching packages: Core, the pure UI kit and the optional application-built Admin. Applications own routing, verified identity, database handles, migrations and deployment; selected REST, MCP, public pages, publishing, translations and media compose around the generated operations.
This is a breaking upgrade from 0.1.x: follow the 0.1-to-0.2 migration guide, use a new 0.2 database and explicitly move data. From alpha.11, pin the three selected packages to 0.2.0, regenerate and review owned output; the stable version commit changes release metadata and documentation rather than adding another runtime API.
GitHub release is the complete change history. Known follow-ups remain tracked: Admin/authoring refinements, production social and magic-link recipe verification and real Sites guide verification. Those provider-specific paths are not claimed as verified by local OTP or native-host tests.
Upgrading to 0.2.0
Pin every selected @aotter/mantle, @aotter/mantle-ui and @aotter/mantle-admin dependency to 0.2.0, refresh the lockfile, then regenerate and check the application's owned artifacts. Keep existing production migrations application-owned; generated initial DDL is for an empty database. This release includes breaking Admin authoring and paging changes.
- Build output and host configuration.
mantle bundlewrites server modules todist/workerand static files todist/client, includingdist/client/admin. Rerunmantle generatewith the application's--entryand check the resulting Wranglermain,base_dir, asset directory and build command. See CLI and generated ownership. - Application-owned React. Install React and React DOM in the application at the same exact version (the examples use
19.3.0). Admin takes them as peers; a mismatched pair fails its build. - Replace extensions with application composition. Move to
defineAdmin(meta, { nav, pages, home, components, plugins, mcp })insrc/admin.tsx. Remove--admin-extensions,adminExtensions,defineAdminExtension, extension handlers and removed manifest widget,list.cellsand panel references.navGroupsbecomesnav.groups. Use the Admin migration table rather than carrying the former registration layer forward. - Build the MCP App with the application. The prebuilt
@aotter/mantle-admin/mcp-app/mantleAppHtmlexport is gone. UseadminMcpApps({ catalogue: mcpAppCatalogues.admin })with the generated catalogue; it reads the built asset. Declare CSP once indefineAdmin. The default resource URI hashes the built HTML, catalogue and metadata; custom HTML functions need an explicit immutable URI. Load interactive host HTML through authenticatedresources/read, which embeds its catalogue. See MCP Apps. - Run the generated compatibility check. Run
node --test generated/admin-mcp-app.check.mjsafter building. Install the optionalplaywright-corepeer and a browser, or setMANTLE_TEST_CHROMIUM; a missing browser fails rather than skips. This checks rendering with a synthetic catalogue, not application authorization or completion of a Procedure action. - Keep the host graph safe. The MCP build rejects
/admin/apiaccess, cookies and undeclared remote loads. Its entry must be readable in Node. An optionalsrc/admin.mcp.tsx(orbuildAdmin({ mcpEntry })) isolates browser-only integrations; both roots can reuse one host-safe config, components and CSS. Without it the whole shared entry graph is checked. - Distinguish total bounds from page sizes. Authored SQL
LIMITnow bounds the complete ordered result set across cursor pages, including web indexes. Use pagingoptions.limitfor page size; remove an authored limit only if it was incorrectly being used as a page-size setting. MCP accepts sizes of 1–500, but a response budget can return fewer rows: append pages untilnextCursoris absent, not until a short page. See View paging. - Install build dependencies where the build runs. Wrangler dev/deploy runs its configured build command, so that environment needs
node_modules. The resulting built server/assets do not readnode_moduleswhen served. See the application shell.
Record/collection cells, inputs, record actions, asides and RecordView are now truthfully browser-only (surface: "browser"); remove unreachable MCP branches. View cells remain both, and pages opt in through pages[].mcp. A Procedure action's input is distinct from a collection record input: use a Procedure target and MCP Trigger with the View's unchanged id and observed version for native row actions. Shared custom-page headers can compose localized title, description and actions on both surfaces; omit the option to keep prior behavior, or use header: false for an element-owned heading.
OAuth issuer verification now follows Better Auth's normalized URL while preserving explicit issuer/JWKS settings. Cloudflare OAuth configurations need global_fetch_strictly_public for public JWKS fetches; generated settings and the official examples retain it. Native MCP Apps use the official ext-apps client. Browser Admin now displays a localized explicit reload action when a lazy page fails, rather than going blank; it does not reload automatically or replay writes.
Native SDK migration (alpha.7)
Alpha.7 replaces the previous Core runtime and generated service contract. Existing alpha.6 applications require a source migration:
- Replace
createMantle,Store.selectand the old host presets with native generated queries and Store. Generate with an explicit target and output directory, for examplemantle generate cloudflare-d1 generated manifests/*.yaml --auth src/auth.ts --admin. See CLI and generated ownership. - The application owns routing, native database handles, verified Caller, Auth configuration and migrations. Runtime no longer converges storage or runs DDL at boot. Apply generated
schema.sqlonly to an empty database; migrate existing data explicitly. PostgreSQL uses Bun.SQL on Bun and postgres.js on Workers. See native execution. - Install the separate
@aotter/mantle-adminpackage when selecting Admin, withreactandreact-domin the application pinned to one identical exact version (Admin no longer installs them). Compose its native bindings in the application. Bare queries retain driver-native values; selected REST/MCP/Admin boundaries own validation and declared-column wire conversion. - Move lifecycle/schedule handlers and dynamic authorization guards into application code. Review authorization and Caller wiring and regenerate all owned artifacts before running
generate --check.
The reference Worker and app shell demonstrate the current composition. The following earlier-alpha notes are historical; the current handbook and the native migration above supersede their runtime and CLI instructions.
Earlier alpha design
0.2.0 replaces 0.1.x's grammar and composition; no stable 0.1.5 was released (0.1.5-alpha.1 was a prerelease). It breaks every consumer, and docs/upgrade-0.1-to-0.2.md in the package is the guide for moving a project by hand.
- Manifests are
cms.mantle.aotter.net/v2. Views are one SQLSELECTand Procedures are SQL statements or aref, in PostgreSQL syntax. The builtin handlers, the Filter AST,params,$ctxreferences andx-mantle-bindare gone; Schemas gainchecks, andsearchableFieldsbecomes full-text search. - SQL in the dialect's syntax. PostgreSQL is the reference dialect (
@aotter/mantle/postgres); D1 and SQLite run a subset (@aotter/mantle/d1). Each dialect is its own target (ADR-0039), and a plan records its dialect. - One Store. Every read and write goes through Store, which adds caller scope, TTL and published-only to every statement, and an optimistic lock where the caller passes one; a write is all or nothing.
- The service is the application's.
createMantle(service, …)replacescreateMantleWorker, and the firstmantle generatewrites the host's preset (src/service.ts,src/index.ts, ...) once.--hostiscloudflare(D1, or PostgreSQL through Hyperdrive),bun(node-postgres or bun:sqlite,@aotter/mantle/bun) ornone(plan and types only), and--dialectissqliteorpostgres; both are chosen once. - Mantle never owns your users. A
CallerResolverturns a request into aCaller; identity ismantle(Better Auth),customornone. Core creates no auth tables. - Storage converges to the plan at boot: additions are applied, unsafe changes are refused with a diagnostic, nothing is dropped, and there are no migration files.
- Schedules are POSIX cron;
toCloudflareCrontranslates them for Wrangler. - MCP tools come from Views and Procedures only. Staff tools are at
/mcp/stafffor an OAuth client, with Mantle's MCP App rendering staff View rows in the chat; Admin's WebMCP runs the same tools on Admin's own routes. - The CLI is
mantle generateandmantle generate --check.validate,emit-openapi,skillsandmantle-harnessare removed. - The Admin console moves to
@aotter/mantle-ui/admin, served at/adminby the preset through the service'sASSETSbinding. - Not yet in 0.2.0: Mantle-rendered public pages and the MCP interaction App tools.
Native driver and before-hook cleanup (#1426)
This breaking source migration removes request-scoped PostgreSQL sessions, custom write pipelining/retries, SQLite lock polling and before-hook runtime policing. Copy the new native preset wiring; existing generated source is not overwritten. Authenticated Bun SQLite uses separate Store/Auth handles to the same WAL file. Operational before hooks no longer add an implicit version lock; explicit OCC, publishing checks and authorization guards remain. See native execution migration and ADR-0041. The alpha.6 instructions below describe that release; ADR-0041 supersedes its requestScoped/pipeline/shared-SQLite-handle directions after this cleanup.
Upgrading an existing PostgreSQL service (0.2.0-alpha.6)
PostgreSQL is native from this alpha (ADR-0039): each dialect is its own target, PostgreSQL's SQL and ordering are PostgreSQL's, and node-postgres (pg) is the PostgreSQL driver on every host. D1 and SQLite output is unchanged except where noted. Work through these in order before deploying.
- Role settings (from #1383, now also checked on Bun). Boot refuses with
STORAGE_CHANGE_BLOCKED(pathstorage:settings) unless the role Mantle connects as has a statement limit and UTC:ALTER ROLE app SET statement_timeout = '10s'; ALTER ROLE app SET TimeZone = 'UTC';(10s or less). Bun'sBun.SQLentry used to pin its own limit and skip this check. A Hyperdrive config must keep--caching-disabled: reads, role re-reads and grant revocations run outside a transaction. - Rewrite SQLite spellings, then run
mantle generate. A PostgreSQL manifest that usesjson_each,-> '$.a',->> '$.a',json_extract,json_set,json_insert,json_removeorhexfails validation with its position and the PostgreSQL spelling. The dialect contract covers the PostgreSQL target:json_each(t.col) jbecomesjsonb_array_elements_text(t.col) WITH ORDINALITY AS j(value, n)(a sorted View over a row source needs the alias andWITH ORDINALITY, andj.idbecomesj.n),CAST(n AS bool)of an integer becomesn <> 0, and||needs a text operand. The PostgreSQL dialect version is now 2, so a plan compiled earlier fails at boot withPLAN_FINGERPRINT_MISMATCH: runmantle generateand deploy the regenerated plan. - NULL order. Unstated
NULLSnow follows PostgreSQL: NULL last ascending, first descending, for everyORDER BYincluding windows andjson_group_array. WriteNULLS FIRSTorNULLS LASTto keep a specific order. Rows tied on the last sort key are ordered byidin that key's direction. A cursor live across the deploy may repeat or skip rows once. - Bun entry. Install
pgand@types/pg. Replacenew SQL(url, { prepare: false, ... })withnew pg.Pool({ connectionString, pipeline: true });Env.SQLbecomesEnv.PG. The simplest path is to deletesrc/service.tsandsrc/index.tsand re-runmantle generate --host bun, or copy the new preset. Removed from@aotter/mantle/bun:bunPostgresStorage,bunPgConnect,bunDatabaseDriver,bunAuthDatabase,BunSqlPool,BunAuthPool. The role no longer needsTEMPORARY. Custom transports losetemporaryResultMetadata,describeResultandreadOnlyfromPgClient/PgStatement. - Generated
src/service.tschanged. Thedatabase(env).runwrapper moved to the exportedmantle, and the Bun pooled wrapper ends each client once. The preset is never rewritten: re-runmantle generateinto a scratch directory and copy the difference, or delete and regenerate if unedited. - First boot does one-time work. It runs once under the advisory lock:
- one
_mantle_ix_<schema>_updatedindex on([scope,] updated_at, id)per Schema, built withoutCONCURRENTLYinside the convergence transaction, so writes to a large table block while it builds. Pre-create it before deploying, on the schema (search_path) Mantle uses, with both names quoted:CREATE INDEX CONCURRENTLY "_mantle_ix_<schema>_updated" ON "<schema>" (["<scope>",] updated_at, id). A matching index is accepted. If a concurrent build fails, drop the invalid index before deploying: boot accepts it by name and columns and never rebuilds it. A Schema name longer than about 43 bytes gets a hashed index name; read it from a fresh database'spg_indexesinstead; - each
_mantle_chk_*check is rebuilt once (earlier releases left no marker); later boots leave unchanged checks alone; - the old
_mantle_jget,_mantle_booland_mantle_json_eachfunctions stay in place, unused: a previous release still running during a rolling deploy calls them.
- one
- Boot under contention. Concurrent boots converge once. A boot that cannot get a lock within about five tries of one second fails with
STORAGE_CHANGE_BLOCKEDnaming the lock (another boot still converging, or a long transaction on a table the plan changes), and is not retried for 10 seconds. A winner building a very large index can outlast its waiters, which then report the blocked boot; boot again when it has finished (#1408). - Auth.
get-sessionno longer carries aset-auth-jwtresponse header when the OAuth provider is configured; call the plugin's/api/auth/tokeninstead.d1Driver's structuralD1PreparedStatementnow requiresfirstandall: a hand-rolled fake must add them. - D1. A paged View with
json_eachin aJOINarm, or with more than onejson_each, now pages by every element'sid: its order among ties changes, no row is skipped, and a cursor issued before the upgrade may need to restart.
0.1.4 — 2026-09-24
0.1.4 repairs the published consumer path without changing the Manifest grammar. Pin every selected @aotter/mantle* package to 0.1.4, refresh the lockfile, then rerun mantle generate and mantle skills with their --check commands. Read the handbook and skills from that installed version.
ChatGPT Sites reference. The 0.1.3 reference omitted the _mantle_boot_state.store_instance_id column required by the runtime, which could make deployed runtime routes return 500. The reference now adds the append-only drizzle/0003_store_instance_id.sql migration. If your project was copied from that reference, carry the new migration forward and apply pending D1 migrations before deploying updated code; do not edit a migration already applied. Repeated builds now clear dist so removed SQL files cannot remain in the deployment artifact. The Sites guide also shows how to mount an HTTP Trigger, identifies the browser and MCP owner workflows, and names its integration chapter docs/handbook/chatgpt-sites/.
Agent installation. npx skills add aotter/mantle installs the small mantle bootstrap skill; the installed npm package supplies version-matched instructions and docs. The skill now checks the application's installed version before reading them. Superseded Starter decisions are marked as history: no mantle-starters/v0.1.4 tag or mantle create command is needed to author a new application.
0.1.3 — 2026-09-23
0.1.3 tightens the contract between Manifests, generated TypeScript, agent skills and the optional Admin surface. To upgrade from 0.1.2, pin every selected @aotter/mantle* package to 0.1.3, refresh the lockfile, then run mantle generate, mantle skills and the matching --check commands.
Private and typed reads. Views may use surface: internal to stay out of REST, MCP, WebMCP, OpenAPI and Admin while remaining callable from host code. Generated bindings type declarative View params and rows, expose typed indexed field reads for Schemas, and can be emitted from an already compiled plan. SQL Views remain SQLite-native and deliberately return an unknown row type.
Safer View storage and validation. Public declarative Views over publishing Schemas always enforce published status. Native entry columns (id, status, version, createdAt, updatedAt, authorId) are reserved consistently and may be used in the supported View/index positions. SQL validation is confined to declared Schema tables, and the local index harness now uses production- shaped planner and fixture state instead of reporting an artificial pass.
MCP and authorization. Tool schemas have agent-shaped inputs and standard read-only, destructive, open-world and idempotency annotations. Calls carry expected-version data through optimistic concurrency checks, enforce the caller gate, and can emit audit records keyed by a declared idempotency input. OAuth provider extensions, account linking and sign-in-link flows are owned by the extracted optional @aotter/mantle-auth package. Session cache keys bind to the prepared store identity so replacing D1 cannot revive stale sessions.
Admin and authoring. Manifest uiSchema can select operational collection columns/tabs, staff report search/filter/CSV fields and collection or row actions. Native columns render correctly in lists, and operation dialogs reset their optimistic-concurrency state between actions. The handbook now starts with a task-oriented overview, a complete Manifest feature table, typed-query and Admin-rendering guides, and an explicit skill-install → pinned SDK → project-skill handoff. The CLI points to those installed, version-matched docs.
Upgrade note. Regeneration is required because generated bindings and projected skills gained APIs and instructions. Applications that declared one of the newly reserved native column names as business data must rename that field before upgrading. Backend-specific D1/IndexedDB cost inspectors and server-side soak budgets remain deferred to #1040; the conservative local harness is a preflight, not production cost evidence.
0.1.2 — 2026-09-21
The first stable release, and Mantle's first public one. Everything before it was an internal prerelease; there is no earlier stable to upgrade from and no migration path to follow.
Requires Node.js 22 or newer. Cloudflare Workers is the supported host.
The manifest engine. Describe data, queries, actions and triggers as four atoms — Schema, View, Procedure and Trigger — in YAML under manifests/. mantle generate parses, links and compiles them into .mantle/generated/mantle.ts: a sealed execution plan, TypeScript types and typed bindings. Fingerprint or version mismatches between a generated plan and the installed packages fail immediately and ask you to regenerate.
Storage. Each Manifest Schema becomes one native SQLite/D1 table. Authored fields keep their exact names as columns; Mantle's row envelope adds _mantle_id, _mantle_status, _mantle_version, _mantle_author_id, _mantle_created_at and _mantle_updated_at. Generated migration artifacts cover initial and additive changes; renames, type changes, data transforms and uniqueIndexes tuple changes are a manual rebuild.
Surfaces. One contract drives all of them: public REST Views and HTTP Triggers, server-rendered HTML with Markdown, llms.txt and sitemap output, the Admin console and its prebuilt React SPA, and MCP — anonymous read-only Views at /api/mcp, staff tools at /api/mcp/staff, and Admin WebMCP in the browser. See HTTP, MCP, CLI and packages for the full list.
Hosting. createMantleWorker assembles the conventional Cloudflare Worker: D1 and assets bindings, Better Auth 1.7 (social providers, email OTP, magic link, passkey), Admin, MCP, Web and R2 media uploads. The Bun and Vercel adapters are experimental, cover public Views and HTTP Triggers only, and leave authentication and CSRF to the host. ChatGPT Sites is a first-class integration with a runnable reference.
Agents. mantle skills projects the installed package's skills into .agents/skills/mantle-* and .claude/skills/mantle-*, so an agent working in your project reads instructions matched to the version you installed. --check detects drift without writing.
Not yet covered. Stable does not carry a published latency budget, production-traffic measurement or a soak window; those acceptance items are tracked for 0.1.3 in #962. The Bun and Vercel adapters may change in a minor release.