@aotter/mantle 0.0.11-alpha.16 → 0.0.11-alpha.19

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 CHANGED
@@ -46,6 +46,12 @@ The `mantle-runtime` package never imports Cloudflare-specific types — adapter
46
46
 
47
47
  ## Documentation
48
48
 
49
+ - Embedded docs and agent skills ship inside this npm package for
50
+ generated-site agents:
51
+ - `node_modules/@aotter/mantle/docs/design-atoms.md`
52
+ - `node_modules/@aotter/mantle/docs/adr/`
53
+ - `node_modules/@aotter/mantle/skills/install/SKILL.md`
54
+ - `node_modules/@aotter/mantle/skills/provision/SKILL.md`
49
55
  - [Repo](https://github.com/aotter/mantle)
50
56
  - [4-atom manifest model (ADR-0001)](https://github.com/aotter/mantle/blob/develop/docs/adr/0001-four-atom-manifest-model.md)
51
57
  - [Release process](https://github.com/aotter/mantle/blob/develop/docs/release-process.md)
@@ -0,0 +1,108 @@
1
+ # Adapter implementation guide
2
+
3
+ This guide is the fresh-developer entry point for implementing a new mantle platform adapter.
4
+
5
+ Read this with [ADR-0011](adr/0011-adapter-port-spec.md). The source of truth for TypeScript shapes is `packages/mantle-runtime/src/domain/port/`.
6
+
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
+
9
+ ## Required runtime ports
10
+
11
+ A first-run adapter must implement exactly these three runtime ports:
12
+
13
+ | Contract | Source | Cloudflare example |
14
+ |---|---|---|
15
+ | `DatabaseDriver` plus `PreparedStatement` and `MigrationRunner` | `packages/mantle-runtime/src/domain/port/DatabaseDriver.ts` | `packages/adapters/cloudflare/src/bindings/D1DatabaseDriver.ts` |
16
+ | `KvCache` | `packages/mantle-runtime/src/domain/port/KvCache.ts` | `packages/adapters/cloudflare/src/bindings/KvCacheBinding.ts` |
17
+ | `AssetServer` | `packages/mantle-runtime/src/domain/port/AssetServer.ts` | `packages/adapters/cloudflare/src/bindings/AssetsAssetServer.ts` |
18
+
19
+ The runtime must not import platform types such as `D1Database`, `KVNamespace`, Cloudflare `Fetcher`, Netlify request objects, Postgres pools, or adapter SDK types. Those live in adapter packages.
20
+
21
+ ## Optional capabilities
22
+
23
+ Optional ports are enabled only when a feature needs them:
24
+
25
+ | Contract | Source | Required when |
26
+ |---|---|---|
27
+ | `MediaStorage` | `packages/mantle-runtime/src/domain/port/MediaStorage.ts` | The adapter exposes admin/MCP media upload flows. |
28
+ | `DeferredHookDispatcher` | `packages/mantle-runtime/src/domain/port/DeferredHookDispatcher.ts` | The adapter wants durable queue delivery for `after_*` lifecycle hooks. |
29
+
30
+ Test seams such as `Clock` and `IdGenerator` are injectable through `createCmsRuntime`, but normal adapters do not need custom implementations.
31
+
32
+ ## Runtime boot
33
+
34
+ Adapters compose the runtime through `createCmsRuntime`:
35
+
36
+ ```ts
37
+ import { createCmsRuntime } from "@aotter/mantle-runtime";
38
+
39
+ const runtime = createCmsRuntime({
40
+ manifests,
41
+ handlers,
42
+ templates,
43
+ siteDefaults,
44
+ db,
45
+ kv,
46
+ assets,
47
+ publicPathResolver,
48
+ mediaStorage,
49
+ deferredHookDispatcher,
50
+ });
51
+
52
+ await runtime.bootInit();
53
+ ```
54
+
55
+ `bootInit()` runs canonical migrations, seeds `siteDefaults`, and validates the manifest set. Call it once before serving CMS traffic. The Cloudflare adapter's reference pattern is `packages/adapters/cloudflare/src/mount/bootRuntimeOnce.ts`.
56
+
57
+ ## HTTP and MCP surfaces
58
+
59
+ The runtime is a library, not an HTTP server. A new adapter must mount equivalent framework routes:
60
+
61
+ | Surface | Adapter responsibility | Cloudflare reference |
62
+ |---|---|---|
63
+ | Public/admin HTTP endpoints | Route HTTP Triggers, View REST endpoints, admin SPA assets, and public render routes into runtime use cases. | `packages/adapters/cloudflare/src/mount/mountServerEndpoints.ts`, `mountPublicRoutes.ts` |
64
+ | Auth endpoints | Own sign-in/session/OAuth metadata routes through the adapter's Better Auth integration. | `packages/adapters/cloudflare/src/auth/createAuth.ts`, `mountServerEndpoints.ts` |
65
+ | MCP endpoints | Mount `/mcp/staff` and `/mcp` via `createOAuthProvider({ apiHandlers })`; the OAuth lib verifies bearer tokens against its KV grant store, then calls the matching apiHandler with `ctx.props` set. The adapter enforces the staff D1 role inside the apiHandler, then dispatches JSON-RPC. | `packages/adapters/cloudflare/src/mount/mountMcp.ts`, `oauth/oauthSingleton.ts`, `oauth/mountOAuth.ts` |
66
+
67
+ 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`.
68
+
69
+ Minimum HTTP behavior for a full adapter:
70
+
71
+ - Route manifest HTTP Triggers to `runtime.invokeProcedure`.
72
+ - Route `GET /api/views/<name>` to `runtime.executeView`.
73
+ - Mount admin content APIs with session/role checks before calling runtime content use cases.
74
+ - Serve admin SPA assets through `AssetServer`, with an SPA catchall for admin client-side routes.
75
+ - Mount public render routes and markdown mirrors when the starter exposes public pages.
76
+ - Translate runtime diagnostics and validation failures into stable HTTP JSON responses instead of throwing raw errors.
77
+
78
+ Minimum auth/MCP behavior:
79
+
80
+ - Provide Better Auth-compatible sign-in/session routes for the platform.
81
+ - Validate `/mcp/staff` requests with the staff D1 admin role (`owner`/`editor`/`contributor`).
82
+ - Validate `/mcp` requests with any authenticated session (D1 role check is surface-driven, not OAuth-scope-driven — claude.ai rejects colon-shaped scopes).
83
+ - Advertise a single non-colon scope (default `["mcp"]`) in `scopes_supported`. Per-surface enforcement happens server-side in the apiHandler.
84
+ - Build `McpAuthContext` from the validated session and pass it to `McpJsonRpcDispatcher`.
85
+ - Build procedure `HandlerContext` with `user`, `staff`, adapter `env`, and optional `waitUntil`.
86
+
87
+ ## Static assets
88
+
89
+ `AssetServer` is required because every adapter must have a strategy for serving the prebuilt admin UI from `@aotter/mantle-admin-ui`. The adapter may serve those files from platform assets, a static publish directory, object storage plus CDN, or a filesystem bundle. Return `null` from `AssetServer.fetch()` when a specific asset is not found so the adapter can fall back to the admin SPA catchall.
90
+
91
+ ## Implementation checklist
92
+
93
+ - [ ] Implement `DatabaseDriver`, including canonical migration tracking.
94
+ - [ ] Implement `KvCache`, including prefix listing and opaque cursors.
95
+ - [ ] Implement `AssetServer` for the admin UI assets.
96
+ - [ ] Compose `createCmsRuntime` with manifests, handlers, templates, site defaults, and required ports.
97
+ - [ ] Call `bootInit()` before serving CMS traffic.
98
+ - [ ] Mount HTTP Trigger and View REST surfaces.
99
+ - [ ] Mount admin/public render routes and admin SPA assets.
100
+ - [ ] Provide adapter-owned Better Auth wiring and session helpers.
101
+ - [ ] Mount `/mcp/staff` and `/mcp` via the platform's OAuth provider lib (Cloudflare adapter uses `@cloudflare/workers-oauth-provider` at top level). Enforce staff D1 role inside the apiHandler.
102
+ - [ ] Add optional `MediaStorage` or `DeferredHookDispatcher` only when the adapter supports those features.
103
+ - [ ] Verify the runtime package still has no platform-specific imports.
104
+
105
+ ## Current non-goals
106
+
107
+ - Do not add `SessionRepository`, `OAuthVerifier`, `UserRepository`, or `StaffRepository` runtime ports. Those were pre-ADR-0014 concepts and are not part of the current adapter contract.
108
+ - Do not add a second canonical migration chain for a new adapter. The runtime owns canonical migrations; adapters execute them through `DatabaseDriver.migrations`.