@aotter/mantle 0.0.11-alpha.73 → 0.1.0-alpha.10
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 +107 -28
- 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 +95 -51
- package/docs/adr/0001-four-atom-manifest-model.md +48 -307
- package/docs/adr/0002-closed-enums-for-bindings.md +6 -15
- package/docs/adr/0007-ai-as-primary-author.md +6 -4
- package/docs/adr/0008-structured-diagnostic-shape.md +2 -2
- package/docs/adr/0009-consumer-supplied-manifests.md +15 -9
- package/docs/adr/0010-locale-and-translates.md +13 -8
- package/docs/adr/0011-adapter-port-spec.md +39 -43
- package/docs/adr/0012-views-as-public-rest.md +6 -10
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +124 -43
- 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 +16 -14
- package/docs/api-mcp-authorization.md +78 -44
- package/docs/assets/mantle-admin-operations.png +0 -0
- package/docs/assets/mantle-hero.jpg +0 -0
- package/docs/auth-hosting-model.md +15 -11
- package/docs/cloudflare-low-level-composition.md +30 -18
- package/docs/deferred-lifecycle-queues.md +20 -41
- package/docs/design-atoms.md +159 -400
- package/docs/labels.md +5 -3
- package/docs/media-uploads.md +9 -2
- package/docs/migration-0.1.2.md +45 -0
- package/docs/performance-harness.md +23 -23
- package/docs/release-process.md +91 -28
- package/docs/schema-indexes.md +1 -1
- package/docs/sealed-pipeline-ownership.md +100 -0
- package/package.json +85 -15
- package/skills/README.md +39 -7
- package/skills/develop/SKILL.md +33 -19
- package/skills/install/SKILL.md +26 -25
- package/skills/media-gc/SKILL.md +85 -0
- package/skills/plugin/SKILL.md +2 -1
- package/skills/provision/SKILL.md +24 -2
- package/skills/theme/SKILL.md +11 -1
- 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 -181
- 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
|
-
| [0011](0011-adapter-port-spec.md) | Adapter port spec. Required runtime ports plus optional feature ports.
|
|
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
|
|
|
@@ -39,12 +41,12 @@ for new v0.1.0 boundaries, and folds / drops the rest:
|
|
|
39
41
|
|
|
40
42
|
- **POC ADR-0003** OpenAPI emission → folded into `mantle-spec` README (the *what* is implementation; the *why* was already captured by ADR-0001's grammar lock).
|
|
41
43
|
- **POC ADR-0004** D1 today, Hyperdrive PG tomorrow → folded into `mantle-cloudflare` README (now a v0.2 roadmap item, not an architectural decision).
|
|
42
|
-
- **POC ADR-0005** v0.1 minimum
|
|
44
|
+
- **POC ADR-0005** v0.1 minimum grammar → folded into ADR-0001's fail-closed grammar policy.
|
|
43
45
|
- **POC ADR-0006** multi-doc YAML → folded into ADR-0001 §"Authoring shape: multi-doc YAML."
|
|
44
|
-
- **POC ADR-0011** lifecycle binary opt-in → distilled to a §"Lifecycle" subsection in `docs/design-atoms.md`. v0.1.0 ships `
|
|
46
|
+
- **POC ADR-0011** lifecycle binary opt-in → distilled to a §"Lifecycle" subsection in `docs/design-atoms.md`. v0.1.0 ships `publishing` and `operational`.
|
|
45
47
|
- **POC ADR-0012** strategic posture vs adjacent CMS designs → strategic / marketing material, lives in `README.md` if anywhere.
|
|
46
48
|
- **POC ADR-0013** role-split surfaces (coder agent vs operator agent) → folded into ADR-0007 (Part B).
|
|
47
|
-
- **POC ADR-0014** builtin handlers and lifecycle Triggers → promoted to v0.1.0 and implemented in the rebuild via `LifecycleHookingEntryRepository` and `InvokeBuiltinUseCase`.
|
|
49
|
+
- **POC ADR-0014** builtin handlers and lifecycle Triggers → promoted to v0.1.0 and implemented in the rebuild via `LifecycleHookingEntryRepository` and `InvokeBuiltinUseCase`. Full shape spec lives in `docs/design-atoms.md`.
|
|
48
50
|
- **POC ADR-0015** cms-astro internal seam discipline → POC-specific to a package that no longer exists; replaced by ADR-0011 (adapter port spec).
|
|
49
51
|
- **POC ADR-0029** drop Astro from cms-cloudflare → POC-specific historical record; the rebuild starts post-Astro.
|
|
50
52
|
|
|
@@ -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.
|
|
@@ -78,9 +78,66 @@ Missing/invalid credentials return `401`; a verified caller missing a required
|
|
|
78
78
|
role or scope returns `403`; a site guard may return
|
|
79
79
|
`ENTITLEMENT_REQUIRED`/`402`. Guards run on every call and are not cached.
|
|
80
80
|
|
|
81
|
+
### Identity-bound Views
|
|
82
|
+
|
|
83
|
+
Use the closed `{ "$ctx.user": "id" }` filter sentinel for rows owned by the
|
|
84
|
+
current site-local Better Auth user. The caller never supplies this value, so
|
|
85
|
+
the same View is safe on both REST and public MCP:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
apiVersion: cms.mantle.aotter.net/v1
|
|
89
|
+
kind: Schema
|
|
90
|
+
metadata: { name: orders }
|
|
91
|
+
spec:
|
|
92
|
+
schema:
|
|
93
|
+
type: object
|
|
94
|
+
properties:
|
|
95
|
+
userId: { type: string, x-mantle-bind: ctx.user }
|
|
96
|
+
orderNumber: { type: string }
|
|
97
|
+
orderStatus: { type: string }
|
|
98
|
+
totalMinor: { type: integer }
|
|
99
|
+
placedAt: { type: integer }
|
|
100
|
+
indexes: [[userId, placedAt]]
|
|
101
|
+
---
|
|
102
|
+
apiVersion: cms.mantle.aotter.net/v1
|
|
103
|
+
kind: View
|
|
104
|
+
metadata: { name: my-orders }
|
|
105
|
+
spec:
|
|
106
|
+
surface: public
|
|
107
|
+
from: orders
|
|
108
|
+
requires:
|
|
109
|
+
auth:
|
|
110
|
+
all: [ctx.user]
|
|
111
|
+
filter:
|
|
112
|
+
and:
|
|
113
|
+
- { eq: { field: status, value: published } }
|
|
114
|
+
- { eq: { field: userId, value: { "$ctx.user": id } } }
|
|
115
|
+
fields: [orderNumber, orderStatus, totalMinor, placedAt]
|
|
116
|
+
orderBy: [{ field: placedAt, direction: desc }]
|
|
117
|
+
limit: 50
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Core rejects this sentinel unless the View requires `ctx.user` and the bound
|
|
121
|
+
field is the leftmost field of a declared Schema index. Missing identity fails
|
|
122
|
+
with `401`; it never drops the filter or falls back to all rows. REST exposes
|
|
123
|
+
`GET /api/views/my-orders`; public MCP exposes `query_view_my_orders`. Both
|
|
124
|
+
call `ExecuteViewUseCase` and bind the same `ctx.user.id`.
|
|
125
|
+
|
|
126
|
+
The id belongs to the customer site's Better Auth user row. It is not a
|
|
127
|
+
Mantle Platform user id, Hosted Auth upstream subject, email, or provider id.
|
|
128
|
+
Hosted Auth may establish the site session, but Platform is not part of the
|
|
129
|
+
View query path.
|
|
130
|
+
|
|
131
|
+
## Site OAuth symmetry
|
|
132
|
+
|
|
133
|
+
A site-issued OAuth access token represents the same caller on public MCP and
|
|
134
|
+
manifest HTTP routes. Both surfaces populate `ctx.user` and `ctx.auth` from the
|
|
135
|
+
same token grant; expiry, revocation, scope, client, and resource audience are
|
|
136
|
+
enforced before the Procedure or View runs.
|
|
137
|
+
|
|
81
138
|
## Cloudflare consumer wiring
|
|
82
139
|
|
|
83
|
-
Pass one site-owned resolver to `
|
|
140
|
+
Pass one site-owned resolver to `createMantleRuntimeRef`. Return `not-handled` when the
|
|
84
141
|
request is not one of the site's credential formats, `invalid` when it is a
|
|
85
142
|
recognized but bad/revoked credential, and `verified` only after checking the
|
|
86
143
|
authoritative site record.
|
|
@@ -163,55 +220,31 @@ function parseScopes(json: string): string[] | null {
|
|
|
163
220
|
}
|
|
164
221
|
```
|
|
165
222
|
|
|
166
|
-
Wire it alongside the existing Auth facade. `
|
|
223
|
+
Wire it alongside the existing Auth facade. `jwtBearer` is optional and
|
|
167
224
|
enables JWT bearer verification for manifest REST routes:
|
|
168
225
|
|
|
169
226
|
```ts
|
|
170
227
|
import {
|
|
171
|
-
|
|
172
|
-
createCmsRef,
|
|
173
|
-
createMcpApiHandler,
|
|
174
|
-
createOAuthProvider,
|
|
175
|
-
D1DatabaseDriver,
|
|
176
|
-
KvCacheBinding,
|
|
177
|
-
mountServerEndpoints,
|
|
228
|
+
createMantleWorker,
|
|
178
229
|
} from "@aotter/mantle/cloudflare";
|
|
179
230
|
|
|
180
|
-
|
|
181
|
-
|
|
231
|
+
export default createMantleWorker({
|
|
232
|
+
plan,
|
|
182
233
|
handlers,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
credentialResolver: siteCredentialResolver(env.DB),
|
|
192
|
-
oauthBearer: {
|
|
193
|
-
audience: "https://api.example.com",
|
|
194
|
-
// Optional server-wide floor. Manifest scopes still run per target.
|
|
195
|
-
scopes: ["api"],
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
mountServerEndpoints(app, runtimeRef);
|
|
200
|
-
|
|
201
|
-
const oauthProvider = createOAuthProvider({
|
|
202
|
-
defaultHandler: {
|
|
203
|
-
fetch: (request, workerEnv, ctx) => app.fetch(request, workerEnv, ctx),
|
|
204
|
-
},
|
|
205
|
-
apiHandlers: {
|
|
206
|
-
"/mcp/staff": createMcpApiHandler({ ref: runtimeRef, surface: "staff" }),
|
|
207
|
-
"/mcp": createMcpApiHandler({ ref: runtimeRef, surface: "public" }),
|
|
208
|
-
},
|
|
209
|
-
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
|
+
}),
|
|
210
242
|
});
|
|
211
243
|
```
|
|
212
244
|
|
|
213
|
-
|
|
214
|
-
|
|
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.
|
|
215
248
|
|
|
216
249
|
Resolution precedence is site resolver, configured OAuth bearer, then cookie
|
|
217
250
|
session. A recognized invalid credential never falls back to a valid cookie.
|
|
@@ -563,7 +596,7 @@ const providerAuth = createAuth({
|
|
|
563
596
|
loginPage: "/sign-in",
|
|
564
597
|
consentPage: "/consent",
|
|
565
598
|
scopes: ["openid", "offline_access", "accounts:read"],
|
|
566
|
-
|
|
599
|
+
resources: ["https://api.example.com"],
|
|
567
600
|
},
|
|
568
601
|
});
|
|
569
602
|
|
|
@@ -574,9 +607,10 @@ const verification = await providerAuth.verifyOAuthAccessToken(request, {
|
|
|
574
607
|
```
|
|
575
608
|
|
|
576
609
|
The verifier accepts JWT access tokens only and checks the configured issuer,
|
|
577
|
-
JWKS/signature, audience, time claims,
|
|
578
|
-
|
|
579
|
-
|
|
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.
|
|
580
614
|
|
|
581
615
|
## OpenAPI reflection
|
|
582
616
|
|
|
Binary file
|
|
Binary file
|
|
@@ -5,8 +5,10 @@ basic login. The split is:
|
|
|
5
5
|
|
|
6
6
|
- **Mantle SDK** gives every generated site the primitives needed to
|
|
7
7
|
run its own auth.
|
|
8
|
-
- **Mantle
|
|
9
|
-
self-hosted
|
|
8
|
+
- **Mantle's conventional Cloudflare adapter** runs the generated site's
|
|
9
|
+
selected self-hosted or Mantle Platform hosted client configuration.
|
|
10
|
+
- **Mantle starters** declare the mode and provider placeholders that landing
|
|
11
|
+
or the site owner completes.
|
|
10
12
|
- **Mantle Platform** can sell hosted identity, provider setup, email,
|
|
11
13
|
and billing convenience for site owners who do not want to operate
|
|
12
14
|
those pieces.
|
|
@@ -92,7 +94,7 @@ customer-owned domain, hosted auth must use OAuth/OIDC:
|
|
|
92
94
|
|
|
93
95
|
```text
|
|
94
96
|
customer.com/login
|
|
95
|
-
-> platform.mantle.tools/
|
|
97
|
+
-> platform.mantle.tools/api/auth/oauth2/authorize
|
|
96
98
|
-> user signs in with Platform-supported methods
|
|
97
99
|
-> customer.com/api/auth/callback/mantle
|
|
98
100
|
-> customer.com verifies the authorization response
|
|
@@ -117,10 +119,12 @@ Landing can probe Platform staff/session state, but provisioning's
|
|
|
117
119
|
GitHub OAuth token is still Landing-owned unless a separate token
|
|
118
120
|
handoff design is introduced.
|
|
119
121
|
|
|
120
|
-
The
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
The conventional hosted-auth client wiring belongs in Core's Cloudflare
|
|
123
|
+
adapter. Starters declare its environment bindings; landing supplies an
|
|
124
|
+
allocated client. A site can still replace Auth construction through
|
|
125
|
+
`createMantleWorker({ auth })` when it needs a different curated identity
|
|
126
|
+
design. Core continues to own the normalized manifest/runtime credential
|
|
127
|
+
vocabulary (`ctx.user`, `ctx.staff`, `ctx.auth`) and guard orchestration.
|
|
124
128
|
|
|
125
129
|
## API and MCP Authorization
|
|
126
130
|
|
|
@@ -154,10 +158,10 @@ The cross-site API use case additionally justifies these curated fields and
|
|
|
154
158
|
facades:
|
|
155
159
|
|
|
156
160
|
- generic OAuth method `resource`
|
|
157
|
-
- OAuth provider `
|
|
161
|
+
- OAuth provider `resources` and the curated `mcpResource`
|
|
158
162
|
- `Auth.getProviderAccessToken(request, providerId)`
|
|
159
163
|
- `Auth.verifyOAuthAccessToken(tokenOrRequest, { audience, scopes })`
|
|
160
164
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
|
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
|
{
|