@aotter/mantle 0.1.0-alpha.1 → 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 +105 -26
- 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 +94 -47
- 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 +29 -21
- package/docs/adr/0012-views-as-public-rest.md +5 -9
- 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 +28 -42
- 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 -38
- package/docs/design-atoms.md +155 -401
- 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 +7 -6
- package/docs/release-process.md +86 -25
- package/docs/schema-indexes.md +1 -1
- package/docs/sealed-pipeline-ownership.md +100 -0
- package/package.json +84 -14
- package/skills/README.md +39 -7
- package/skills/develop/SKILL.md +31 -17
- 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.
|
|
@@ -128,9 +128,16 @@ Mantle Platform user id, Hosted Auth upstream subject, email, or provider id.
|
|
|
128
128
|
Hosted Auth may establish the site session, but Platform is not part of the
|
|
129
129
|
View query path.
|
|
130
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
|
+
|
|
131
138
|
## Cloudflare consumer wiring
|
|
132
139
|
|
|
133
|
-
Pass one site-owned resolver to `
|
|
140
|
+
Pass one site-owned resolver to `createMantleRuntimeRef`. Return `not-handled` when the
|
|
134
141
|
request is not one of the site's credential formats, `invalid` when it is a
|
|
135
142
|
recognized but bad/revoked credential, and `verified` only after checking the
|
|
136
143
|
authoritative site record.
|
|
@@ -213,53 +220,31 @@ function parseScopes(json: string): string[] | null {
|
|
|
213
220
|
}
|
|
214
221
|
```
|
|
215
222
|
|
|
216
|
-
Wire it alongside the existing Auth facade. `
|
|
223
|
+
Wire it alongside the existing Auth facade. `jwtBearer` is optional and
|
|
217
224
|
enables JWT bearer verification for manifest REST routes:
|
|
218
225
|
|
|
219
226
|
```ts
|
|
220
227
|
import {
|
|
221
|
-
|
|
222
|
-
createCmsRef,
|
|
223
|
-
createMcpApiHandler,
|
|
224
|
-
createOAuthProvider,
|
|
225
|
-
D1DatabaseDriver,
|
|
226
|
-
mountServerEndpoints,
|
|
228
|
+
createMantleWorker,
|
|
227
229
|
} from "@aotter/mantle/cloudflare";
|
|
228
230
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
+
export default createMantleWorker({
|
|
232
|
+
plan,
|
|
231
233
|
handlers,
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
oauthBearer: {
|
|
241
|
-
audience: "https://api.example.com",
|
|
242
|
-
// Optional server-wide floor. Manifest scopes still run per target.
|
|
243
|
-
scopes: ["api"],
|
|
244
|
-
},
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
mountServerEndpoints(app, runtimeRef);
|
|
248
|
-
|
|
249
|
-
const oauthProvider = createOAuthProvider({
|
|
250
|
-
defaultHandler: {
|
|
251
|
-
fetch: (request, workerEnv, ctx) => app.fetch(request, workerEnv, ctx),
|
|
252
|
-
},
|
|
253
|
-
apiHandlers: {
|
|
254
|
-
"/mcp/staff": createMcpApiHandler({ ref: runtimeRef, surface: "staff" }),
|
|
255
|
-
"/mcp": createMcpApiHandler({ ref: runtimeRef, surface: "public" }),
|
|
256
|
-
},
|
|
257
|
-
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
|
+
}),
|
|
258
242
|
});
|
|
259
243
|
```
|
|
260
244
|
|
|
261
|
-
|
|
262
|
-
|
|
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.
|
|
263
248
|
|
|
264
249
|
Resolution precedence is site resolver, configured OAuth bearer, then cookie
|
|
265
250
|
session. A recognized invalid credential never falls back to a valid cookie.
|
|
@@ -611,7 +596,7 @@ const providerAuth = createAuth({
|
|
|
611
596
|
loginPage: "/sign-in",
|
|
612
597
|
consentPage: "/consent",
|
|
613
598
|
scopes: ["openid", "offline_access", "accounts:read"],
|
|
614
|
-
|
|
599
|
+
resources: ["https://api.example.com"],
|
|
615
600
|
},
|
|
616
601
|
});
|
|
617
602
|
|
|
@@ -622,9 +607,10 @@ const verification = await providerAuth.verifyOAuthAccessToken(request, {
|
|
|
622
607
|
```
|
|
623
608
|
|
|
624
609
|
The verifier accepts JWT access tokens only and checks the configured issuer,
|
|
625
|
-
JWKS/signature, audience, time claims,
|
|
626
|
-
|
|
627
|
-
|
|
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.
|
|
628
614
|
|
|
629
615
|
## OpenAPI reflection
|
|
630
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` 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
|
|