@aotter/mantle 0.1.0-alpha.6 → 0.1.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -30
- package/dist/admin.d.ts +2 -0
- package/dist/admin.d.ts.map +1 -0
- package/dist/admin.js +2 -0
- package/dist/admin.js.map +1 -0
- package/dist/bun.d.ts +2 -0
- package/dist/bun.d.ts.map +1 -0
- package/dist/bun.js +2 -0
- package/dist/bun.js.map +1 -0
- package/dist/cli/create.d.ts +2 -0
- package/dist/cli/create.d.ts.map +1 -0
- package/dist/cli/create.js +243 -0
- package/dist/cli/create.js.map +1 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +101 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/cli/harness.d.ts +3 -0
- package/dist/cli/harness.d.ts.map +1 -0
- package/dist/{harness-cli.js → cli/harness.js} +13 -8
- package/dist/cli/harness.js.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/{cli.js → cli/main.js} +6 -8
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/skills.d.ts +3 -0
- package/dist/cli/skills.d.ts.map +1 -0
- package/dist/{skills.js → cli/skills.js} +51 -6
- package/dist/cli/skills.js.map +1 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/{update.js → cli/update.js} +72 -46
- package/dist/cli/update.js.map +1 -0
- package/dist/codegen/emitMantleModule.d.ts +16 -0
- package/dist/codegen/emitMantleModule.d.ts.map +1 -0
- package/dist/codegen/emitMantleModule.js +217 -0
- package/dist/codegen/emitMantleModule.js.map +1 -0
- package/dist/codegen.d.ts +2 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +2 -0
- package/dist/codegen.js.map +1 -0
- package/dist/provision/renderProvisionBundle.d.ts +70 -0
- package/dist/provision/renderProvisionBundle.d.ts.map +1 -0
- package/dist/provision/renderProvisionBundle.js +367 -0
- package/dist/provision/renderProvisionBundle.js.map +1 -0
- package/dist/provision.d.ts +2 -0
- package/dist/provision.d.ts.map +1 -0
- package/dist/provision.js +2 -0
- package/dist/provision.js.map +1 -0
- package/dist/vercel-libsql.d.ts +2 -0
- package/dist/vercel-libsql.d.ts.map +1 -0
- package/dist/vercel-libsql.js +2 -0
- package/dist/vercel-libsql.js.map +1 -0
- package/dist/vercel.d.ts +2 -0
- package/dist/vercel.d.ts.map +1 -0
- package/dist/vercel.js +2 -0
- package/dist/vercel.js.map +1 -0
- package/dist/web.d.ts +2 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +2 -0
- package/dist/web.js.map +1 -0
- package/docs/adapter-guide.md +88 -45
- package/docs/adr/0001-four-atom-manifest-model.md +11 -3
- package/docs/adr/0007-ai-as-primary-author.md +3 -3
- package/docs/adr/0009-consumer-supplied-manifests.md +9 -4
- package/docs/adr/0011-adapter-port-spec.md +18 -4
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +111 -6
- package/docs/adr/0018-core-starters-repository-boundary.md +22 -1
- package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +204 -0
- package/docs/adr/README.md +12 -10
- package/docs/api-mcp-authorization.md +21 -42
- package/docs/assets/mantle-admin-operations.png +0 -0
- package/docs/assets/mantle-hero.jpg +0 -0
- package/docs/auth-hosting-model.md +5 -5
- package/docs/cloudflare-low-level-composition.md +30 -18
- package/docs/deferred-lifecycle-queues.md +20 -38
- package/docs/design-atoms.md +31 -133
- package/docs/labels.md +1 -1
- package/docs/media-uploads.md +9 -2
- package/docs/migration-0.1.2.md +45 -0
- package/docs/performance-harness.md +4 -4
- package/docs/release-process.md +72 -22
- package/docs/sealed-pipeline-ownership.md +100 -0
- package/package.json +84 -14
- package/skills/README.md +39 -7
- package/skills/develop/SKILL.md +7 -5
- package/skills/install/SKILL.md +26 -25
- package/skills/media-gc/SKILL.md +85 -0
- package/skills/plugin/SKILL.md +1 -0
- package/skills/provision/SKILL.md +2 -0
- package/skills/theme/SKILL.md +1 -0
- package/skills/update/SKILL.md +1 -0
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/generate.d.ts.map +0 -1
- package/dist/generate.js +0 -226
- package/dist/generate.js.map +0 -1
- package/dist/harness-cli.d.ts +0 -3
- package/dist/harness-cli.d.ts.map +0 -1
- package/dist/harness-cli.js.map +0 -1
- package/dist/skills.d.ts +0 -2
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- /package/dist/{generate.d.ts → cli/generate.d.ts} +0 -0
- /package/dist/{update.d.ts → cli/update.d.ts} +0 -0
package/docs/adapter-guide.md
CHANGED
|
@@ -2,20 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
This guide is the fresh-developer entry point for implementing a new mantle platform adapter.
|
|
4
4
|
|
|
5
|
-
Read this with [ADR-
|
|
5
|
+
Read this with [ADR-0019](adr/0019-sealed-manifest-runtime-pipeline.md). The source of truth for TypeScript shapes is `packages/mantle-runtime/src/domain/port/`.
|
|
6
6
|
|
|
7
7
|
Adapter packages live under `packages/adapters/<platform>/` using a plural `adapters` bucket. The npm package names stay unchanged, for example `@aotter/mantle-cloudflare`. Keep adapters in this monorepo until the runtime/spec API is stable enough that coordinated releases across separate repositories would not create version skew for starters.
|
|
8
8
|
|
|
9
|
-
## Required
|
|
9
|
+
## Required storage boundary
|
|
10
10
|
|
|
11
|
-
A
|
|
11
|
+
A storage adapter prepares one `RuntimePlan` into semantic ports:
|
|
12
12
|
|
|
13
13
|
| Contract | Source | Cloudflare example |
|
|
14
14
|
|---|---|---|
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
15
|
+
| `MantleStorageAdapter` / `PreparedMantleStorage` | `packages/mantle-runtime/src/domain/port/MantleStorageAdapter.ts` | `SqliteMantleStorageAdapter` over `D1DatabaseDriver` |
|
|
16
|
+
| `EntryRepository & EntryReader` | Existing semantic content ports | `DatabaseEntryRepository` supplied by the SQLite adapter |
|
|
17
|
+
| `ViewQueryExecutor` | `packages/mantle-runtime/src/domain/port/ViewQueryExecutor.ts` | `SqliteViewQueryExecutor` |
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
`DatabaseDriver` is only the reusable SQLite/D1 implementation seam. A
|
|
20
|
+
PostgreSQL, MongoDB, or application-owned-table adapter implements the semantic
|
|
21
|
+
ports directly; it does not emulate D1 and needs no mapping DSL. Platform types
|
|
22
|
+
such as `D1Database`, Postgres pools, and Mongo clients remain in adapter code.
|
|
19
23
|
|
|
20
24
|
## Optional capabilities
|
|
21
25
|
|
|
@@ -26,7 +30,7 @@ Optional ports are enabled only when a feature needs them:
|
|
|
26
30
|
| `MediaStorage` | `packages/mantle-runtime/src/domain/port/MediaStorage.ts` | The adapter exposes admin/MCP media upload flows. |
|
|
27
31
|
| `DeferredHookDispatcher` | `packages/mantle-runtime/src/domain/port/DeferredHookDispatcher.ts` | The adapter wants at-least-once queue delivery for `after_*` lifecycle hooks. |
|
|
28
32
|
|
|
29
|
-
Test seams such as `Clock` and `IdGenerator` are injectable through `
|
|
33
|
+
Test seams such as `Clock` and `IdGenerator` are injectable through `createMantleRuntime`, but normal adapters do not need custom implementations.
|
|
30
34
|
|
|
31
35
|
Deferred delivery is an optional, versioned wire contract. Queue acceptance is
|
|
32
36
|
not atomic with the entry write; adapters must preserve the supplied event id,
|
|
@@ -35,37 +39,72 @@ to their retry mechanism, and document poison-message/DLQ behavior. See
|
|
|
35
39
|
[Deferred lifecycle hooks on Cloudflare Queues](deferred-lifecycle-queues.md)
|
|
36
40
|
for the reference implementation and exact guarantees.
|
|
37
41
|
|
|
38
|
-
##
|
|
42
|
+
## Storage preparation
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
Compile before deployment preparation, then pass only the sealed plan:
|
|
41
45
|
|
|
42
46
|
```ts
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
import {
|
|
48
|
+
bootMantleRuntime,
|
|
49
|
+
SqliteMantleStorageAdapter,
|
|
50
|
+
} from "@aotter/mantle-runtime";
|
|
51
|
+
|
|
52
|
+
const storage = new SqliteMantleStorageAdapter(db, siteDefaults);
|
|
53
|
+
const runtime = await bootMantleRuntime({
|
|
54
|
+
plan,
|
|
55
|
+
storage,
|
|
47
56
|
handlers,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
deferredHookDispatcher,
|
|
57
|
+
ports,
|
|
58
|
+
deployment: {
|
|
59
|
+
reservedHttpPathPrefixes: selectedCapabilities.flatMap(
|
|
60
|
+
(capability) => capability.reservedHttpPathPrefixes,
|
|
61
|
+
),
|
|
62
|
+
},
|
|
55
63
|
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`bootMantleRuntime` performs one attempt and derives Procedure readiness from
|
|
67
|
+
the supplied handlers. The platform adapter remains responsible for lazy boot,
|
|
68
|
+
promise caching, retry policy, and teardown.
|
|
56
69
|
|
|
57
|
-
|
|
70
|
+
Hosts that need work between preparation and binding may keep the same stages
|
|
71
|
+
public and explicit:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const prepared = await prepareDeployment(plan, storage, {
|
|
75
|
+
handlerNames: Object.keys(handlers ?? {}),
|
|
76
|
+
});
|
|
77
|
+
const runtime = createMantleRuntime({ prepared, handlers, ports });
|
|
58
78
|
```
|
|
59
79
|
|
|
60
|
-
|
|
80
|
+
The prepared revision carries its exact plan, but exposes both `plan` and
|
|
81
|
+
`storage`; it seals the pairing invariant rather than hiding low-level host
|
|
82
|
+
capabilities. Omit `handlerNames` only for a read-only embedding that never
|
|
83
|
+
dispatches Procedures.
|
|
61
84
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
The official SQLite adapter runs canonical migrations, defaults, indexes, and
|
|
86
|
+
schema-View reconciliation, and skips mutation for an unchanged revision. A
|
|
87
|
+
custom adapter owns its own preparation and returns application-owned semantic
|
|
88
|
+
ports. Unsupported native View dialects fail before the adapter mutates state.
|
|
89
|
+
|
|
90
|
+
### Bun embedding
|
|
91
|
+
|
|
92
|
+
`@aotter/mantle-bun` is the minimal SQLite reference: pass an application-owned
|
|
93
|
+
`bun:sqlite` `Database` and a compiled `RuntimePlan` to `createBunMantle()`.
|
|
94
|
+
Its `handle()` returns `null` for sibling routes and a Web-standard `Response`
|
|
95
|
+
for manifest-declared public Views and HTTP Triggers. The host owns
|
|
96
|
+
`Bun.serve`, database shutdown, authentication, and CSRF policy; the adapter
|
|
97
|
+
prepares the semantic revision once and retries only after failed preparation.
|
|
98
|
+
|
|
99
|
+
### Vercel Functions embedding
|
|
100
|
+
|
|
101
|
+
`@aotter/mantle-vercel` accepts a compiled plan plus any application-owned
|
|
102
|
+
`MantleStorageAdapter` and reuses the same public View/HTTP Trigger transport as
|
|
103
|
+
Bun. It maps deferred work to Vercel Functions `waitUntil` and otherwise leaves
|
|
104
|
+
the Web Handler, auth/CSRF, and route composition to the application. The
|
|
105
|
+
optional `/libsql` subpath adapts a caller-owned remote Turso/libSQL client to
|
|
106
|
+
the canonical SQLite chain; the default entry has no database-vendor policy.
|
|
107
|
+
Vercel's read-only filesystem and writable `/tmp` are never durable state.
|
|
69
108
|
|
|
70
109
|
## HTTP and MCP surfaces
|
|
71
110
|
|
|
@@ -73,9 +112,10 @@ The runtime is a library, not an HTTP server. A new adapter must mount equivalen
|
|
|
73
112
|
|
|
74
113
|
| Surface | Adapter responsibility | Cloudflare reference |
|
|
75
114
|
|---|---|---|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
115
|
+
| Runtime HTTP endpoints | Route HTTP Triggers and public View REST endpoints into runtime use cases. | `packages/adapters/cloudflare/src/mount/mountRuntimeEndpoints.ts` |
|
|
116
|
+
| Optional Admin | Supply session/request context and assets to `mantle-admin`; mount only when selected. | `packages/adapters/cloudflare/src/mount/mountAdmin.ts` |
|
|
117
|
+
| Auth endpoints | Own sign-in/session/OAuth metadata through the adapter's Better Auth implementation selected by Admin/OAuth surfaces. | `packages/adapters/cloudflare/src/auth/createAuth.ts` |
|
|
118
|
+
| MCP endpoints | Mount `/mcp/staff` and `/mcp` behind one canonical protected resource. The adapter verifies Better Auth JWTs, normalizes the caller, re-reads the staff D1 role, then dispatches JSON-RPC. | `packages/adapters/cloudflare/src/mount/mountMcp.ts`, `auth/createAuth.ts` |
|
|
79
119
|
|
|
80
120
|
Auth is not a runtime port. Per [ADR-0014](adr/0014-auth-better-auth-and-multi-tenant-mcp.md), the adapter owns Better Auth wiring and passes authenticated user/staff context into runtime dispatchers. Procedure handlers receive that data through `HandlerContext` in `packages/mantle-runtime/src/domain/model/HandlerContext.ts`.
|
|
81
121
|
|
|
@@ -93,7 +133,7 @@ Minimum HTTP behavior for a full adapter:
|
|
|
93
133
|
- Route manifest HTTP Triggers to `runtime.invokeProcedure`.
|
|
94
134
|
- Route `GET /api/views/<name>` to `runtime.executeView`.
|
|
95
135
|
- Mount admin content APIs with session/role checks before calling runtime content use cases.
|
|
96
|
-
- Serve
|
|
136
|
+
- Serve selected Admin SPA assets through `AdminAssetServer`, with an SPA catchall for client-side routes.
|
|
97
137
|
- Mount public render routes and markdown mirrors when the starter exposes public pages.
|
|
98
138
|
- Translate runtime diagnostics and validation failures into stable HTTP JSON responses instead of throwing raw errors.
|
|
99
139
|
- Evaluate target auth and dynamic guards through the runtime use cases; do
|
|
@@ -101,16 +141,20 @@ Minimum HTTP behavior for a full adapter:
|
|
|
101
141
|
|
|
102
142
|
For the Cloudflare adapter, public rendering requires three matching consumer
|
|
103
143
|
inputs: `mountPublicRoutes(...)` route declarations, a `TemplateRegistry`
|
|
104
|
-
passed through `
|
|
105
|
-
`
|
|
144
|
+
passed through `MantleCloudflareConfig.templates`, and a `publicPathResolver`
|
|
145
|
+
passed through `MantleCloudflareConfig.publicPathResolver`. Omitting public routes is valid for a headless
|
|
106
146
|
consumer; mounting every Schema automatically is not, because some collections
|
|
107
147
|
are private even when they contain a slug.
|
|
108
148
|
|
|
149
|
+
`TemplateRegistry` and `createPublicPathResolver` come from
|
|
150
|
+
`@aotter/mantle-web`. Other adapters can call `createMantleWeb(runtime)` and map
|
|
151
|
+
its document operations into their own routing and cache conventions.
|
|
152
|
+
|
|
109
153
|
### HTTP cache contract
|
|
110
154
|
|
|
111
|
-
The Cloudflare adapter is private by default. Consumers must
|
|
112
|
-
|
|
113
|
-
|
|
155
|
+
The Cloudflare adapter is private by default. Consumers must apply its final
|
|
156
|
+
cache policy after admin, auth, API, OAuth, MCP, application routes, redirects,
|
|
157
|
+
and errors.
|
|
114
158
|
Those responses receive `Cache-Control: private, no-store`; Cloudflare-specific
|
|
115
159
|
CDN cache overrides are removed.
|
|
116
160
|
|
|
@@ -146,20 +190,19 @@ Minimum auth/MCP behavior:
|
|
|
146
190
|
|
|
147
191
|
## Static assets
|
|
148
192
|
|
|
149
|
-
`
|
|
193
|
+
`AdminAssetServer` belongs to optional `@aotter/mantle-admin`. Headless Core
|
|
194
|
+
storage preparation and binding do not accept or require a static asset port.
|
|
150
195
|
|
|
151
196
|
## Implementation checklist
|
|
152
197
|
|
|
153
|
-
- [ ] Implement `
|
|
154
|
-
- [ ]
|
|
155
|
-
- [ ] Compose `createCmsRuntime` with manifests, handlers, templates, site defaults, and required ports.
|
|
156
|
-
- [ ] Call `bootInit()` before serving CMS traffic.
|
|
198
|
+
- [ ] Implement `MantleStorageAdapter` returning existing semantic ports, or reuse `SqliteMantleStorageAdapter` with an already-owned handle.
|
|
199
|
+
- [ ] Call `bootMantleRuntime()` once per semantic revision, or explicitly prepare before binding.
|
|
157
200
|
- [ ] Mount HTTP Trigger and View REST surfaces.
|
|
158
201
|
- [ ] Mount admin/public render routes and admin SPA assets.
|
|
159
202
|
- [ ] Provide adapter-owned Better Auth wiring and session helpers.
|
|
160
203
|
- [ ] Normalize session/OAuth and any consumer credential seam into
|
|
161
204
|
`HandlerContext.auth`; never put raw credentials in runtime context.
|
|
162
|
-
- [ ] Mount `/mcp/staff` and `/mcp`
|
|
205
|
+
- [ ] Mount `/mcp/staff` and `/mcp` behind one resource-bound token verifier. Enforce the live staff role inside the apiHandler.
|
|
163
206
|
- [ ] Preserve the HTTP cache contract: private by default; explicit anonymous 200 `GET`/`HEAD` public opt-in only.
|
|
164
207
|
- [ ] Prove one guarded target has identical REST/MCP outcomes, including
|
|
165
208
|
mutable revocation on the next call.
|
|
@@ -174,4 +217,4 @@ Minimum auth/MCP behavior:
|
|
|
174
217
|
- Do not add API-key, personal-token, transaction, billing, or entitlement
|
|
175
218
|
repositories to Core. They are consumer state behind the adapter resolver
|
|
176
219
|
and guard Procedure.
|
|
177
|
-
- Do not
|
|
220
|
+
- Do not generalize `DatabaseDriver` for PostgreSQL/MongoDB or add a mapping DSL. Implement semantic ports; SQLite/D1 alone reuse the canonical SQL chain.
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# ADR-0001: 4-atom YAML manifest model under `cms.mantle.aotter.net/v1`
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** Accepted for the four-atom grammar and multi-document YAML;
|
|
4
|
+
the fixed `site.yaml` file contract is superseded by ADR-0019.
|
|
4
5
|
|
|
5
|
-
**Date**: 2026-04-30 (POC origin)
|
|
6
|
+
**Date**: 2026-04-30 (POC origin); last amended 2026-08-16
|
|
6
7
|
|
|
7
8
|
**Deciders**: phsu
|
|
8
9
|
|
|
9
|
-
**Related**: ADR-0002 (closed enums for bindings)
|
|
10
|
+
**Related**: ADR-0002 (closed enums for bindings),
|
|
11
|
+
[ADR-0019](0019-sealed-manifest-runtime-pipeline.md) (caller-owned source boundary)
|
|
12
|
+
|
|
13
|
+
> **2026-08-16 amendment:** the four atoms and multi-document YAML remain
|
|
14
|
+
> normative. Core no longer assigns source file names: its parser accepts
|
|
15
|
+
> caller-owned source IDs, and its CLI reads immediate `.yaml` / `.yml` files
|
|
16
|
+
> in lexicographic order. `manifests/site.yaml` remains only a starter
|
|
17
|
+
> convention. Fixed-file statements below are retained as decision history.
|
|
10
18
|
|
|
11
19
|
---
|
|
12
20
|
|
|
@@ -252,9 +252,9 @@ Concrete artifacts today:
|
|
|
252
252
|
- Version-matched Core skills ship inside `@aotter/mantle`, through the Mantle
|
|
253
253
|
agent plugin, and through exact-byte repo-local projections written by
|
|
254
254
|
`mantle skills`.
|
|
255
|
-
- The umbrella `mantle` CLI exposes `
|
|
256
|
-
`
|
|
257
|
-
|
|
255
|
+
- The umbrella `mantle` CLI exposes `create`, `generate`, `validate`,
|
|
256
|
+
`emit-openapi`, `update`, and `skills`; direct spec installs expose advanced
|
|
257
|
+
`introspect` and `emit-types` primitives through `mantle-spec`.
|
|
258
258
|
- `@aotter/mantle/runtime/testing` and `mantle-harness` expose the measured
|
|
259
259
|
SQLite/index and live-HTTP checks described in Loop 2.
|
|
260
260
|
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# ADR-0009: Consumer-supplied manifests at SDK boot
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
4
|
-
consumer boundary.
|
|
3
|
+
**Status:** Superseded by ADR-0019.
|
|
5
4
|
|
|
6
|
-
**Date:** 2026-05-01 (POC);
|
|
5
|
+
**Date:** 2026-05-01 (POC); superseded 2026-08-16
|
|
7
6
|
|
|
8
7
|
**Related:** [ADR-0001](0001-four-atom-manifest-model.md),
|
|
9
8
|
[ADR-0007](0007-ai-as-primary-author.md),
|
|
10
|
-
[ADR-0018](0018-core-starters-repository-boundary.md)
|
|
9
|
+
[ADR-0018](0018-core-starters-repository-boundary.md),
|
|
10
|
+
[ADR-0019](0019-sealed-manifest-runtime-pipeline.md)
|
|
11
|
+
|
|
12
|
+
> **Supersession note:** consumer ownership remains durable, but the exact
|
|
13
|
+
> `site.yaml`, generated `site.ts`, and parser-free array workflow below is
|
|
14
|
+
> historical. The sealed source → parse → link → compile pipeline in ADR-0019
|
|
15
|
+
> is current; source names and generated code are optional caller concerns.
|
|
11
16
|
|
|
12
17
|
## Context
|
|
13
18
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# ADR-0011: Adapter port spec
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** Superseded for Core storage and Admin composition by ADR-0019.
|
|
4
|
+
Retained as the alpha.7 Cloudflare adapter record; `DatabaseDriver` is now an
|
|
5
|
+
implementation detail of the SQLite/D1 `MantleStorageAdapter`, while the asset
|
|
6
|
+
contract belongs to optional `@aotter/mantle-admin`.
|
|
4
7
|
|
|
5
|
-
**Date:** 2026-05-04 (revised 2026-05-09, 2026-05-10, 2026-08-11, and 2026-08-
|
|
8
|
+
**Date:** 2026-05-04 (revised 2026-05-09, 2026-05-10, 2026-08-11, 2026-08-13, and 2026-08-22).
|
|
6
9
|
|
|
7
10
|
## Context
|
|
8
11
|
|
|
9
12
|
`@aotter/mantle-runtime` is adapter-agnostic. It owns dispatcher, entry-writer, view executor, content-ops, render pipeline, boot validation, and MCP JSON-RPC dispatch. It depends only on `@aotter/mantle-spec` and a small set of TypeScript interfaces it defines itself.
|
|
10
13
|
|
|
11
|
-
`@aotter/mantle-cloudflare` is the only adapter shipping in v0.1.0. It binds the runtime's interfaces against Cloudflare Workers' D1 and ASSETS, and supplies a Better Auth instance (per ADR-0014) for sign-in
|
|
14
|
+
`@aotter/mantle-cloudflare` is the only adapter shipping in v0.1.0. It binds the runtime's interfaces against Cloudflare Workers' D1 and ASSETS, and supplies a Better Auth instance (per ADR-0014) for sign-in and MCP authorization. Better Auth 1.7 stores OAuth grants in D1; auth storage remains adapter-owned infrastructure, not a runtime port.
|
|
12
15
|
|
|
13
16
|
This ADR fixes the contract so:
|
|
14
17
|
- Future adapter authors have a stable target.
|
|
@@ -19,7 +22,14 @@ The POC accumulated multiple half-decisions about this seam (POC ADR-0015 docume
|
|
|
19
22
|
|
|
20
23
|
## Decision
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
> 0.1.2 amendment: the portable storage input is
|
|
26
|
+
> `PreparedMantleStorage` (`EntryRepository & EntryReader` plus
|
|
27
|
+
> `ViewQueryExecutor`). Hosts either use an official storage adapter with an
|
|
28
|
+
> already-owned client/handle or implement those semantic ports over their own
|
|
29
|
+
> tables. `AssetServer` moved out of Runtime to optional Mantle Admin. The
|
|
30
|
+
> two-port contract below is the historical alpha.7 shape.
|
|
31
|
+
|
|
32
|
+
Alpha.7 had **two required adapter ports**, defined as TypeScript interfaces in `@aotter/mantle-runtime/src/domain/port/`. Concrete adapters provided implementations and injected them into `createCmsRuntime`.
|
|
23
33
|
|
|
24
34
|
| Port | Surface |
|
|
25
35
|
|---|---|
|
|
@@ -140,6 +150,10 @@ The admin SPA itself lives in `@aotter/mantle-admin-ui` as a pre-built `dist/`.
|
|
|
140
150
|
|
|
141
151
|
## How adapters wire ports
|
|
142
152
|
|
|
153
|
+
> Historical alpha.7 example. Current convenience composition is
|
|
154
|
+
> `createMantleWorker`; ADR-0014's 2026-08-22 amendment replaces the OAuth
|
|
155
|
+
> provider shown below with Better Auth 1.7 MCP/CIMD.
|
|
156
|
+
|
|
143
157
|
```ts
|
|
144
158
|
// simplified Cloudflare adapter wiring (post-ADR-0014, amended
|
|
145
159
|
// 2026-05-15 by PR #193's OAuth carve-out).
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
|
-
Accepted. Amended 2026-05-14, 2026-05-15, 2026-06-30, 2026-07-15,
|
|
6
|
-
2026-08-03.
|
|
5
|
+
Accepted. Amended 2026-05-14, 2026-05-15, 2026-06-30, 2026-07-15,
|
|
6
|
+
2026-08-03, and 2026-08-22.
|
|
7
7
|
|
|
8
8
|
## Date
|
|
9
9
|
|
|
10
|
-
2026-05-09 (last amended 2026-08-
|
|
10
|
+
2026-05-09 (last amended 2026-08-22)
|
|
11
11
|
|
|
12
12
|
> **Current authority:** the original decision below records the rejected
|
|
13
|
-
> Better-Auth-for-MCP design. The 2026-
|
|
14
|
-
>
|
|
15
|
-
>
|
|
13
|
+
> Better-Auth-for-MCP design. The 2026-08-22 Better Auth 1.7 amendment is
|
|
14
|
+
> authoritative where it conflicts with the 2026-05-15 carve-out; the
|
|
15
|
+
> 2026-07-15 normalized authorization boundary still applies. Operational
|
|
16
|
+
> guidance is maintained against the current adapter/runtime.
|
|
16
17
|
|
|
17
18
|
## Context
|
|
18
19
|
|
|
@@ -520,3 +521,107 @@ Dynamic membership, billing, and entitlement state remains consumer-owned.
|
|
|
520
521
|
does not introduce a Policy atom or an entitlement service. See
|
|
521
522
|
[`API and MCP authorization`](../api-mcp-authorization.md) for the public API
|
|
522
523
|
and end-to-end examples.
|
|
524
|
+
|
|
525
|
+
## Amendment — 2026-08-22: Better Auth 1.7 MCP and CIMD convergence
|
|
526
|
+
|
|
527
|
+
Issue #734 revisits the 2026-05-15 compatibility carve-out after Better Auth
|
|
528
|
+
1.7 shipped a dedicated `@better-auth/mcp` package, resource-bound JWT grants,
|
|
529
|
+
and the MCP 2026-07-28 Client ID Metadata Document profile. These are the
|
|
530
|
+
missing capabilities that originally forced Mantle to keep a second OAuth
|
|
531
|
+
authority.
|
|
532
|
+
|
|
533
|
+
The Cloudflare adapter now uses one Better Auth instance for staff identity,
|
|
534
|
+
OAuth authorization, consent, client registration/discovery, token issuance,
|
|
535
|
+
and MCP resource verification. `@cloudflare/workers-oauth-provider` and Core's
|
|
536
|
+
`OAUTH_KV` requirement are removed. This amendment supersedes only the
|
|
537
|
+
2026-05-15 transport carve-out; adapter ownership, the curated `Auth` facade,
|
|
538
|
+
fresh D1 staff-role checks, the single `mcp` compatibility scope, and normalized
|
|
539
|
+
`HandlerContext.auth` remain unchanged.
|
|
540
|
+
|
|
541
|
+
### Provider and resource boundary
|
|
542
|
+
|
|
543
|
+
`createAuth` keeps its general OAuth-provider capability and adds MCP as an
|
|
544
|
+
explicit curated mode, implemented by composing `jwt()` and
|
|
545
|
+
`@better-auth/mcp`. It is not replaced by an MCP-only factory and no Better
|
|
546
|
+
Auth passthrough is exposed. Standard Workers bind one canonical protected
|
|
547
|
+
resource, `${PUBLIC_ORIGIN}/mcp`. Both `/mcp` and `/mcp/staff` accept tokens for
|
|
548
|
+
that resource; `/mcp/staff` remains a stricter server-side role projection, not
|
|
549
|
+
a second OAuth audience.
|
|
550
|
+
|
|
551
|
+
The adapter continues to verify and normalize credentials before calling the
|
|
552
|
+
portable runtime. Better Auth imports remain confined to the default
|
|
553
|
+
implementation. A host app may still implement the `Auth` facade or mount its
|
|
554
|
+
own low-level routes; the generated convenience path does not make Better Auth
|
|
555
|
+
a runtime dependency.
|
|
556
|
+
|
|
557
|
+
MCP request verification reuses Better Auth's DPoP binding primitive and its
|
|
558
|
+
database-backed replay store. Bearer JWTs remain valid; a DPoP-bound JWT is
|
|
559
|
+
accepted only with a matching request proof, method, URL, token hash, and
|
|
560
|
+
single-use proof id. Raw-token callers cannot bypass that request boundary.
|
|
561
|
+
|
|
562
|
+
### CIMD is primary; DCR is bounded compatibility
|
|
563
|
+
|
|
564
|
+
The MCP mode composes:
|
|
565
|
+
|
|
566
|
+
```ts
|
|
567
|
+
mcp({ resource, loginPage, consentPage, scopes: ["mcp"], ... })
|
|
568
|
+
cimd({
|
|
569
|
+
fetchClientMetadataResource,
|
|
570
|
+
metadataProfile: "mcp-2026-07-28",
|
|
571
|
+
})
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
Cloudflare's native `fetch` is the metadata transport only when the Worker has
|
|
575
|
+
`global_fetch_strictly_public` enabled. The runtime flag makes the subrequest
|
|
576
|
+
use Cloudflare's public-Internet routing boundary; Better Auth owns URL
|
|
577
|
+
validation, timeout, response limits, redirect refusal, revalidation, and the
|
|
578
|
+
bounded fetch governor. Mantle does not add a DNS resolver, socket HTTP client,
|
|
579
|
+
or generic transport abstraction.
|
|
580
|
+
|
|
581
|
+
Unauthenticated DCR remains enabled only for older MCP clients. Its lifetime
|
|
582
|
+
stays at the removed provider's 90-day default. Better Auth expires confidential
|
|
583
|
+
registration secrets; Mantle additionally prunes only expired ownerless DCR
|
|
584
|
+
rows (`clientDiscoveryId`, user owner, and reference owner all absent) on
|
|
585
|
+
OAuth traffic. CIMD-owned and operator-managed clients are never cleanup
|
|
586
|
+
candidates. Cleanup is storage hygiene: failures are logged and do not turn a
|
|
587
|
+
valid authorization request into an outage. No cron or second registry is
|
|
588
|
+
introduced.
|
|
589
|
+
|
|
590
|
+
Better Auth's `oauthClient` row is the connected-client authority. It retains
|
|
591
|
+
the discovery provenance and validated name, URI, redirect URIs, application
|
|
592
|
+
type, and private server metadata. The consent UI reads only the public
|
|
593
|
+
secret-free projection. Remote client metadata is not copied into portable
|
|
594
|
+
`HandlerContext` or deferred event envelopes.
|
|
595
|
+
|
|
596
|
+
### Breaking migration
|
|
597
|
+
|
|
598
|
+
All Better Auth packages upgrade together to 1.7. The D1 schema adopts account
|
|
599
|
+
issuer identity, resource/client relationships, resource-bound token and
|
|
600
|
+
consent fields, discovery provenance, and replay storage required by the
|
|
601
|
+
installed plugins. Removed `validAudiences` configuration becomes the one
|
|
602
|
+
explicit MCP resource; generic upstream OAuth adopts the 1.7 social sign-in and
|
|
603
|
+
callback contract.
|
|
604
|
+
|
|
605
|
+
This is an alpha breaking migration. KV registrations, grants, and opaque
|
|
606
|
+
tokens are not migrated into D1 because their issuer/resource provenance cannot
|
|
607
|
+
be established safely; MCP clients reconnect through CIMD or DCR. Existing
|
|
608
|
+
pre-1.7 alpha auth databases are reset and re-bootstrapped rather than receiving
|
|
609
|
+
a guessed account issuer backfill.
|
|
610
|
+
|
|
611
|
+
The authorization endpoints consequently move from `/oauth/*` to Better
|
|
612
|
+
Auth's `/api/auth/oauth2/*` discovery-advertised endpoints. No compatibility
|
|
613
|
+
aliases are retained: existing KV client identifiers are invalid after the
|
|
614
|
+
authority change regardless, and standards-compliant clients rediscover the
|
|
615
|
+
new endpoints.
|
|
616
|
+
|
|
617
|
+
### Downstream ownership
|
|
618
|
+
|
|
619
|
+
Starters remove their obsolete OAuth package and `OAUTH_KV` binding while
|
|
620
|
+
retaining `global_fetch_strictly_public`. Landing opts its custom `createAuth`
|
|
621
|
+
construction into the same MCP mode. Landing may keep an `OAUTH_KV` binding for
|
|
622
|
+
its own launch/bootstrap state; that storage is unrelated to the removed Core
|
|
623
|
+
OAuth store and is not renamed by this decision.
|
|
624
|
+
|
|
625
|
+
This amendment adopts the 2026-07-28 CIMD authorization profile only. Updating
|
|
626
|
+
Mantle's JSON-RPC dispatcher to the complete MCP 2026-07-28 transport revision
|
|
627
|
+
is a separate decision.
|
|
@@ -19,9 +19,30 @@ Core (SDK producer)
|
|
|
19
19
|
-> published npm contract
|
|
20
20
|
mantle-starters (external consumer and bundle producer)
|
|
21
21
|
-> immutable provision bundle
|
|
22
|
-
mantle-landing (provisioner)
|
|
22
|
+
mantle-landing (provisioner) Core CLI (`mantle create`)
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
> **Amended 2026-08-19 (#699).** The chain above is no longer linear. Core's
|
|
26
|
+
> umbrella CLI is now also a consumer of the immutable bundle: `mantle create`
|
|
27
|
+
> resolves the official `v${packageVersion}` starter tag, renders it through
|
|
28
|
+
> the environment-neutral module Core owns, and writes a local project. Core
|
|
29
|
+
> is therefore both the upstream producer of the npm contract and a downstream
|
|
30
|
+
> consumer of the release train it starts.
|
|
31
|
+
>
|
|
32
|
+
> This does not move starter content into Core, so the decision below stands:
|
|
33
|
+
> starters still author the bundles and still validate the published SDK as an
|
|
34
|
+
> external consumer. What changed is the supporting argument. Two consequences
|
|
35
|
+
> are worth stating rather than rediscovering:
|
|
36
|
+
>
|
|
37
|
+
> - **The published-consumer guarantee is now proven twice.** `mantle create`
|
|
38
|
+
> materializes a project that installs the published package, so a break in
|
|
39
|
+
> the npm contract fails in Core's own release smoke as well as in starter CI.
|
|
40
|
+
> - **Reconsideration input 1 is being answered.** #699 replaces the release
|
|
41
|
+
> order so a candidate is published under a temporary dist-tag, validated
|
|
42
|
+
> against the exact packed artifacts, and only then promoted. That was listed
|
|
43
|
+
> below as an unresolved prerequisite for any future merge; when it lands,
|
|
44
|
+
> this ADR should record it as resolved rather than pending.
|
|
45
|
+
|
|
25
46
|
The repositories were originally split because premium starters needed a
|
|
26
47
|
private ACL. That reason does not determine where public starters must live,
|
|
27
48
|
and the private premium repository remains a stub. A later decision, #191,
|