@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.
- 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 +2 -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 +38 -7
- package/skills/develop/SKILL.md +7 -5
- package/skills/install/SKILL.md +26 -25
- package/skills/media-gc/SKILL.md +6 -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
|
@@ -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.
|
package/docs/adr/README.md
CHANGED
|
@@ -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
|
|
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) |
|
|
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
|
|
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. **
|
|
29
|
-
3. **
|
|
30
|
-
4. **
|
|
31
|
-
5. **
|
|
32
|
-
6. **
|
|
33
|
-
7. **
|
|
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
|
|
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 `
|
|
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. `
|
|
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
|
-
|
|
229
|
-
createCmsRef,
|
|
230
|
-
createMcpApiHandler,
|
|
231
|
-
createOAuthProvider,
|
|
232
|
-
D1DatabaseDriver,
|
|
233
|
-
mountServerEndpoints,
|
|
228
|
+
createMantleWorker,
|
|
234
229
|
} from "@aotter/mantle/cloudflare";
|
|
235
230
|
|
|
236
|
-
|
|
237
|
-
|
|
231
|
+
export default createMantleWorker({
|
|
232
|
+
plan,
|
|
238
233
|
handlers,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
269
|
-
|
|
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
|
-
|
|
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,
|
|
633
|
-
|
|
634
|
-
|
|
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
|
|
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/
|
|
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 `
|
|
161
|
+
- OAuth provider `resources` and the curated `mcpResource`
|
|
162
162
|
- `Auth.getProviderAccessToken(request, providerId)`
|
|
163
163
|
- `Auth.verifyOAuthAccessToken(tokenOrRequest, { audience, scopes })`
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
11
|
+
applyCachePolicy,
|
|
12
|
+
conventionalMcpResource,
|
|
13
|
+
createMantleRuntimeRef,
|
|
12
14
|
createConventionalAuth,
|
|
13
15
|
createConventionalBindings,
|
|
14
16
|
createMcpApiHandler,
|
|
15
|
-
createOAuthProvider,
|
|
16
17
|
mountAuthorize,
|
|
17
|
-
|
|
18
|
+
mountAdmin,
|
|
19
|
+
mountRuntimeEndpoints,
|
|
18
20
|
runMantleWorkerRequest,
|
|
19
21
|
setupIncompleteAuthResponse,
|
|
20
22
|
type MantleCloudflareEnv,
|
|
21
23
|
} from "@aotter/mantle/cloudflare";
|
|
22
|
-
import {
|
|
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 =
|
|
55
|
+
const ref = createMantleRuntimeRef({ plan, bindings, auth });
|
|
54
56
|
const app = new Hono<{ Bindings: Env }>();
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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 `
|
|
76
|
-
`
|
|
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 `
|
|
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
|
-
|
|
105
|
+
createMantleWorker,
|
|
108
106
|
createQueueHandler,
|
|
109
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
145
|
-
return worker(env).http.fetch(request, env, ctx);
|
|
146
|
-
},
|
|
127
|
+
fetch: worker.fetch,
|
|
147
128
|
queue(batch, env) {
|
|
148
|
-
return
|
|
129
|
+
return createQueueHandler<Env>({
|
|
130
|
+
get: () => worker.getRuntime(env),
|
|
131
|
+
})(batch, env);
|
|
149
132
|
},
|
|
150
133
|
} satisfies ExportedHandler<Env>;
|
|
151
134
|
```
|
|
152
135
|
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|