@aotter/mantle 0.1.0-alpha.7 → 0.1.0-alpha.9

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.
Files changed (106) hide show
  1. package/README.md +68 -30
  2. package/dist/admin.d.ts +2 -0
  3. package/dist/admin.d.ts.map +1 -0
  4. package/dist/admin.js +2 -0
  5. package/dist/admin.js.map +1 -0
  6. package/dist/bun.d.ts +2 -0
  7. package/dist/bun.d.ts.map +1 -0
  8. package/dist/bun.js +2 -0
  9. package/dist/bun.js.map +1 -0
  10. package/dist/cli/create.d.ts +2 -0
  11. package/dist/cli/create.d.ts.map +1 -0
  12. package/dist/cli/create.js +243 -0
  13. package/dist/cli/create.js.map +1 -0
  14. package/dist/cli/generate.d.ts.map +1 -0
  15. package/dist/cli/generate.js +101 -0
  16. package/dist/cli/generate.js.map +1 -0
  17. package/dist/cli/harness.d.ts +3 -0
  18. package/dist/cli/harness.d.ts.map +1 -0
  19. package/dist/{harness-cli.js → cli/harness.js} +13 -8
  20. package/dist/cli/harness.js.map +1 -0
  21. package/dist/cli/main.d.ts +3 -0
  22. package/dist/cli/main.d.ts.map +1 -0
  23. package/dist/{cli.js → cli/main.js} +6 -8
  24. package/dist/cli/main.js.map +1 -0
  25. package/dist/cli/skills.d.ts +3 -0
  26. package/dist/cli/skills.d.ts.map +1 -0
  27. package/dist/{skills.js → cli/skills.js} +51 -6
  28. package/dist/cli/skills.js.map +1 -0
  29. package/dist/cli/update.d.ts.map +1 -0
  30. package/dist/{update.js → cli/update.js} +72 -46
  31. package/dist/cli/update.js.map +1 -0
  32. package/dist/codegen/emitMantleModule.d.ts +16 -0
  33. package/dist/codegen/emitMantleModule.d.ts.map +1 -0
  34. package/dist/codegen/emitMantleModule.js +217 -0
  35. package/dist/codegen/emitMantleModule.js.map +1 -0
  36. package/dist/codegen.d.ts +2 -0
  37. package/dist/codegen.d.ts.map +1 -0
  38. package/dist/codegen.js +2 -0
  39. package/dist/codegen.js.map +1 -0
  40. package/dist/provision/renderProvisionBundle.d.ts +70 -0
  41. package/dist/provision/renderProvisionBundle.d.ts.map +1 -0
  42. package/dist/provision/renderProvisionBundle.js +367 -0
  43. package/dist/provision/renderProvisionBundle.js.map +1 -0
  44. package/dist/provision.d.ts +2 -0
  45. package/dist/provision.d.ts.map +1 -0
  46. package/dist/provision.js +2 -0
  47. package/dist/provision.js.map +1 -0
  48. package/dist/vercel-libsql.d.ts +2 -0
  49. package/dist/vercel-libsql.d.ts.map +1 -0
  50. package/dist/vercel-libsql.js +2 -0
  51. package/dist/vercel-libsql.js.map +1 -0
  52. package/dist/vercel.d.ts +2 -0
  53. package/dist/vercel.d.ts.map +1 -0
  54. package/dist/vercel.js +2 -0
  55. package/dist/vercel.js.map +1 -0
  56. package/dist/web.d.ts +2 -0
  57. package/dist/web.d.ts.map +1 -0
  58. package/dist/web.js +2 -0
  59. package/dist/web.js.map +1 -0
  60. package/docs/adapter-guide.md +88 -45
  61. package/docs/adr/0001-four-atom-manifest-model.md +11 -3
  62. package/docs/adr/0007-ai-as-primary-author.md +3 -3
  63. package/docs/adr/0009-consumer-supplied-manifests.md +9 -4
  64. package/docs/adr/0011-adapter-port-spec.md +18 -4
  65. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +111 -6
  66. package/docs/adr/0018-core-starters-repository-boundary.md +22 -1
  67. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +204 -0
  68. package/docs/adr/README.md +12 -10
  69. package/docs/api-mcp-authorization.md +21 -42
  70. package/docs/assets/mantle-admin-operations.png +0 -0
  71. package/docs/assets/mantle-hero.jpg +0 -0
  72. package/docs/auth-hosting-model.md +5 -5
  73. package/docs/cloudflare-low-level-composition.md +30 -18
  74. package/docs/deferred-lifecycle-queues.md +20 -38
  75. package/docs/design-atoms.md +31 -133
  76. package/docs/labels.md +1 -1
  77. package/docs/media-uploads.md +2 -2
  78. package/docs/migration-0.1.2.md +45 -0
  79. package/docs/performance-harness.md +4 -4
  80. package/docs/release-process.md +72 -22
  81. package/docs/sealed-pipeline-ownership.md +100 -0
  82. package/package.json +84 -14
  83. package/skills/README.md +38 -7
  84. package/skills/develop/SKILL.md +7 -5
  85. package/skills/install/SKILL.md +26 -25
  86. package/skills/media-gc/SKILL.md +6 -0
  87. package/skills/plugin/SKILL.md +1 -0
  88. package/skills/provision/SKILL.md +2 -0
  89. package/skills/theme/SKILL.md +1 -0
  90. package/skills/update/SKILL.md +1 -0
  91. package/dist/cli.d.ts +0 -3
  92. package/dist/cli.d.ts.map +0 -1
  93. package/dist/cli.js.map +0 -1
  94. package/dist/generate.d.ts.map +0 -1
  95. package/dist/generate.js +0 -226
  96. package/dist/generate.js.map +0 -1
  97. package/dist/harness-cli.d.ts +0 -3
  98. package/dist/harness-cli.d.ts.map +0 -1
  99. package/dist/harness-cli.js.map +0 -1
  100. package/dist/skills.d.ts +0 -2
  101. package/dist/skills.d.ts.map +0 -1
  102. package/dist/skills.js.map +0 -1
  103. package/dist/update.d.ts.map +0 -1
  104. package/dist/update.js.map +0 -1
  105. /package/dist/{generate.d.ts → cli/generate.d.ts} +0 -0
  106. /package/dist/{update.d.ts → cli/update.d.ts} +0 -0
@@ -0,0 +1,204 @@
1
+ # ADR-0019: Seal the manifest-to-runtime pipeline
2
+
3
+ **Status:** Accepted
4
+
5
+ **Date:** 2026-08-16
6
+
7
+ **Related:** [#656](https://github.com/aotter/mantle/issues/656),
8
+ [#662](https://github.com/aotter/mantle/issues/662),
9
+ [#546](https://github.com/aotter/mantle/issues/546), ADR-0008, ADR-0009,
10
+ ADR-0011, ADR-0018
11
+
12
+ ## Context
13
+
14
+ Mantle already has an adapter-neutral runtime package and structured
15
+ diagnostics, but its real execution boundary is still the complete Cloudflare
16
+ site product. Authored YAML is parsed into an ordinary `Manifest[]`; validation,
17
+ defaulting, lookup construction, boot checks, SQL compilation, storage setup,
18
+ public rendering, and Admin helpers then overlap across spec, runtime, and the
19
+ Cloudflare adapter.
20
+
21
+ That overlap creates two portability failures:
22
+
23
+ 1. A downstream caller can bypass an earlier stage and reinterpret raw
24
+ manifests, so a rule or default can have more than one owner.
25
+ 2. Embedding the runtime also selects SQL-shaped storage, public Web behavior,
26
+ Admin assets, and Cloudflare-oriented boot conventions.
27
+
28
+ The product needs one embeddable Core that can be called directly by an
29
+ existing application, with Web, Admin, and platform integrations selected
30
+ downstream. The migration cannot introduce a permanent `v2` pipeline beside
31
+ the current one: every compatibility path must delegate forward and be deleted
32
+ within the same milestone.
33
+
34
+ ## Decision
35
+
36
+ ### One sealed pipeline
37
+
38
+ Mantle has one supported semantic path:
39
+
40
+ ```text
41
+ ManifestSourceSet
42
+ -> parse + normalize
43
+ -> ParsedManifestSet
44
+ -> link
45
+ -> LinkedManifestSet
46
+ -> compile
47
+ -> RuntimePlan
48
+ -> prepare deployment
49
+ -> PreparedRevision
50
+ -> bind runtime
51
+ -> MantleRuntime
52
+ ```
53
+
54
+ Decode may remain an internal parser step. The public contract starts with
55
+ caller-supplied sources and ends with a programmatic runtime. A failed stage
56
+ does not produce a usable value for the next stage.
57
+
58
+ Parse, link, and compile are pure and deterministic. Their sealed outputs may
59
+ be constructed only by their owning package, apart from explicit test fixture
60
+ helpers. Source locations remain authored metadata; they do not affect the
61
+ semantic fingerprint.
62
+
63
+ ### One owner per invariant
64
+
65
+ | Stage | Owns | Must not own |
66
+ |---|---|---|
67
+ | Parse + normalize | YAML syntax/alias limits, closed four-atom shape, primitive and atom-local rules, behavior-affecting defaults, source metadata | Cross-atom references, handlers, storage, optional routes |
68
+ | Link | Duplicate symbols, cross-atom references, guard graphs, translations, manifest-owned route/tool collisions | I/O, selected modules, handler availability |
69
+ | Compile | Immutable lookup records, authorization plans, Trigger indices, Procedure descriptors, logical View plans, semantic fingerprint | Connections, repositories, handlers, requests, templates, assets |
70
+ | Prepare | Selected storage migrations/indexes/native Views, handler availability, selected capability/route checks, readiness revision | YAML interpretation or request execution |
71
+ | Bind/invoke | Semantic ports, handler dispatch, parameter binding, centralized authorization, content/View/Procedure/Trigger/lifecycle operations | DDL, route mounting, assets, HTTP/session/cache policy |
72
+ | Optional modules/adapters | Web/Admin composition and request/session/cache/platform translation | Re-parsing, re-linking, or a second authorization/runtime stack |
73
+
74
+ The current-to-target rule ledger and evidence are maintained in
75
+ [`docs/sealed-pipeline-ownership.md`](../sealed-pipeline-ownership.md).
76
+
77
+ ### Core and optional products
78
+
79
+ `@aotter/mantle-spec` owns source, parse, normalize, link, introspection, and
80
+ manifest-derived code generation. It has no runtime or platform dependency.
81
+
82
+ `@aotter/mantle-runtime` owns `RuntimePlan`, preparation contracts, semantic
83
+ storage ports, and `MantleRuntime`. The portable runtime input is prepared
84
+ semantic storage, not `DatabaseDriver`. It has no Web, Admin, Cloudflare, Bun,
85
+ or Vercel dependency.
86
+
87
+ `@aotter/mantle-web` is optional and owns the official public HTML, Markdown,
88
+ `llms.txt`, sitemap, SEO, preview, template, and public-path composition.
89
+ Applications still own their router, URLs, navigation, and design. Platform
90
+ adapters own HTTP mounting and cache behavior.
91
+
92
+ `@aotter/mantle-admin` is optional and owns Admin API/application
93
+ orchestration and its asset contract. `@aotter/mantle-admin-ui` remains the UI
94
+ artifact. Core supplies content operations and authorization but does not
95
+ serve the UI or reserve Admin paths when the module is absent.
96
+
97
+ Cloudflare, Bun, and Vercel packages bind platform storage, lifecycle,
98
+ request/session, cache, and asset concerns. They may expose convenience
99
+ facades, but those facades compose the same Core and selected modules.
100
+
101
+ ### Storage and Views
102
+
103
+ Preparation accepts a selected storage adapter and produces semantic ports,
104
+ including existing content repositories/readers and a `ViewQueryExecutor`.
105
+ Concrete D1/SQLite drivers and SQL repositories remain implementation details.
106
+ An existing application may either pass its already-owned database/client to
107
+ an official adapter or implement the semantic ports over its own tables.
108
+
109
+ Declarative Views compile to logical plans once. Storage preparation lowers
110
+ those plans to native queries. The v0.1 `View.spec.sql` form remains explicitly
111
+ SQLite-only and is rejected by unsupported storage during preparation; Mantle
112
+ does not guess a translation and does not add a universal query driver.
113
+
114
+ ### Naming and code generation
115
+
116
+ The Core execution unit is `MantleRuntime`, not a site. Optional TypeScript
117
+ generation is a pure projection of linked/compiled semantics. It exposes
118
+ `bindMantle(runtime)` plus an eager `createMantle()` convenience that delegates
119
+ one preparation attempt to Runtime. Generated code never caches, retries,
120
+ mounts routes, or owns host lifecycle. The bound API retains its raw `runtime`
121
+ so typed projection does not hide lower-level Core capabilities. It emits
122
+ deterministic lower-camel identifiers while preserving manifest wire names
123
+ internally. Identifier collisions are errors.
124
+
125
+ Code generation is never required by the runtime and never copies Web or Admin
126
+ assets. Dynamic/string-based calls remain the escape hatch for JavaScript and
127
+ runtime-defined consumers.
128
+
129
+ ### Migration rule
130
+
131
+ Issues #663 through #673 move ownership in pipeline order. At every step:
132
+
133
+ - existing callers may use a temporary wrapper only if it delegates to the new
134
+ owner;
135
+ - no rule, default, query compiler, authorization evaluator, or router is
136
+ implemented twice;
137
+ - the old owner and its obsolete tests are deleted when its last caller moves;
138
+ - exact packed consumers, not workspace links, are the final compatibility
139
+ gate.
140
+
141
+ Breaking API changes from the `v0.1.0-alpha.7` contract are allowed in the
142
+ 0.1.2 milestone. Migration notes should keep old projects mechanically
143
+ adaptable, but downward compatibility is not a reason to retain a second stack.
144
+
145
+ ## Consequences
146
+
147
+ ### Positive
148
+
149
+ - Human and agent authors get one deterministic path and one diagnostic owner.
150
+ - Existing applications can embed Core without surrendering process, router,
151
+ database, or transaction ownership.
152
+ - Web, Admin, and each platform can evolve without entering Core.
153
+ - Performance work happens once per semantic revision instead of per request.
154
+ - Compatibility code has a defined deletion point.
155
+
156
+ ### Negative
157
+
158
+ - 0.1.2 intentionally breaks parts of the alpha.7 API.
159
+ - Cloudflare's current convenience facade must be decomposed and then
160
+ reassembled from optional modules.
161
+ - Downstream projects must update generated imports and runtime construction.
162
+ - Exact consumer tests are required across more than one platform.
163
+
164
+ ## Alternatives
165
+
166
+ ### Keep `Manifest[]` as the shared contract
167
+
168
+ Rejected. It cannot prove that defaults, references, and plans were evaluated
169
+ once, and it lets every downstream package rebuild semantic state.
170
+
171
+ ### Generalize `DatabaseDriver` for every database
172
+
173
+ Rejected. Its SQL/D1 shape is not a useful MongoDB or application-domain
174
+ contract. Existing semantic repository ports are the smaller portable seam.
175
+
176
+ ### Add a plugin/provider framework
177
+
178
+ Rejected. Ordinary package dependencies, exported mount functions, and narrow
179
+ ports cover the known compositions. A registry would add a second architecture
180
+ before a use case requires it.
181
+
182
+ ### Build a parallel Core v2 and migrate later
183
+
184
+ Rejected. It duplicates rules and makes deletion optional. The migration is
185
+ ordered specifically so each new owner replaces the old owner in place.
186
+
187
+ ## How to apply
188
+
189
+ - Start a pipeline change at the current owner listed in the rule ledger.
190
+ - Add one check at the new owner, route every caller through it, then delete the
191
+ old implementation and obsolete tests.
192
+ - Reject runtime imports of raw manifests, Web/Admin packages, or platform
193
+ primitives once their migration issue closes.
194
+ - Prefer current ports and native platform APIs; add a package only for a real
195
+ dependency/runtime boundary.
196
+ - PR descriptions for #656 must state old owner, new owner, evidence, deleted
197
+ code, and any diagnostic timing change.
198
+
199
+ ## Implementation status
200
+
201
+ Issue #662 records the current behavior, ownership ledger, consumer corpus, and
202
+ performance baseline. Issues #663 through #674 execute the migration and final
203
+ repository cleanup. The ADR is complete only when #673 removes every temporary
204
+ compatibility path; #674 then updates contributor guidance to the final tree.
@@ -6,31 +6,33 @@ Records of *why* mantle ended up shaped this way. The numbering preserves POC AD
6
6
 
7
7
  | # | Title | Status |
8
8
  |---|---|---|
9
- | [0001](0001-four-atom-manifest-model.md) | Four-atom manifest model (Schema / View / Procedure / Trigger). Folds POC ADR-0005 (grammar discipline) and POC ADR-0006 (multi-doc YAML). | Accepted (refreshed) |
9
+ | [0001](0001-four-atom-manifest-model.md) | Four-atom manifest model (Schema / View / Procedure / Trigger). Folds POC ADR-0005 (grammar discipline) and POC ADR-0006 (multi-doc YAML). | Accepted; fixed-file contract superseded |
10
10
  | [0002](0002-closed-enums-for-bindings.md) | Closed enums for `x-mantle-bind` and `ctx.*` predicates. | Accepted (refreshed) |
11
11
  | [0007](0007-ai-as-primary-author.md) | AI is the primary author of consumer projects; SDK contract is three pre-serve feedback loops, runtime diagnostics, and coder/operator role surfaces. | Accepted + amended |
12
12
  | [0008](0008-structured-diagnostic-shape.md) | Diagnostic shape for validate/boot/runtime failures, with a reserved consumer-test phase; measured harnesses keep purpose-shaped reports. | Accepted + amended |
13
- | [0009](0009-consumer-supplied-manifests.md) | Consumers own manifest YAML; the installed CLI emits the parser-free runtime module and handler types. Core ships no application manifests. | Accepted + amended |
13
+ | [0009](0009-consumer-supplied-manifests.md) | Historical generated-array workflow; consumer source ownership continues in ADR-0019. | Superseded by 0019 |
14
14
  | [0010](0010-locale-and-translates.md) | Locale 3-layer (manifest / D1 site_config / data field) + translates pattern. Boot decoupled from `site_config` (issue #60 fix). | Accepted (refreshed) |
15
15
  | [0011](0011-adapter-port-spec.md) | Adapter port spec. Required runtime ports plus optional feature ports. | Accepted (new) |
16
16
  | [0012](0012-views-as-public-rest.md) | Views auto-expose matching REST and `query_view_*` MCP reads on their declared `public` or `staff` surface. Schemas never get a public REST endpoint. | Accepted + amended |
17
17
  | [0013](0013-agent-provisioned-consumer-projects.md) | Historical agent-provisioned consumer projects path. Superseded for first launch by landing provision bundles. | Superseded |
18
- | [0014](0014-auth-better-auth-and-multi-tenant-mcp.md) | The Cloudflare adapter owns the curated Better Auth identity/session facade and top-level `@cloudflare/workers-oauth-provider` MCP transport. Both normalize verified callers into runtime context; mutable staff role and target authorization are re-evaluated per call. | Accepted + amended |
18
+ | [0014](0014-auth-better-auth-and-multi-tenant-mcp.md) | The Cloudflare adapter owns one curated Better Auth 1.7 identity/OAuth/MCP authority with CIMD discovery. Verified callers are normalized into runtime context; mutable staff role and target authorization are re-evaluated per call. | Accepted + amended |
19
19
  | [0016](0016-site-semantic-layer.md) | Site semantic layer: `AGENTS.md` (cross-tool entry) + `.mantle/launch-state.json` (deterministic install context). The older `mantle/site.md` letter surface is suspended from first-run scaffolds. | Accepted (slimmed) |
20
20
  | [0017](0017-media-multi-variant-agent-side-optimization.md) | Multi-variant media assets with agent-side optimization and asset-id entry references. | Accepted |
21
21
  | [0018](0018-core-starters-repository-boundary.md) | Core produces published SDK artifacts; the separate starters repository validates them as an external consumer. Revisit after release-contract simplification. | Accepted for now |
22
+ | [0019](0019-sealed-manifest-runtime-pipeline.md) | One sealed source-to-runtime pipeline, semantic storage seam, and optional Web/Admin/platform dependency direction. | Accepted |
22
23
 
23
24
  ## Reading order
24
25
 
25
26
  If you're new to the codebase:
26
27
 
27
28
  1. **0001** — what the 4 atoms are.
28
- 2. **0009** — how consumers wire them in.
29
- 3. **0007** — what running the SDK feels like as an AI author (and as the operator agent).
30
- 4. **0011** — the boundary between the runtime and the adapter (most load-bearing for the rebuild).
31
- 5. **0010** — how locale flows through the system.
32
- 6. **0013** — historical install-session context; current first launch is landing provision bundles plus repo-local handoff.
33
- 7. **0002, 0008** — the two ADRs that touch every diagnostic and every binding.
29
+ 2. **0019** — the sealed source-to-runtime pipeline and optional product boundaries.
30
+ 3. **0009** — historical context for consumer-owned manifests.
31
+ 4. **0007** — what running the SDK feels like as an AI author (and as the operator agent).
32
+ 5. **0011** — the boundary between the runtime and the adapter.
33
+ 6. **0010** — how locale flows through the system.
34
+ 7. **0013** — historical install-session context; current first launch is landing provision bundles plus repo-local handoff.
35
+ 8. **0002, 0008** — the two ADRs that touch every diagnostic and every binding.
34
36
 
35
37
  ## What's NOT here (and why)
36
38
 
@@ -52,7 +54,7 @@ The rebuild's ADR-0011 (new) is the most load-bearing addition — the POC accum
52
54
 
53
55
  ## Contributing a new ADR
54
56
 
55
- 1. Pick the next number (currently 0019).
57
+ 1. Pick the next number (currently 0020).
56
58
  2. File: `docs/adr/<NNNN>-<kebab-title>.md`.
57
59
  3. Sections: Status, Date, Context, Decision, Consequences, Alternatives, How to apply, Implementation status.
58
60
  4. Link from this README's table.
@@ -137,7 +137,7 @@ enforced before the Procedure or View runs.
137
137
 
138
138
  ## Cloudflare consumer wiring
139
139
 
140
- Pass one site-owned resolver to `createCmsRef`. Return `not-handled` when the
140
+ Pass one site-owned resolver to `createMantleRuntimeRef`. Return `not-handled` when the
141
141
  request is not one of the site's credential formats, `invalid` when it is a
142
142
  recognized but bad/revoked credential, and `verified` only after checking the
143
143
  authoritative site record.
@@ -220,53 +220,31 @@ function parseScopes(json: string): string[] | null {
220
220
  }
221
221
  ```
222
222
 
223
- Wire it alongside the existing Auth facade. `oauthBearer` is optional and
223
+ Wire it alongside the existing Auth facade. `jwtBearer` is optional and
224
224
  enables JWT bearer verification for manifest REST routes:
225
225
 
226
226
  ```ts
227
227
  import {
228
- AssetsAssetServer,
229
- createCmsRef,
230
- createMcpApiHandler,
231
- createOAuthProvider,
232
- D1DatabaseDriver,
233
- mountServerEndpoints,
228
+ createMantleWorker,
234
229
  } from "@aotter/mantle/cloudflare";
235
230
 
236
- const runtimeRef = createCmsRef({
237
- manifests,
231
+ export default createMantleWorker({
232
+ plan,
238
233
  handlers,
239
- bindings: {
240
- db: new D1DatabaseDriver(env.DB),
241
- assets: env.ASSETS
242
- ? new AssetsAssetServer(env.ASSETS)
243
- : { fetch: async () => null },
244
- },
245
- auth,
246
- credentialResolver: siteCredentialResolver(env.DB),
247
- oauthBearer: {
248
- audience: "https://api.example.com",
249
- // Optional server-wide floor. Manifest scopes still run per target.
250
- scopes: ["api"],
251
- },
252
- });
253
-
254
- mountServerEndpoints(app, runtimeRef);
255
-
256
- const oauthProvider = createOAuthProvider({
257
- defaultHandler: {
258
- fetch: (request, workerEnv, ctx) => app.fetch(request, workerEnv, ctx),
259
- },
260
- apiHandlers: {
261
- "/mcp/staff": createMcpApiHandler({ ref: runtimeRef, surface: "staff" }),
262
- "/mcp": createMcpApiHandler({ ref: runtimeRef, surface: "public" }),
263
- },
264
- scopesSupported: ["mcp"],
234
+ extend: ({ env }) => ({
235
+ credentialResolver: siteCredentialResolver(env.DB),
236
+ jwtBearer: {
237
+ audience: "https://api.example.com",
238
+ // Optional server-wide floor. Manifest scopes still run per target.
239
+ scopes: ["api"],
240
+ },
241
+ }),
265
242
  });
266
243
  ```
267
244
 
268
- Export or delegate to `oauthProvider` as the Worker's top-level handler so the
269
- OAuth provider can verify MCP bearers before dispatching to either MCP surface.
245
+ The facade mounts both MCP surfaces behind the same Better Auth 1.7 resource.
246
+ Low-level composition must pass that canonical resource to each
247
+ `createMcpApiHandler` explicitly.
270
248
 
271
249
  Resolution precedence is site resolver, configured OAuth bearer, then cookie
272
250
  session. A recognized invalid credential never falls back to a valid cookie.
@@ -618,7 +596,7 @@ const providerAuth = createAuth({
618
596
  loginPage: "/sign-in",
619
597
  consentPage: "/consent",
620
598
  scopes: ["openid", "offline_access", "accounts:read"],
621
- validAudiences: ["https://api.example.com"],
599
+ resources: ["https://api.example.com"],
622
600
  },
623
601
  });
624
602
 
@@ -629,9 +607,10 @@ const verification = await providerAuth.verifyOAuthAccessToken(request, {
629
607
  ```
630
608
 
631
609
  The verifier accepts JWT access tokens only and checks the configured issuer,
632
- JWKS/signature, audience, time claims, and required scopes. It returns only
633
- `userId`, `clientId`, `credentialId`, and scopes. Opaque tokens are rejected;
634
- there is no introspection fallback.
610
+ JWKS/signature, audience, time claims, required scopes, and—when passed the
611
+ request—DPoP proof binding with database-backed replay protection. It returns
612
+ only `userId`, `clientId`, `credentialId`, and scopes. Opaque tokens are
613
+ rejected; there is no introspection fallback.
635
614
 
636
615
  ## OpenAPI reflection
637
616
 
Binary file
@@ -94,7 +94,7 @@ customer-owned domain, hosted auth must use OAuth/OIDC:
94
94
 
95
95
  ```text
96
96
  customer.com/login
97
- -> platform.mantle.tools/oauth/authorize
97
+ -> platform.mantle.tools/api/auth/oauth2/authorize
98
98
  -> user signs in with Platform-supported methods
99
99
  -> customer.com/api/auth/callback/mantle
100
100
  -> customer.com verifies the authorization response
@@ -158,10 +158,10 @@ The cross-site API use case additionally justifies these curated fields and
158
158
  facades:
159
159
 
160
160
  - generic OAuth method `resource`
161
- - OAuth provider `validAudiences`
161
+ - OAuth provider `resources` and the curated `mcpResource`
162
162
  - `Auth.getProviderAccessToken(request, providerId)`
163
163
  - `Auth.verifyOAuthAccessToken(tokenOrRequest, { audience, scopes })`
164
164
 
165
- All are additive. Existing generated sites that do not pass them keep their
166
- previous cookie, session, and REST behavior. These are not a raw Better Auth
167
- options passthrough.
165
+ Generic OAuth providers use Better Auth 1.7's standard social sign-in and
166
+ `/api/auth/callback/:id` path. These are not a raw Better Auth options
167
+ passthrough.
@@ -8,18 +8,20 @@ while adding one application-owned post-response Queue audit across every route.
8
8
  ```ts
9
9
  import { Hono } from "hono";
10
10
  import {
11
- createCmsRef,
11
+ applyCachePolicy,
12
+ conventionalMcpResource,
13
+ createMantleRuntimeRef,
12
14
  createConventionalAuth,
13
15
  createConventionalBindings,
14
16
  createMcpApiHandler,
15
- createOAuthProvider,
16
17
  mountAuthorize,
17
- mountServerEndpoints,
18
+ mountAdmin,
19
+ mountRuntimeEndpoints,
18
20
  runMantleWorkerRequest,
19
21
  setupIncompleteAuthResponse,
20
22
  type MantleCloudflareEnv,
21
23
  } from "@aotter/mantle/cloudflare";
22
- import { manifest } from "../.mantle/generated/site.js";
24
+ import { plan } from "../.mantle/generated/mantle.js";
23
25
 
24
26
  interface Env extends MantleCloudflareEnv {
25
27
  readonly AUDIT_QUEUE: Queue<{
@@ -50,30 +52,40 @@ export default {
50
52
  function assemble(env: Env) {
51
53
  const bindings = createConventionalBindings(env);
52
54
  const auth = createConventionalAuth(env);
53
- const ref = createCmsRef({ manifests: manifest, bindings, auth });
55
+ const ref = createMantleRuntimeRef({ plan, bindings, auth });
54
56
  const app = new Hono<{ Bindings: Env }>();
55
57
 
56
- mountServerEndpoints(app, ref);
57
- mountAuthorize(app, { auth, loginPath: "/admin/sign-in" });
58
+ mountRuntimeEndpoints(app, ref);
59
+ if (bindings.adminAssets) mountAdmin(app, ref, bindings.adminAssets);
60
+ mountAuthorize(app, { auth });
58
61
  app.get("/cache-probe", () => new Response("public", {
59
62
  headers: { "cache-control": "public, s-maxage=60" },
60
63
  }));
61
64
 
62
- const provider = createOAuthProvider<Env>({
63
- defaultHandler: {
64
- fetch: (request, workerEnv, ctx) => app.fetch(request, workerEnv, ctx),
65
+ const resource = conventionalMcpResource(env);
66
+ const mcp = new Map([
67
+ ["/mcp/staff", createMcpApiHandler<Env>({ ref, surface: "staff", resource })],
68
+ ["/mcp", createMcpApiHandler<Env>({ ref, surface: "public", resource })],
69
+ ]);
70
+ return {
71
+ auth,
72
+ async fetch(request: Request, workerEnv: Env, ctx: ExecutionContext) {
73
+ // Low-level owners must prepare the canonical D1 schema before Better
74
+ // Auth handles a token, client, consent, or CIMD request.
75
+ await ref.get();
76
+ const handler = mcp.get(new URL(request.url).pathname);
77
+ const response = handler?.fetch
78
+ ? await handler.fetch(request, workerEnv, ctx)
79
+ : await app.fetch(request, workerEnv, ctx);
80
+ return applyCachePolicy(request, response);
65
81
  },
66
- apiHandlers: {
67
- "/mcp/staff": createMcpApiHandler<Env>({ ref, surface: "staff" }),
68
- "/mcp": createMcpApiHandler<Env>({ ref, surface: "public" }),
69
- },
70
- });
71
- return { auth, fetch: provider.fetch.bind(provider) };
82
+ };
72
83
  }
73
84
  ```
74
85
 
75
- Keep the conventional `DB` and `OAUTH_KV` bindings and
76
- `nodejs_compat`; add the Queue producer in `wrangler.jsonc`:
86
+ Keep the conventional `DB` binding and `nodejs_compat`. CIMD metadata fetches
87
+ also require `global_fetch_strictly_public`; add the Queue producer in
88
+ `wrangler.jsonc`:
77
89
 
78
90
  ```jsonc
79
91
  {
@@ -94,65 +94,47 @@ retry_delay = 60
94
94
  dead_letter_queue = "mantle-internal-dlq"
95
95
  ```
96
96
 
97
- Wire the producer into `CmsConfig.bindings` and export the consumer alongside
97
+ Wire the producer into `MantleCloudflareConfig.bindings` and export the consumer alongside
98
98
  the existing HTTP/OAuth handler. The same Worker may be both producer and
99
99
  consumer:
100
100
 
101
101
  ```ts
102
102
  import type { DeferredHookEnvelope } from "@aotter/mantle/runtime";
103
103
  import {
104
- AssetsAssetServer,
105
- D1DatabaseDriver,
106
104
  WorkersQueueHookDispatcher,
107
- createCmsRef,
105
+ createMantleWorker,
108
106
  createQueueHandler,
109
- createOAuthProvider,
107
+ type MantleCloudflareEnv,
110
108
  } from "@aotter/mantle/cloudflare";
111
109
 
112
- interface Env {
110
+ interface Env extends MantleCloudflareEnv {
113
111
  DB: D1Database;
114
- ASSETS: Fetcher;
115
112
  MANTLE_INTERNAL_QUEUE: Queue<DeferredHookEnvelope>;
116
113
  }
117
114
 
118
- function buildWorker(env: Env) {
119
- const cms = createCmsRef({
120
- manifests,
121
- handlers,
122
- auth: createSiteAuth(env),
123
- bindings: {
124
- db: new D1DatabaseDriver(env.DB),
125
- assets: new AssetsAssetServer(env.ASSETS),
126
- deferredHookDispatcher: new WorkersQueueHookDispatcher(
127
- env.MANTLE_INTERNAL_QUEUE,
128
- ),
129
- },
130
- });
131
-
132
- const http = createOAuthProvider<Env>({
133
- defaultHandler: createSiteHttpHandler(cms),
134
- apiHandlers: createSiteMcpHandlers(cms),
135
- });
136
-
137
- return { http, consumeMantle: createQueueHandler<Env>(cms) };
138
- }
139
-
140
- let built: ReturnType<typeof buildWorker> | undefined;
141
- const worker = (env: Env) => built ??= buildWorker(env);
115
+ const worker = createMantleWorker<Env>({
116
+ plan,
117
+ handlers,
118
+ bindings: (env, conventional) => ({
119
+ ...conventional,
120
+ deferredHookDispatcher: new WorkersQueueHookDispatcher(
121
+ env.MANTLE_INTERNAL_QUEUE,
122
+ ),
123
+ }),
124
+ });
142
125
 
143
126
  export default {
144
- fetch(request, env, ctx) {
145
- return worker(env).http.fetch(request, env, ctx);
146
- },
127
+ fetch: worker.fetch,
147
128
  queue(batch, env) {
148
- return worker(env).consumeMantle(batch, env);
129
+ return createQueueHandler<Env>({
130
+ get: () => worker.getRuntime(env),
131
+ })(batch, env);
149
132
  },
150
133
  } satisfies ExportedHandler<Env>;
151
134
  ```
152
135
 
153
- `createSiteAuth`, `createSiteHttpHandler`, and `createSiteMcpHandlers` above
154
- stand for the site's existing adapter assembly; Queue opt-in adds only the
155
- dispatcher binding and `queue` export.
136
+ Queue opt-in adds only the dispatcher binding and `queue` export; the facade
137
+ keeps Auth, MCP, cache, and runtime assembly on the standard path.
156
138
 
157
139
  ## Idempotent handlers
158
140