@aotter/mantle 0.1.5-alpha.1 → 0.2.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 +166 -298
- package/dist/auth/adapter.d.ts +32 -0
- package/dist/auth/adapter.js +288 -0
- package/dist/auth/admin.d.ts +3 -0
- package/dist/auth/admin.js +21 -0
- package/dist/auth/caller.d.ts +86 -0
- package/dist/auth/caller.js +155 -0
- package/dist/auth/hardening.d.ts +24 -0
- package/dist/auth/hardening.js +40 -0
- package/dist/auth/identity.d.ts +121 -0
- package/dist/auth/identity.js +203 -0
- package/dist/auth/oauth.d.ts +63 -0
- package/dist/auth/oauth.js +217 -0
- package/dist/auth/resource-seeds.js +10 -0
- package/dist/auth/routes.d.ts +32 -0
- package/dist/auth/routes.js +226 -0
- package/dist/build/compiler.d.ts +721 -0
- package/dist/build/compiler.js +7029 -0
- package/dist/build/spec/domain/model/ManifestGrammar.d.ts +439 -0
- package/dist/build/spec/domain/model/RuntimePlan.d.ts +168 -0
- package/dist/build/spec/domain/model/SqlIr.d.ts +129 -0
- package/dist/build/spec/domain/model/UserFields.d.ts +21 -0
- package/dist/build/spec/domain/service/PlanFingerprint.d.ts +7 -0
- package/dist/build/spec/kernel/diagnostic.d.ts +108 -0
- package/dist/bun/assets.d.ts +2 -0
- package/dist/bun/assets.js +24 -0
- package/dist/domain/adminOrder.js +24 -0
- package/dist/domain/columns.js +57 -0
- package/dist/domain/describe/chunk-ZVXBGP7Y.js +424 -0
- package/dist/domain/describe/index.d.ts +58 -0
- package/dist/domain/describe/index.js +115 -0
- package/dist/domain/describe/target-hash.d.ts +8 -0
- package/dist/domain/describe/target-hash.js +12 -0
- package/dist/domain/describe/targets.d.ts +40 -0
- package/dist/domain/entry.js +33 -0
- package/dist/domain/generated-tools.js +19 -0
- package/dist/domain/generation.d.ts +16 -0
- package/dist/domain/generation.js +1 -0
- package/dist/domain/input.js +17 -0
- package/dist/domain/model.js +1 -0
- package/dist/domain/paging.js +230 -0
- package/dist/domain/recipes.js +24 -0
- package/dist/domain/userReferences.js +5 -0
- package/dist/domain/wireScalars.js +47 -0
- package/dist/infrastructure/adminEntries.js +153 -0
- package/dist/infrastructure/adminPaging.js +72 -0
- package/dist/infrastructure/artifacts.js +71 -0
- package/dist/infrastructure/authSchema.js +83 -0
- package/dist/infrastructure/bundle.js +188 -0
- package/dist/infrastructure/cli.js +118 -0
- package/dist/infrastructure/generate.d.ts +12 -0
- package/dist/infrastructure/generate.js +189 -0
- package/dist/infrastructure/generateAuth.d.ts +8 -0
- package/dist/infrastructure/generateAuth.js +74 -0
- package/dist/infrastructure/help.js +60 -0
- package/dist/infrastructure/init.js +54 -0
- package/dist/infrastructure/inputs.js +41 -0
- package/dist/infrastructure/mounts.js +25 -0
- package/dist/infrastructure/wrangler.js +109 -0
- package/dist/kernel/authorization.d.ts +4 -0
- package/dist/kernel/authorization.js +16 -0
- package/dist/kernel/binds.d.ts +60 -0
- package/dist/kernel/binds.js +49 -0
- package/dist/kernel/caller-identity.d.ts +24 -0
- package/dist/kernel/caller-identity.js +22 -0
- package/dist/kernel/contract-error.d.ts +12 -0
- package/dist/kernel/contract-error.js +14 -0
- package/dist/kernel/contracts.d.ts +143 -0
- package/dist/kernel/contracts.js +97 -0
- package/dist/kernel/cron.d.ts +1 -0
- package/dist/kernel/cron.js +58 -0
- package/dist/kernel/dialect/common.d.ts +38 -0
- package/dist/kernel/dialect/common.js +6 -0
- package/dist/kernel/dialect/postgres.d.ts +16 -0
- package/dist/kernel/dialect/postgres.js +18 -0
- package/dist/kernel/dialect/sqlite.d.ts +20 -0
- package/dist/kernel/dialect/sqlite.js +34 -0
- package/dist/kernel/drain.d.ts +5 -0
- package/dist/kernel/drain.js +35 -0
- package/dist/kernel/driver/bun-sqlite.d.ts +19 -0
- package/dist/kernel/driver/bun-sqlite.js +77 -0
- package/dist/kernel/driver/d1.d.ts +24 -0
- package/dist/kernel/driver/d1.js +22 -0
- package/dist/kernel/driver/postgres.d.ts +25 -0
- package/dist/kernel/driver/postgres.js +99 -0
- package/dist/kernel/driver/transaction-context.d.ts +10 -0
- package/dist/kernel/driver/transaction-context.js +29 -0
- package/dist/kernel/email.d.ts +28 -0
- package/dist/kernel/email.js +38 -0
- package/dist/kernel/host-access.d.ts +10 -0
- package/dist/kernel/host-access.js +65 -0
- package/dist/kernel/host-caller.d.ts +39 -0
- package/dist/kernel/host-caller.js +56 -0
- package/dist/kernel/json-body.d.ts +4 -0
- package/dist/kernel/json-body.js +44 -0
- package/dist/kernel/mcp-apps.d.ts +69 -0
- package/dist/kernel/mcp-apps.js +40 -0
- package/dist/kernel/mcp-call.d.ts +5 -0
- package/dist/kernel/mcp-call.js +25 -0
- package/dist/kernel/mcp-handler.js +371 -0
- package/dist/kernel/mcp.d.ts +110 -0
- package/dist/kernel/mcp.js +50 -0
- package/dist/kernel/media.d.ts +27 -0
- package/dist/kernel/media.js +216 -0
- package/dist/kernel/paging.d.ts +55 -0
- package/dist/kernel/paging.js +95 -0
- package/dist/kernel/publishing.d.ts +4 -0
- package/dist/kernel/publishing.js +16 -0
- package/dist/kernel/r2-media.d.ts +42 -0
- package/dist/kernel/r2-media.js +78 -0
- package/dist/kernel/rest-dispatch.js +38 -0
- package/dist/kernel/rest-router.js +64 -0
- package/dist/kernel/rest.d.ts +58 -0
- package/dist/kernel/rest.js +41 -0
- package/dist/kernel/scalars.d.ts +6 -0
- package/dist/kernel/scalars.js +44 -0
- package/dist/kernel/search.js +5 -0
- package/dist/kernel/site-types.d.ts +146 -0
- package/dist/kernel/site-types.js +1 -0
- package/dist/kernel/site.d.ts +39 -0
- package/dist/kernel/site.js +32 -0
- package/dist/kernel/store.d.ts +175 -0
- package/dist/kernel/store.js +269 -0
- package/dist/kernel/surface.d.ts +10 -0
- package/dist/kernel/surface.js +19 -0
- package/dist/kernel/sweep.d.ts +17 -0
- package/dist/kernel/sweep.js +27 -0
- package/dist/kernel/translations.d.ts +1 -0
- package/dist/kernel/translations.js +13 -0
- package/dist/kernel/user-caller.d.ts +31 -0
- package/dist/kernel/user-caller.js +67 -0
- package/dist/kernel/user-field-name.js +3 -0
- package/dist/kernel/user-fields.d.ts +35 -0
- package/dist/kernel/user-fields.js +81 -0
- package/dist/kernel/validation.d.ts +15 -0
- package/dist/kernel/validation.js +54 -0
- package/dist/kernel/web-gateway.js +68 -0
- package/dist/kernel/web-head.js +15 -0
- package/dist/kernel/web-index.js +40 -0
- package/dist/kernel/web-response.js +8 -0
- package/dist/kernel/web.d.ts +77 -0
- package/dist/kernel/web.js +124 -0
- package/dist/kernel/where.js +27 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/usecase/adminBindings.js +143 -0
- package/dist/usecase/adminClient.js +87 -0
- package/dist/usecase/adminDescriptors.js +107 -0
- package/dist/usecase/generate.js +166 -0
- package/dist/usecase/handlers.js +21 -0
- package/dist/usecase/mcp-media.js +17 -0
- package/dist/usecase/mcp.js +100 -0
- package/dist/usecase/paging.js +71 -0
- package/dist/usecase/publishing.js +63 -0
- package/dist/usecase/refusals.js +27 -0
- package/dist/usecase/rest.js +54 -0
- package/dist/usecase/search.js +7 -0
- package/dist/usecase/site.js +39 -0
- package/dist/usecase/stores.js +64 -0
- package/dist/usecase/tool-version.js +9 -0
- package/dist/usecase/translations.js +78 -0
- package/dist/usecase/ttl.js +20 -0
- package/dist/usecase/validation.js +241 -0
- package/dist/usecase/web.js +23 -0
- package/docs/examples/README.md +39 -18
- package/docs/examples/app-shell/.dev.vars.example +4 -0
- package/docs/examples/app-shell/README.md +121 -0
- package/docs/examples/app-shell/accept.mjs +91 -0
- package/docs/examples/app-shell/gitignore +7 -0
- package/docs/examples/app-shell/package.json +43 -0
- package/docs/examples/app-shell/pnpm-workspace.yaml +8 -0
- package/docs/examples/app-shell/smoke-kit.mjs +168 -0
- package/docs/examples/app-shell/src/auth.ts +58 -0
- package/docs/examples/app-shell/src/handlers.ts +5 -0
- package/docs/examples/app-shell/src/index.ts +4 -0
- package/docs/examples/app-shell/src/routes.ts +14 -0
- package/docs/examples/app-shell/src/schedule.ts +8 -0
- package/docs/examples/app-shell/src/service.ts +70 -0
- package/docs/examples/app-shell/src/sign-in.ts +41 -0
- package/docs/examples/app-shell/src/signup.ts +8 -0
- package/docs/examples/app-shell/tsconfig.json +26 -0
- package/docs/examples/app-shell/worker-configuration.d.ts +22 -0
- package/docs/examples/app-shell/wrangler.jsonc +62 -0
- package/docs/examples/club-events/README.md +65 -0
- package/docs/examples/club-events/manifests/club-events.yaml +247 -0
- package/docs/examples/club-events/smoke.mjs +127 -0
- package/docs/examples/club-events/src/handlers.ts +25 -0
- package/docs/examples/club-events/src/signup.ts +9 -0
- package/docs/examples/generated-web/README.md +78 -0
- package/docs/examples/generated-web/posts.yaml +38 -0
- package/docs/examples/generated-web/seed.sql +6 -0
- package/docs/examples/generated-web/src/worker.ts +43 -0
- package/docs/examples/generated-web/tsconfig.json +8 -0
- package/docs/examples/generated-web/wrangler.jsonc +8 -0
- package/docs/examples/purchasing/.dev.vars.example +6 -0
- package/docs/examples/purchasing/README.md +99 -0
- package/docs/examples/purchasing/manifests/purchasing.yaml +374 -0
- package/docs/examples/purchasing/seed.sql +4 -0
- package/docs/examples/purchasing/smoke.mjs +191 -0
- package/docs/examples/purchasing/src/admin.tsx +13 -0
- package/docs/examples/purchasing/src/handlers.ts +33 -0
- package/docs/examples/purchasing/src/notify.ts +29 -0
- package/docs/examples/purchasing/src/requests-page.ts +57 -0
- package/docs/examples/purchasing/src/routes.ts +53 -0
- package/docs/examples/purchasing/src/schedule.ts +31 -0
- package/docs/examples/purchasing/src/signup.ts +5 -0
- package/docs/examples/purchasing/src/supplier.ts +32 -0
- package/docs/examples/purchasing/wrangler.jsonc +69 -0
- package/docs/examples/recipes/README.md +60 -0
- package/docs/examples/recipes/manifests/recipes.yaml +75 -0
- package/docs/examples/recipes/smoke.mjs +102 -0
- package/docs/examples/recipes/src/media.ts +13 -0
- package/docs/examples/recipes/src/pages.ts +99 -0
- package/docs/examples/recipes/src/routes.ts +71 -0
- package/docs/examples/recipes/src/site-config.ts +12 -0
- package/docs/examples/recipes/wrangler.jsonc +74 -0
- package/docs/handbook/cloudflare/authentication.md +180 -322
- package/docs/handbook/cloudflare/chatgpt-sites.md +5 -23
- package/docs/handbook/cloudflare/deploy-and-operate.md +55 -77
- package/docs/handbook/cloudflare/media-r2.md +61 -205
- package/docs/handbook/cloudflare/service-entry.md +239 -0
- package/docs/handbook/concepts/authorization.md +10 -107
- package/docs/handbook/concepts/four-atoms.md +9 -168
- package/docs/handbook/concepts/lifecycle-and-locales.md +9 -132
- package/docs/handbook/concepts/mcp-and-agents.md +24 -121
- package/docs/handbook/concepts/procedures-and-triggers.md +5 -217
- package/docs/handbook/concepts/runtime-and-adapters.md +15 -169
- package/docs/handbook/concepts/views.md +10 -185
- package/docs/handbook/examples/hub.md +3 -8
- package/docs/handbook/guides/adapter-guide.md +11 -0
- package/docs/handbook/guides/admin-ui.md +115 -133
- package/docs/handbook/guides/agent-setup.md +64 -74
- package/docs/handbook/guides/agent-workflow-acceptance.md +232 -0
- package/docs/handbook/guides/email.md +86 -0
- package/docs/handbook/guides/local-testing.md +40 -0
- package/docs/handbook/guides/mcp-apps.md +560 -0
- package/docs/handbook/guides/native-admin.md +566 -0
- package/docs/handbook/guides/native-execution.md +133 -0
- package/docs/handbook/guides/oauth.md +219 -0
- package/docs/handbook/guides/postgres-adapters.md +56 -0
- package/docs/handbook/guides/public-pages.md +87 -0
- package/docs/handbook/guides/size-and-startup.md +9 -0
- package/docs/handbook/guides/sql-patterns.md +266 -0
- package/docs/handbook/guides/typed-queries.md +211 -107
- package/docs/handbook/navigation.json +176 -55
- package/docs/handbook/reference/admin-descriptors.md +53 -0
- package/docs/handbook/reference/authorization.md +39 -266
- package/docs/handbook/reference/describe.md +56 -0
- package/docs/handbook/reference/diagnostics.md +19 -186
- package/docs/handbook/reference/features.md +45 -55
- package/docs/handbook/reference/manifest.md +6 -129
- package/docs/handbook/reference/procedure.md +11 -303
- package/docs/handbook/reference/schema.md +64 -240
- package/docs/handbook/reference/site-config.md +5 -218
- package/docs/handbook/reference/sql.md +64 -0
- package/docs/handbook/reference/surface.md +392 -201
- package/docs/handbook/reference/trigger.md +56 -192
- package/docs/handbook/reference/view.md +24 -243
- package/docs/handbook/releases/index.md +244 -77
- package/docs/handbook/start/agents.md +45 -0
- package/docs/handbook/start/overview.md +9 -48
- package/docs/handbook/start/project-and-cli.md +51 -104
- package/docs/handbook/start/quickstart-worker.md +43 -178
- package/docs/skills/develop/SKILL.md +79 -249
- package/docs/skills/integrate/SKILL.md +39 -50
- package/docs/skills/media-gc/SKILL.md +34 -10
- package/docs/skills/provision/SKILL.md +36 -186
- package/docs/skills/update/SKILL.md +23 -38
- package/docs/source/scripts/postgres-adapter.ts +101 -0
- package/docs/upgrade-0.1-to-0.2.md +227 -0
- package/package.json +120 -131
- package/skills/README.md +48 -108
- package/skills/mantle/SKILL.md +76 -0
- package/dist/admin-ui.d.ts +0 -2
- package/dist/admin-ui.d.ts.map +0 -1
- package/dist/admin-ui.js +0 -2
- package/dist/admin-ui.js.map +0 -1
- package/dist/admin.d.ts +0 -2
- package/dist/admin.d.ts.map +0 -1
- package/dist/admin.js +0 -2
- package/dist/admin.js.map +0 -1
- package/dist/auth.d.ts +0 -2
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -2
- package/dist/auth.js.map +0 -1
- package/dist/bun.d.ts +0 -2
- package/dist/bun.d.ts.map +0 -1
- package/dist/bun.js +0 -2
- package/dist/bun.js.map +0 -1
- package/dist/cli/generate-cloudflare.d.ts +0 -7
- package/dist/cli/generate-cloudflare.d.ts.map +0 -1
- package/dist/cli/generate-cloudflare.js +0 -123
- package/dist/cli/generate-cloudflare.js.map +0 -1
- package/dist/cli/generate-project.d.ts +0 -25
- package/dist/cli/generate-project.d.ts.map +0 -1
- package/dist/cli/generate-project.js +0 -282
- package/dist/cli/generate-project.js.map +0 -1
- package/dist/cli/generate-sites.d.ts +0 -8
- package/dist/cli/generate-sites.d.ts.map +0 -1
- package/dist/cli/generate-sites.js +0 -273
- package/dist/cli/generate-sites.js.map +0 -1
- package/dist/cli/generate.d.ts +0 -20
- package/dist/cli/generate.d.ts.map +0 -1
- package/dist/cli/generate.js +0 -299
- package/dist/cli/generate.js.map +0 -1
- package/dist/cli/harness.d.ts +0 -3
- package/dist/cli/harness.d.ts.map +0 -1
- package/dist/cli/harness.js +0 -214
- package/dist/cli/harness.js.map +0 -1
- package/dist/cli/main.d.ts +0 -3
- package/dist/cli/main.d.ts.map +0 -1
- package/dist/cli/main.js +0 -77
- package/dist/cli/main.js.map +0 -1
- package/dist/cli/skills.d.ts +0 -3
- package/dist/cli/skills.d.ts.map +0 -1
- package/dist/cli/skills.js +0 -128
- package/dist/cli/skills.js.map +0 -1
- package/dist/cloudflare.d.ts +0 -2
- package/dist/cloudflare.d.ts.map +0 -1
- package/dist/cloudflare.js +0 -2
- package/dist/cloudflare.js.map +0 -1
- package/dist/codegen/emitMantleModule.d.ts +0 -22
- package/dist/codegen/emitMantleModule.d.ts.map +0 -1
- package/dist/codegen/emitMantleModule.js +0 -291
- package/dist/codegen/emitMantleModule.js.map +0 -1
- package/dist/codegen.d.ts +0 -2
- package/dist/codegen.d.ts.map +0 -1
- package/dist/codegen.js +0 -2
- package/dist/codegen.js.map +0 -1
- package/dist/runtime-testing.d.ts +0 -2
- package/dist/runtime-testing.d.ts.map +0 -1
- package/dist/runtime-testing.js +0 -2
- package/dist/runtime-testing.js.map +0 -1
- package/dist/runtime.d.ts +0 -2
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -2
- package/dist/runtime.js.map +0 -1
- package/dist/spec.d.ts +0 -2
- package/dist/spec.d.ts.map +0 -1
- package/dist/spec.js +0 -2
- package/dist/spec.js.map +0 -1
- package/dist/vercel-libsql.d.ts +0 -2
- package/dist/vercel-libsql.d.ts.map +0 -1
- package/dist/vercel-libsql.js +0 -2
- package/dist/vercel-libsql.js.map +0 -1
- package/dist/vercel.d.ts +0 -2
- package/dist/vercel.d.ts.map +0 -1
- package/dist/vercel.js +0 -2
- package/dist/vercel.js.map +0 -1
- package/dist/web.d.ts +0 -2
- package/dist/web.d.ts.map +0 -1
- package/dist/web.js +0 -2
- package/dist/web.js.map +0 -1
- package/docs/adapter-guide.md +0 -222
- package/docs/adr/0001-four-atom-manifest-model.md +0 -440
- package/docs/adr/0002-closed-enums-for-bindings.md +0 -253
- package/docs/adr/0007-ai-as-primary-author.md +0 -473
- package/docs/adr/0008-structured-diagnostic-shape.md +0 -338
- package/docs/adr/0009-consumer-supplied-manifests.md +0 -149
- package/docs/adr/0010-locale-and-translates.md +0 -453
- package/docs/adr/0011-adapter-port-spec.md +0 -281
- package/docs/adr/0012-views-as-public-rest.md +0 -161
- package/docs/adr/0013-agent-provisioned-consumer-projects.md +0 -154
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +0 -900
- package/docs/adr/0016-site-semantic-layer.md +0 -70
- package/docs/adr/0017-media-multi-variant-agent-side-optimization.md +0 -181
- package/docs/adr/0018-core-starters-repository-boundary.md +0 -179
- package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +0 -240
- package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +0 -199
- package/docs/adr/0021-retire-starter-scaffolding.md +0 -30
- package/docs/adr/0022-caller-observed-version-occ.md +0 -212
- package/docs/adr/0023-port-failure-contract.md +0 -69
- package/docs/adr/0024-manifest-native-schema-tables.md +0 -264
- package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +0 -105
- package/docs/adr/0026-progressive-generate.md +0 -36
- package/docs/adr/0027-scheduled-procedure-triggers.md +0 -31
- package/docs/adr/0028-schema-ttl-expiry.md +0 -33
- package/docs/adr/README.md +0 -81
- package/docs/adr/adr-lite-803-request-diagnostics.md +0 -38
- package/docs/adr/adr-lite-808-route-readiness.md +0 -47
- package/docs/adr/adr-lite-809-bounded-public-content.md +0 -71
- package/docs/adr/adr-lite-812-native-parity.md +0 -150
- package/docs/adr/adr-lite-823-home-statistics.md +0 -64
- package/docs/adr/adr-lite-842-mcp-authoring.md +0 -30
- package/docs/adr/adr-lite-845-frontend-client.md +0 -38
- package/docs/adr/adr-lite-861-admin-webmcp.md +0 -42
- package/docs/adr/adr-lite-909-admin-ui-kit.md +0 -37
- package/docs/agent-prompts.md +0 -64
- package/docs/api-mcp-authorization.md +0 -5
- package/docs/assets/mantle-admin-developer.png +0 -0
- package/docs/assets/mantle-admin-operations.png +0 -0
- package/docs/assets/mantle-hero.jpg +0 -0
- package/docs/auth-hosting-model.md +0 -166
- package/docs/cloudflare-low-level-composition.md +0 -5
- package/docs/consumer-onboarding-audit.md +0 -65
- package/docs/deferred-lifecycle-queues.md +0 -5
- package/docs/design-atoms.md +0 -16
- package/docs/design-references/editorial-blog-2026-05-05.md +0 -218
- package/docs/direct-authoring.md +0 -5
- package/docs/examples/builtin-commerce.md +0 -269
- package/docs/examples/builtin-intake.md +0 -143
- package/docs/examples/builtin-legal-documents.md +0 -189
- package/docs/examples/builtin-procurement.md +0 -241
- package/docs/examples/builtin-publication.md +0 -241
- package/docs/examples/builtin-reservation.md +0 -149
- package/docs/examples/cf-primitives-commerce-inventory.md +0 -809
- package/docs/examples/cf-primitives-guarded-api.md +0 -429
- package/docs/examples/cf-primitives-intake-hooks.md +0 -319
- package/docs/examples/host-chatgpt-sites/.openai/hosting.json +0 -1
- package/docs/examples/host-chatgpt-sites/README.md +0 -70
- package/docs/examples/host-chatgpt-sites/drizzle/0000_sites_users.sql +0 -8
- package/docs/examples/host-chatgpt-sites/drizzle/0001_mantle.sql +0 -294
- package/docs/examples/host-chatgpt-sites/drizzle/0002_article_cover.sql +0 -5
- package/docs/examples/host-chatgpt-sites/drizzle/0003_store_instance_id.sql +0 -1
- package/docs/examples/host-chatgpt-sites/drizzle/0004_mantle.sql +0 -10
- package/docs/examples/host-chatgpt-sites/drizzle/0005_mantle.sql +0 -24
- package/docs/examples/host-chatgpt-sites/drizzle/meta/_journal.json +0 -1
- package/docs/examples/host-chatgpt-sites/drizzle/meta/mantle-state.json +0 -60
- package/docs/examples/host-chatgpt-sites/manifests/site.yaml +0 -27
- package/docs/examples/host-chatgpt-sites/package.json +0 -1
- package/docs/examples/host-chatgpt-sites/public/site.css +0 -1
- package/docs/examples/host-chatgpt-sites/scripts/build.mjs +0 -13
- package/docs/examples/host-chatgpt-sites/scripts/check.mjs +0 -101
- package/docs/examples/host-chatgpt-sites/scripts/migration.mjs +0 -43
- package/docs/examples/host-chatgpt-sites/src/chatgpt-auth.ts +0 -66
- package/docs/examples/host-chatgpt-sites/src/index.ts +0 -46
- package/docs/examples/host-chatgpt-sites/src/mcp.ts +0 -49
- package/docs/examples/host-chatgpt-sites/src/media.ts +0 -109
- package/docs/examples/host-chatgpt-sites/src/r2-lab.ts +0 -38
- package/docs/examples/host-chatgpt-sites/src/storage-fingerprint.json +0 -1
- package/docs/examples/host-chatgpt-sites/src/web.ts +0 -35
- package/docs/examples/host-chatgpt-sites/tsconfig.json +0 -1
- package/docs/examples/host-chatgpt-sites/wrangler.jsonc +0 -10
- package/docs/examples/host-local-admin-otp/.dev.vars.example +0 -3
- package/docs/examples/host-local-admin-otp/README.md +0 -80
- package/docs/examples/host-local-admin-otp/ensure-dev-vars.mjs +0 -5
- package/docs/examples/host-local-admin-otp/manifests/site.yaml +0 -25
- package/docs/examples/host-local-admin-otp/package.json +0 -29
- package/docs/examples/host-local-admin-otp/public/.gitkeep +0 -1
- package/docs/examples/host-local-admin-otp/smoke.mjs +0 -141
- package/docs/examples/host-local-admin-otp/src/index.ts +0 -54
- package/docs/examples/host-local-admin-otp/tsconfig.json +0 -17
- package/docs/examples/host-local-admin-otp/wrangler.jsonc +0 -23
- package/docs/examples/host-minimal-worker/README.md +0 -41
- package/docs/examples/host-minimal-worker/manifests/site.yaml +0 -25
- package/docs/examples/host-minimal-worker/package.json +0 -26
- package/docs/examples/host-minimal-worker/smoke.mjs +0 -23
- package/docs/examples/host-minimal-worker/src/index.ts +0 -4
- package/docs/examples/host-minimal-worker/tsconfig.json +0 -17
- package/docs/examples/host-minimal-worker/wrangler.jsonc +0 -22
- package/docs/handbook/chatgpt-sites/equipment-checkout.md +0 -231
- package/docs/handbook/chatgpt-sites/generated-app.md +0 -84
- package/docs/handbook/chatgpt-sites/host-reference.md +0 -113
- package/docs/handbook/chatgpt-sites/index.md +0 -133
- package/docs/handbook/cloudflare/bindings.md +0 -291
- package/docs/handbook/cloudflare/conventional-worker.md +0 -166
- package/docs/handbook/cloudflare/deferred-hooks-queues.md +0 -170
- package/docs/handbook/cloudflare/low-level-composition.md +0 -169
- package/docs/handbook/cloudflare/public-web.md +0 -144
- package/docs/handbook/cloudflare/site-chrome.md +0 -75
- package/docs/handbook/examples/commerce-transaction.md +0 -11
- package/docs/handbook/examples/commerce.md +0 -11
- package/docs/handbook/examples/guarded-api.md +0 -10
- package/docs/handbook/examples/intake-form.md +0 -12
- package/docs/handbook/examples/intake-hooks.md +0 -11
- package/docs/handbook/examples/legal-documents.md +0 -10
- package/docs/handbook/examples/procurement-approvals.md +0 -10
- package/docs/handbook/examples/publication.md +0 -10
- package/docs/handbook/examples/reservation.md +0 -10
- package/docs/handbook/start/quickstart-admin.md +0 -52
- package/docs/labels.md +0 -71
- package/docs/media-uploads.md +0 -5
- package/docs/migration-0.1.2.md +0 -13
- package/docs/performance-harness.md +0 -228
- package/docs/release-process.md +0 -244
- package/docs/schema-indexes.md +0 -5
- package/docs/sealed-pipeline-ownership.md +0 -101
- package/docs/skills/plugin/SKILL.md +0 -132
- package/docs/skills/theme/SKILL.md +0 -77
- package/docs/spec-only-host-adoption.md +0 -157
- package/docs/transaction-patterns.md +0 -5
- package/skills/develop/SKILL.md +0 -258
- package/skills/install/SKILL.md +0 -112
- package/skills/integrate/SKILL.md +0 -60
- package/skills/media-gc/SKILL.md +0 -85
- package/skills/plugin/SKILL.md +0 -132
- package/skills/provision/SKILL.md +0 -194
- package/skills/theme/SKILL.md +0 -77
- package/skills/update/SKILL.md +0 -51
- package/templates/chatgpt-sites/auth.ts +0 -66
- package/templates/chatgpt-sites/build.mjs +0 -14
- package/templates/chatgpt-sites/env.ts +0 -1
- package/templates/chatgpt-sites/mcp.ts +0 -48
- package/templates/chatgpt-sites/public-mcp.ts +0 -29
- package/templates/chatgpt-sites/smoke-local.mjs +0 -20
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Application-owned service composition
|
|
2
|
+
|
|
3
|
+
Read the exact installed SDK/Admin versions. The [Native Admin guide](../guides/native-admin.md)
|
|
4
|
+
is the authority for generated collection bindings and retained console behavior.
|
|
5
|
+
|
|
6
|
+
The app owns `fetch`, native database handles, pools, auth options and deployment.
|
|
7
|
+
Select generated modules by importing them; omitted modules stay out of the bundle.
|
|
8
|
+
Keep application routing source outside
|
|
9
|
+
the generated directory. The [auth template](authentication.md), [surface contracts](../reference/surface.md)
|
|
10
|
+
and [media wiring](media-r2.md) describe the selected capabilities.
|
|
11
|
+
|
|
12
|
+
Mount auth (including discovery), consent, Admin, MCP and REST in that order,
|
|
13
|
+
matching 0.2. `authRoutes` owns both auth and consent paths. Supply one lazy,
|
|
14
|
+
request-local resolver to those surfaces, memoizing read-only and fresh authority
|
|
15
|
+
separately so a cached read cannot authorize a write. All cookie-session mutations
|
|
16
|
+
use the shared origin check. A custom identity
|
|
17
|
+
supplies the same verified `Caller`; absent directory/role facets remain absent.
|
|
18
|
+
Never construct a system Caller from credentials or request input.
|
|
19
|
+
|
|
20
|
+
Generate `Env` from the Wrangler config; do not hand-write it. Declare secrets
|
|
21
|
+
under `secrets.required`, run `pnpm exec wrangler types --include-runtime=false`,
|
|
22
|
+
add `worker-configuration.d.ts` to `tsconfig.json` and commit it.
|
|
23
|
+
`@cloudflare/workers-types` still supplies runtime types. Rerun after every binding
|
|
24
|
+
change; `wrangler types --include-runtime=false --check` fails on drift.
|
|
25
|
+
Once `secrets.required` is set, `wrangler dev` loads only those keys from
|
|
26
|
+
`.dev.vars`/`.env`, so declare every secret the app reads.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// src/worker.ts — application-owned; choose imports for the features you use.
|
|
30
|
+
import { authLoader, resolveCaller } from '@aotter/mantle/auth';
|
|
31
|
+
import { authRoutes } from '@aotter/mantle/auth/routes';
|
|
32
|
+
import { withAuthTasks } from '@aotter/mantle/auth/hardening';
|
|
33
|
+
import { checkOrigin, surfaceFailure, type Caller } from '@aotter/mantle/contracts';
|
|
34
|
+
import { admin, adminShell } from '@aotter/mantle-admin/server';
|
|
35
|
+
import { adminMcpApps } from '@aotter/mantle-admin/mcp';
|
|
36
|
+
import { entries } from '@aotter/mantle-admin/server/entries';
|
|
37
|
+
import { adminBindings } from '../generated/admin-bindings.js';
|
|
38
|
+
import { mcpAppCatalogues } from '../generated/admin-descriptors.js';
|
|
39
|
+
import { mcp } from '../generated/mcp.js';
|
|
40
|
+
import { rest } from '../generated/rest.js';
|
|
41
|
+
import { handlers } from './handlers.js'; // omit when there are no ref Procedures
|
|
42
|
+
|
|
43
|
+
// Env comes from worker-configuration.d.ts: declare DB, ASSETS, the email provider binding
|
|
44
|
+
// and the PUBLIC_ORIGIN, BETTER_AUTH_SECRET and ADMIN_EMAIL secrets in Wrangler.
|
|
45
|
+
// createAuth(env) is exported by the app's src/auth.ts (see the auth template).
|
|
46
|
+
let auth: import('@aotter/mantle/auth').AuthLoader<ReturnType<typeof import('./auth.js').createAuth>> | undefined;
|
|
47
|
+
export default {
|
|
48
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
49
|
+
return withAuthTasks(ctx.waitUntil.bind(ctx), async () => {
|
|
50
|
+
auth ??= authLoader(() => import('./auth.js').then(m => m.createAuth(env)));
|
|
51
|
+
const headers = new Headers(); // retain Better Auth's sliding-session cookies
|
|
52
|
+
let authority: Promise<Caller> | undefined, readAuthority: Promise<Caller> | undefined;
|
|
53
|
+
const resolve = (readOnly: boolean) => resolveCaller(request, auth!, {
|
|
54
|
+
readOnly,
|
|
55
|
+
audience: [`${env.PUBLIC_ORIGIN}/mcp`, `${env.PUBLIC_ORIGIN}/mcp/admin`],
|
|
56
|
+
responseHeaders: headers,
|
|
57
|
+
}).then(value => { checkOrigin(request, value); return value; });
|
|
58
|
+
const caller = (_request?: Request, _env?: Env, classification = { readOnly: false }) =>
|
|
59
|
+
classification.readOnly ? readAuthority ??= resolve(true) : authority ??= resolve(false);
|
|
60
|
+
const options = { db: (env: Env) => env.DB, caller, handlers };
|
|
61
|
+
const bindings = adminBindings({ handlers }); // use adminBindings() without ref Procedures
|
|
62
|
+
const routes = [
|
|
63
|
+
authRoutes(auth, { methods: [{ kind: 'email-otp' }], resolver: caller }),
|
|
64
|
+
admin(bindings.metadata, bindings.views, bindings.operations, {
|
|
65
|
+
...options, developerViews: bindings.developerViews, routes: entries(bindings.schemas, bindings.adapter),
|
|
66
|
+
}),
|
|
67
|
+
mcp({ ...options, apps: { admin: adminMcpApps({ catalogue: mcpAppCatalogues.admin }) } }),
|
|
68
|
+
rest(options),
|
|
69
|
+
];
|
|
70
|
+
try {
|
|
71
|
+
for (const route of routes) {
|
|
72
|
+
const response = await route(request, env);
|
|
73
|
+
if (!response) continue;
|
|
74
|
+
if (!headers.getSetCookie().length) return response;
|
|
75
|
+
const result = new Response(response.body, response);
|
|
76
|
+
for (const cookie of headers.getSetCookie()) result.headers.append('set-cookie', cookie);
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
// Serves the built Admin shell for /admin and its client routes; undefined for any other path.
|
|
80
|
+
const shell = await adminShell(request, path => env.ASSETS.fetch(new Request(new URL('/admin/' + path, request.url), request)));
|
|
81
|
+
if (shell) return shell;
|
|
82
|
+
// Your public frontend/web.ts routes can use an anonymous Caller without
|
|
83
|
+
// loading auth; their generated SQL still controls publication visibility.
|
|
84
|
+
return new Response('Not found', { status: 404, headers: { 'cache-control': 'no-store' } });
|
|
85
|
+
} catch (error) { return surfaceFailure(error); }
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Keep the MCP and REST entries together for the normal symmetric path. For a deliberate
|
|
92
|
+
asymmetric exception, remove the omitted surface's imports and array entry. `mcp.ts`
|
|
93
|
+
exists only when the manifest has an MCP surface and requires the documented optional
|
|
94
|
+
MCP peer. An application that deliberately does not mount MCP need not import generated
|
|
95
|
+
`mcp.ts`; outside the entry import graph, it contributes nothing to the bundle.
|
|
96
|
+
Pass the same handlers to each selected surface that exposes ref Procedures.
|
|
97
|
+
Bind site/media/identity capabilities through the selected Admin/MCP
|
|
98
|
+
options; do not expose native auth administrative routes without `authRoutes` or
|
|
99
|
+
`guardAuthAdminRequest`. `withAuthTasks` retains background callbacks on the
|
|
100
|
+
current request's context; `authHardening` configures the background handler.
|
|
101
|
+
Select `--admin` to emit `admin-bindings.ts`. Admin's API is a separate
|
|
102
|
+
`@aotter/mantle-admin` package; the SDK never imports it. The API handles only
|
|
103
|
+
`/admin/api`. `adminShell` (in the block above) serves the built shell for `/admin` and
|
|
104
|
+
client routes, including `/admin/sign-in`; it sets `frame-ancestors 'none'`, `X-Frame-Options: DENY`, `no-store` and `nosniff`
|
|
105
|
+
on the shell, answers a missing file under `/admin` with a 404 `Response`, and returns `undefined` for other paths and
|
|
106
|
+
non-GET requests. The app owns the rest of the CSP and the asset cache headers.
|
|
107
|
+
Collection statistics use the generated shared `statisticsView`:
|
|
108
|
+
its collection parameter selects only a generated table and visibility policy.
|
|
109
|
+
|
|
110
|
+
## Workers assets and secrets
|
|
111
|
+
|
|
112
|
+
`mantle generate --admin --wrangler wrangler.jsonc` sets the selected assets
|
|
113
|
+
directory. A new assets block defaults to `binding: "ASSETS"`, `run_worker_first: true`
|
|
114
|
+
and `html_handling: "none"`. Existing blocks keep their binding and routing; when
|
|
115
|
+
`run_worker_first` is already true, a missing `html_handling` defaults to `none`.
|
|
116
|
+
A different binding needs a custom Admin HTML loader. Deploy the built assets and
|
|
117
|
+
Worker together.
|
|
118
|
+
|
|
119
|
+
`mantle generate cloudflare-d1 generated manifests/*.yaml --auth src/auth.ts --admin`
|
|
120
|
+
updates the split-build Wrangler keys (`main` and `base_dir` under `dist/worker`),
|
|
121
|
+
ensures `nodejs_compat` and defaults to `global_fetch_strictly_public` ([public fetch routing](deploy-and-operate.md#public-fetch-routing))
|
|
122
|
+
and, with `--admin`, `assets.directory`: `mantle bundle` builds Admin into
|
|
123
|
+
`dist/client/admin`, served at `/admin`. Configure existing assets blocks explicitly;
|
|
124
|
+
JSONC comments and unrelated settings survive regeneration:
|
|
125
|
+
|
|
126
|
+
```jsonc
|
|
127
|
+
{
|
|
128
|
+
"assets": {
|
|
129
|
+
"directory": "dist/client",
|
|
130
|
+
"binding": "ASSETS",
|
|
131
|
+
"run_worker_first": true,
|
|
132
|
+
"html_handling": "none"
|
|
133
|
+
},
|
|
134
|
+
"d1_databases": [{ "binding": "DB", "database_name": "your-database", "database_id": "your-id" }]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`run_worker_first` lets the application supply shell/security/cache headers;
|
|
139
|
+
`none` keeps `index.html` available by name. `adminShell(request, assets)` passes
|
|
140
|
+
paths relative to the Admin directory, so the app fetches them under `/admin/`:
|
|
141
|
+
`path => env.ASSETS.fetch(new Request(new URL("/admin/" + path, request.url), request))`. Admin assets are not a public database
|
|
142
|
+
API. PostgreSQL uses application-owned Hyperdrive/postgres.js wiring,
|
|
143
|
+
with Hyperdrive query caching disabled for authority and mutation visibility.
|
|
144
|
+
|
|
145
|
+
Commit `.dev.vars.example` containing variable names and placeholders only:
|
|
146
|
+
|
|
147
|
+
```dotenv
|
|
148
|
+
PUBLIC_ORIGIN=http://localhost:8787
|
|
149
|
+
ADMIN_EMAIL=you@example.test
|
|
150
|
+
BETTER_AUTH_SECRET=replace-with-a-random-secret
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Copy it to ignored `.dev.vars`; use deployment secrets for production. Configure
|
|
154
|
+
an app-owned [`EmailSender`](../guides/email.md#application-wiring) and call its
|
|
155
|
+
`send({ to, subject, text, html?, locale, category? })` from Better Auth's email
|
|
156
|
+
callbacks. Adapt the auth template's `MAIL.otp`/`MAIL.link` callbacks to this
|
|
157
|
+
sender; it is an object, not a `.dev.vars` value. The auth
|
|
158
|
+
module wires `ADMIN_EMAIL` to the bootstrap helper, which requires verified email.
|
|
159
|
+
Name the variables as your `auth.ts` reads them (the app shell uses these three) and keep the trusted origin consistent.
|
|
160
|
+
|
|
161
|
+
## Scheduling and Bun
|
|
162
|
+
|
|
163
|
+
Scheduling remains app-owned; schedule/lifecycle Triggers are refused. Import
|
|
164
|
+
only the desired generated `sweepExpired` entry in `scheduled()`, pass a system
|
|
165
|
+
Caller with a reason, and choose an application-owned page budget for each
|
|
166
|
+
invocation; see the [TTL contract](../guides/native-execution.md#ttl-maintenance). Configure `triggers.crons` yourself;
|
|
167
|
+
`toCloudflareCron` converts POSIX schedules without adding a dispatcher. Keep
|
|
168
|
+
larger sweeps' cursors for a later invocation instead of scanning without a bound.
|
|
169
|
+
|
|
170
|
+
On Bun, reuse a process-owned `Database` or `SQL` pool and call the same generated
|
|
171
|
+
surfaces from `Bun.serve({fetch})`. Bun runs the server as TypeScript, so
|
|
172
|
+
`mantle bundle` builds only Admin for it: the `bun-*` target that
|
|
173
|
+
`generated/.mantle-inputs.json` records skips the Worker bundle, and Admin goes
|
|
174
|
+
to `dist/client/admin` (`--assets` as on Workers) when the server imports
|
|
175
|
+
`@aotter/mantle-admin/server`. Add it as the build script and run it before
|
|
176
|
+
starting the server:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{ "scripts": { "build": "mantle bundle src/server.ts dist" } }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The optional host reader is independent of the query target:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { bunAdminAssets } from '@aotter/mantle/bun/assets';
|
|
186
|
+
const assets = bunAdminAssets('dist/client/admin');
|
|
187
|
+
const bindings = adminBindings({ handlers });
|
|
188
|
+
const serveAdmin = admin(bindings.metadata, bindings.views, bindings.operations, {
|
|
189
|
+
db: () => db, caller, handlers, routes: entries(bindings.schemas, bindings.adapter),
|
|
190
|
+
});
|
|
191
|
+
// Call assets(path) from your separate shell/static route. adminShell(request, reader) wants
|
|
192
|
+
// paths without a leading slash and a Response (404 when missing), so adapt the reader:
|
|
193
|
+
const reader = async (path: string) => (await assets('/' + path)) ?? new Response(null, { status: 404 });
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The reader returns a native `Response` or `null`, rejects traversal/control
|
|
197
|
+
characters and escaping symlinks; the app owns shell, CSP and cache headers.
|
|
198
|
+
Use ignored `.env` on Bun.
|
|
199
|
+
|
|
200
|
+
Admin's build tools and UI packages are dependencies of `@aotter/mantle-admin`,
|
|
201
|
+
so a production install carries them (about 65 MB). For a container image, use a
|
|
202
|
+
multi-stage build: the build stage installs everything and runs `mantle bundle`
|
|
203
|
+
and `bun build src/server.ts --target bun --outdir dist/server`; the runtime stage
|
|
204
|
+
copies only `dist/` (plus any files the server reads at runtime) and starts
|
|
205
|
+
`bun dist/server/server.js`. Neither the Admin assets nor the bundled server read
|
|
206
|
+
`node_modules` once built. Run cron work with your native scheduler; neither
|
|
207
|
+
imports nor ordinary requests execute DDL, migrations or cron work. OAuth resource
|
|
208
|
+
rows are inserted on first OAuth use from runtime configuration, with no deploy
|
|
209
|
+
step; auth initialization and cookie/bearer/MCP/REST requests do no resource work.
|
|
210
|
+
|
|
211
|
+
## Commit generated output and verify it in CI
|
|
212
|
+
|
|
213
|
+
Commit every file written by `mantle generate`: TypeScript, `schema.sql`, optional
|
|
214
|
+
seed SQL and Wrangler edits. Never hand-edit generated output. Every emitted
|
|
215
|
+
TypeScript/SQL file carries the ownership header. Add the line printed by
|
|
216
|
+
generation to `.gitattributes`, for example:
|
|
217
|
+
|
|
218
|
+
```gitattributes
|
|
219
|
+
generated/** linguist-generated=true
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Resolve generated merge conflicts by merging authored manifests/options first,
|
|
223
|
+
then regenerating with the same options. Review the SQL/policy diff and write
|
|
224
|
+
explicit migrations before deployment. Keep `dist/` (the `mantle bundle` output)
|
|
225
|
+
untracked. Deployment uses committed generated source; it does not regenerate.
|
|
226
|
+
|
|
227
|
+
```sh
|
|
228
|
+
# CI, with the project's exact installed SDK and peer versions:
|
|
229
|
+
pnpm exec mantle generate cloudflare-d1 generated manifests/*.yaml --auth src/auth.ts --admin --check
|
|
230
|
+
pnpm exec tsc --noEmit
|
|
231
|
+
pnpm exec mantle bundle src/worker.ts dist # dist/worker, and dist/client/admin with --admin
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`--check` regenerates in memory, lists missing/changed/obsolete SDK-owned files
|
|
235
|
+
and stale Wrangler build settings, exits nonzero, and writes nothing. Unmarked
|
|
236
|
+
application files are preserved. TOML generation prints manual edits, so an
|
|
237
|
+
exact Wrangler `--check` requires JSON/JSONC. Use all the same `--site`, `--auth` and `--admin` options as normal generation. The check needs no database,
|
|
238
|
+
network access, credentials or deployment. Imported configuration modules must
|
|
239
|
+
remain pure: exporting `authOptions(env)` avoids constructing Better Auth at build time.
|
|
@@ -1,116 +1,19 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: One authorization pipeline for REST, MCP and Admin — identity kinds, static predicates, the dynamic guard, staff roles and 401/403/402.
|
|
3
|
-
---
|
|
4
1
|
# Authorization
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
The app verifies credentials and supplies a `Caller` from `@aotter/mantle/contracts`:
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
- Anonymous: `{ kind: 'anonymous' }`.
|
|
6
|
+
- User: `kind`, stable opaque `subject`, optional `issuer`, `role: string | null`, `admin?: boolean`, `scopes`, `credential`, `credentialId` and `clientId`. Credentials are session, OAuth, API key or personal token; never put raw credentials in the Caller.
|
|
7
|
+
- System: `{ kind: 'system', reason }`, constructed only by trusted application code for maintenance. Request data cannot grant system authority.
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
Resolve once per request. Invalid presented credentials must fail; do not downgrade them to anonymous. Use `checkOrigin(request, caller)` for cookie-session mutations; generated boundaries enforce it too. Keep response cookie headers from `resolveCaller` so sessions can slide.
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
2. Evaluate the static predicates in `requires.auth.all`, before any input-schema detail is exposed.
|
|
14
|
-
3. Validate and coerce the target input, or the View params.
|
|
15
|
-
4. Invoke the guard Procedure with that validated value and the same context.
|
|
16
|
-
5. Invoke the target only after the guard succeeds.
|
|
11
|
+
## Operations and rows
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
View/Procedure `requires.auth.all` gates invocation. `ctx.user` / `ctx.auth` require a user; `ctx.admin` requires the admin layer; `ctx.auth.scope` checks a scope. Which admin role may call which target is application code, decided before the call. [Requirements](../reference/authorization.md) lists the vocabulary. Store collections do not check these operation gates: authorize before using Store.
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
The compiler adds row policy to every Schema relation, including joins and nested SQL. Scope limits a user to their subject or to their [user field](../reference/authorization.md#user-fields) values and gives anonymous callers no scoped rows. System calls bypass scope. TTL hides expired rows. Public Views see only published rows on publishing Schemas; caller reads expose drafts only to admin/system. Translations also retain parent visibility.
|
|
21
16
|
|
|
22
|
-
|
|
17
|
+
Field `scope` hides rows even from admin callers; use View/Procedure [`restrict`](../reference/view.md#restrict) to limit member access while Admin collection reads see all rows allowed by the Schema's scope.
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
interface HandlerContext {
|
|
26
|
-
readonly user: { readonly id: string } | null;
|
|
27
|
-
readonly staff: { readonly id: string; readonly role: StaffRole } | null;
|
|
28
|
-
readonly auth?: {
|
|
29
|
-
readonly credential: "session" | "oauth" | "api-key" | "personal-token";
|
|
30
|
-
readonly credentialId: string | null;
|
|
31
|
-
readonly clientId: string | null;
|
|
32
|
-
readonly scopes: readonly string[];
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Three things are distinct:
|
|
38
|
-
|
|
39
|
-
- **`ctx.user`** is the end-user identity: a row in the site's own Better Auth user table. A service API key may have no user at all.
|
|
40
|
-
- **`ctx.staff`** is a role overlay on top of a user, not a separate account. A user with no overlay is `null` here.
|
|
41
|
-
- **`ctx.auth`** is the verified credential itself, of kind `session`, `oauth`, `api-key` or `personal-token`. Raw keys, refresh tokens and cookies never enter the runtime; `credentialId` is an opaque record id.
|
|
42
|
-
|
|
43
|
-
The predicate vocabulary is closed and maps onto those three: `ctx.user`, `ctx.auth`, `{ "ctx.auth.scope": "<scope>" }` (repeat it to require several), and `{ "ctx.staff": [roles] }`. There is no credential-kind predicate — a target that must reject browser sessions does so by requiring a scope no session grant carries.
|
|
44
|
-
|
|
45
|
-
## Predicates versus the guard
|
|
46
|
-
|
|
47
|
-
| | Static predicates | Guard Procedure |
|
|
48
|
-
|---|---|---|
|
|
49
|
-
| Declared as | `requires.auth.all` | `requires.guard.procedure` |
|
|
50
|
-
| Answers | Is this caller of the right kind, with the right role and scopes? | Is this verified caller allowed to do this business action right now? |
|
|
51
|
-
| Evaluated | Before input validation | After input validation, on every call, never cached |
|
|
52
|
-
| Reads | The compiled plan only | Your tables, your provider state, `ctx.env` |
|
|
53
|
-
| Typical failure | `401` or `403` | `402` |
|
|
54
|
-
|
|
55
|
-
Anything that changes independently of the credential belongs in the guard: payment state, subscription or membership status, seat counts, per-tenant entitlement. A key stays valid while a subscription lapses, so the fact that a caller paid is not something a token can carry. The guard is an ordinary unguarded `handler.kind: ref` Procedure — not a fifth atom — and it throws a structured diagnostic to deny:
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
throw new DiagnosticError(
|
|
59
|
-
runtimeDiagnostic({
|
|
60
|
-
code: "ENTITLEMENT_REQUIRED",
|
|
61
|
-
severity: "error",
|
|
62
|
-
path: `site:membership/${ctx.user!.id}`,
|
|
63
|
-
message: "Active membership is required.",
|
|
64
|
-
}),
|
|
65
|
-
);
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## 401, 403 and 402
|
|
69
|
-
|
|
70
|
-
| Status | Diagnostic | Meaning |
|
|
71
|
-
|---|---|---|
|
|
72
|
-
| `401` | `UNAUTHENTICATED` | No credential, or a recognized credential that is bad, revoked or expired. |
|
|
73
|
-
| `403` | `AUTH_DENIED` | Verified caller, but a required predicate failed — a missing scope, a missing user subject, an insufficient staff role. |
|
|
74
|
-
| `402` | `ENTITLEMENT_REQUIRED` | Verified and permitted caller whose current business state does not allow the action. |
|
|
75
|
-
|
|
76
|
-
The split is the point: `401` says *who are you*, `403` says *you may not*, `402` says *not until you settle something*. Only the guard produces `402`, and on `402` the target handler is never invoked. See [Diagnostic codes](../reference/diagnostics.md) for the full mapping.
|
|
77
|
-
|
|
78
|
-
## Staff roles
|
|
79
|
-
|
|
80
|
-
Staff roles are `owner`, `editor` and `contributor`. Owners manage staff and site settings; editors publish, approve and manage entries; contributors work on drafts.
|
|
81
|
-
|
|
82
|
-
A manifest predicate tests **exact membership**, not rank: `{ "ctx.staff": [editor] }` admits editors and nobody else, an owner included. List every role you mean, as in `{ "ctx.staff": [owner, editor] }`. Rank ordering does exist, but only Admin's own route table uses it, which is why an owner passes an Admin route gated at editor. See [Authorization requirements](../reference/authorization.md).
|
|
83
|
-
|
|
84
|
-
The role is re-read from the database on every protected REST and MCP call. A token snapshot or a consent-time role is not an authorization boundary, so demoting or revoking a user in Admin locks them out on their very next request — no token revocation, no cache flush, no waiting for expiry.
|
|
85
|
-
|
|
86
|
-
## The credential resolver seam
|
|
87
|
-
|
|
88
|
-
Sites that issue their own API keys or personal tokens supply one `ConsumerCredentialResolver` through the Worker's `extend` seam. It returns exactly one of three outcomes:
|
|
89
|
-
|
|
90
|
-
| Outcome | Meaning |
|
|
91
|
-
|---|---|
|
|
92
|
-
| `not-handled` | The request carries none of the site's credential formats. Resolution moves on. |
|
|
93
|
-
| `invalid` | The request carries a recognized format that is bad, revoked or malformed. |
|
|
94
|
-
| `verified` | The site's authoritative record was checked; normalized metadata is returned. |
|
|
95
|
-
|
|
96
|
-
Resolution precedence is site resolver, then configured OAuth bearer, then cookie session. A recognized-but-invalid credential never falls back to a valid cookie: presenting a revoked key is a failure, not an invitation to be treated as an anonymous browser. Return `not-handled`, never `invalid`, for a request your resolver simply does not recognize.
|
|
97
|
-
|
|
98
|
-
Scopes are opaque strings the site defines and grants. Mantle compares them; it does not interpret `catalog:read`, publish a scope catalog, or infer a hierarchy. Correspondingly, Mantle issues and stores no API keys or personal tokens, and holds no payment or subscription state. Those tables, their issuance, hashing, rotation and revocation, and whatever fills them from a billing provider, are application code. Mantle owns verification, normalization and enforcement.
|
|
99
|
-
|
|
100
|
-
## MCP is the same pipeline
|
|
101
|
-
|
|
102
|
-
An MCP `tools/call` runs the identical evaluator, in the identical order, with the identical diagnostics — surfaced as JSON-RPC errors carrying `error.data.code` instead of an HTTP status. A Trigger's `source.surface` selects which catalog lists a tool, and `tools/list` hides what the caller cannot see. That filtering is discovery UX, not enforcement: a client that guesses a tool name still meets every predicate and the guard. Discovery is never the authorization boundary. See [MCP and agents](./mcp-and-agents.md).
|
|
103
|
-
|
|
104
|
-
## Related
|
|
105
|
-
|
|
106
|
-
- [Authorization requirements](../reference/authorization.md) — the `requires` shape, predicate forms, guard rules, OpenAPI projection.
|
|
107
|
-
- [Guarded API access](../../examples/cf-primitives-guarded-api.md) — a full resolver, guards and the REST/MCP outcome tables.
|
|
108
|
-
- [Reads: Views, REST and MCP](./views.md) — the `$ctx.user` identity-View sentinel.
|
|
109
|
-
- [Authentication](../cloudflare/authentication.md) — sessions, first owner, role management routes.
|
|
110
|
-
|
|
111
|
-
## Source
|
|
112
|
-
- [`docs/adapter-guide.md`](../../../docs/adapter-guide.md)
|
|
113
|
-
- [`packages/mantle-runtime/src/domain/model/HandlerContext.ts`](../../../packages/mantle-runtime/src/domain/model/HandlerContext.ts)
|
|
114
|
-
- [`packages/mantle-runtime/src/usecase/procedure/InvokeProcedureUseCase.ts`](../../../packages/mantle-runtime/src/usecase/procedure/InvokeProcedureUseCase.ts)
|
|
115
|
-
- [`packages/adapters/cloudflare/src/mount/resolveCaller.ts`](../../../packages/adapters/cloudflare/src/mount/resolveCaller.ts)
|
|
116
|
-
- [`packages/mantle-spec/src/domain/service/StaffRoleHierarchy.ts`](../../../packages/mantle-spec/src/domain/service/StaffRoleHierarchy.ts)
|
|
19
|
+
The app owns custom identity and roles. Better Auth’s admin plugin `adminRoles` (default `["admin"]`) classifies comma-separated `user.role` values. `resolveCaller` carries this layer as `admin`; `isAdmin(caller)` and `hasAdminRole(caller, name)` consult it. Callers come from `resolveCaller` over Better Auth; an existing identity provider connects through Better Auth's OAuth or SSO plugins, since no public API builds a Caller from another provider's session (`authenticatedAs` in `@aotter/mantle/testing` is for tests). Setting `admin: true` on a plain object grants no privilege. Member role names alone do not grant privilege. Optional [Better Auth](../cloudflare/authentication.md) helpers use fresh primary authority by default. Admin additionally requires an admin cookie session. UI visibility never replaces a server authorization check.
|
|
@@ -1,173 +1,14 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Schema, View, Procedure and Trigger — what each atom owns, why reads and writes are asymmetric, and how to compose everything else from four kinds.
|
|
3
|
-
---
|
|
4
1
|
# The four atoms
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
The manifest grammar is closed: Schema, View, Procedure and Trigger.
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
| Atom | Declares | Native output |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Schema | Fields, indexes, scope, TTL, lifecycle and Admin presentation | Initial DDL and typed Store collection |
|
|
8
|
+
| View | One SQL SELECT with input, authorization and surface | Typed query; optional paging and exposure |
|
|
9
|
+
| Procedure | SQL statements or a ref handler contract | Typed operation with declared authorization |
|
|
10
|
+
| Trigger | HTTP or MCP exposure of a Procedure | Selected route/tool binding |
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
Generation rejects lifecycle and schedule Triggers, although they exist in the v2 grammar. Application code owns scheduling and side effects. Unknown manifest keys fail validation; a new runtime capability does not open the grammar.
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|---|---|---|---|
|
|
14
|
-
| `Schema` | `CREATE TABLE` | No. Reached only through a View or a Procedure. | No |
|
|
15
|
-
| `View` | `CREATE VIEW` | Yes. Auto-mounted on its declared `public` or `staff` REST and MCP surface. | No |
|
|
16
|
-
| `Procedure` | `CREATE FUNCTION` | No. Transport-agnostic; needs a Trigger to gain a surface. | Yes — a handler in your project's registry. |
|
|
17
|
-
| `Trigger` | `CREATE TRIGGER` plus route and tool binding | Yes. It is the binding atom. | No |
|
|
18
|
-
|
|
19
|
-
A four-word gloss covers most questions: Schema is state, View is the read API, Procedure is the write API, Trigger is the binding.
|
|
20
|
-
|
|
21
|
-
## Reads and writes are asymmetric on purpose
|
|
22
|
-
|
|
23
|
-
Views mount themselves; Procedures do not. That asymmetry matches HTTP safe-versus-unsafe semantics. A View is a named, read-only query: idempotent, cacheable, and safe to expose the moment its `surface` is declared. A Procedure changes state, so its path, method, tool name and authorization are decisions an author must make explicitly, one Trigger at a time. There is no `Schema.spec.expose.rest` switch and no `/api/<collection>` shortcut; exposing collections directly was considered and rejected, because a Schema stores drafts, server-stamped fields and rows the author never meant to publish.
|
|
24
|
-
|
|
25
|
-
The consequence for authors: a Procedure that has no Trigger is unreachable from outside the runtime, and calling its name as a URL returns `404`.
|
|
26
|
-
|
|
27
|
-
## The composition rule
|
|
28
|
-
|
|
29
|
-
Anything more domain-shaped than these four is not an atom. A Form, Membership, Email, Webhook, Workflow or ScheduledJob is a composition of Schemas, Views, Procedures and Triggers plus your own TypeScript. A fifth kind would recreate the same overlap: authors would have to decide whether a contact form was a Form, a Workflow or an Email.
|
|
30
|
-
|
|
31
|
-
If you find yourself wanting another kind, sketch the same thing as a composition of the four first. Almost always it works.
|
|
32
|
-
|
|
33
|
-
## The Manifest envelope
|
|
34
|
-
|
|
35
|
-
Every document carries the same four top-level keys. Unknown keys at any known level are rejected with `INVALID_MANIFEST_ENVELOPE`.
|
|
36
|
-
|
|
37
|
-
```yaml
|
|
38
|
-
apiVersion: cms.mantle.aotter.net/v1
|
|
39
|
-
kind: Schema | View | Procedure | Trigger
|
|
40
|
-
metadata:
|
|
41
|
-
name: posts
|
|
42
|
-
spec: {}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
There is no `namespace` field. Names are unique within a kind, so a Schema and a View may share a name; two Schemas may not. Multi-tenancy belongs in your application layer, not in manifest metadata.
|
|
46
|
-
|
|
47
|
-
One feature usually needs several atoms, so YAML's `---` separator keeps the file count down. Core reads every immediate `.yaml` and `.yml` file in the manifest directory and parses each document independently:
|
|
48
|
-
|
|
49
|
-
```yaml
|
|
50
|
-
# manifests/contact.yaml
|
|
51
|
-
apiVersion: cms.mantle.aotter.net/v1
|
|
52
|
-
kind: Procedure
|
|
53
|
-
metadata:
|
|
54
|
-
name: send-contact-message
|
|
55
|
-
spec:
|
|
56
|
-
input:
|
|
57
|
-
type: object
|
|
58
|
-
additionalProperties: false
|
|
59
|
-
required: [name, message]
|
|
60
|
-
properties:
|
|
61
|
-
name: { type: string, minLength: 1, maxLength: 80 }
|
|
62
|
-
message: { type: string, minLength: 1, maxLength: 4000 }
|
|
63
|
-
output: { type: object }
|
|
64
|
-
handler: { kind: ref, ref: send-contact-message }
|
|
65
|
-
---
|
|
66
|
-
apiVersion: cms.mantle.aotter.net/v1
|
|
67
|
-
kind: Trigger
|
|
68
|
-
metadata:
|
|
69
|
-
name: contact-http
|
|
70
|
-
spec:
|
|
71
|
-
source: { kind: http, method: POST, path: /api/contact }
|
|
72
|
-
target: { procedure: send-contact-message }
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Parsing is all-or-nothing: one error in one document means no `ParsedManifestSet` at all.
|
|
76
|
-
|
|
77
|
-
## Schema — the entity
|
|
78
|
-
|
|
79
|
-
A Schema declares one collection: the JSON Schema for each entry's `data`, its indexes, its Admin presentation and its [lifecycle mode](./lifecycle-and-locales.md). Entries also carry six [native columns](../reference/schema.md#reserved-entry-columns) outside `data`.
|
|
80
|
-
|
|
81
|
-
```yaml
|
|
82
|
-
apiVersion: cms.mantle.aotter.net/v1
|
|
83
|
-
kind: Schema
|
|
84
|
-
metadata:
|
|
85
|
-
name: notes
|
|
86
|
-
spec:
|
|
87
|
-
title: Notes
|
|
88
|
-
lifecycle: publishing
|
|
89
|
-
schema:
|
|
90
|
-
type: object
|
|
91
|
-
additionalProperties: false
|
|
92
|
-
required: [title, body]
|
|
93
|
-
properties:
|
|
94
|
-
title: { type: string, minLength: 1, maxLength: 200 }
|
|
95
|
-
body: { type: string, x-mcp-hint: markdown }
|
|
96
|
-
searchableFields: [title]
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Full field rules, the JSON Schema subset and the `x-mantle-bind` / `x-mantle-ref` / `x-mcp-hint` keywords are in the [Schema reference](../reference/schema.md).
|
|
100
|
-
|
|
101
|
-
## View — the read surface
|
|
102
|
-
|
|
103
|
-
A View is a named read-only query over Schemas, mounted on exactly one surface. No Trigger is involved.
|
|
104
|
-
|
|
105
|
-
```yaml
|
|
106
|
-
apiVersion: cms.mantle.aotter.net/v1
|
|
107
|
-
kind: View
|
|
108
|
-
metadata:
|
|
109
|
-
name: published-notes
|
|
110
|
-
spec:
|
|
111
|
-
title: Published notes
|
|
112
|
-
surface: public
|
|
113
|
-
from: notes
|
|
114
|
-
fields: [id, title, updatedAt]
|
|
115
|
-
filter:
|
|
116
|
-
eq: { field: status, value: published }
|
|
117
|
-
orderBy:
|
|
118
|
-
- { field: updatedAt, direction: desc }
|
|
119
|
-
limit: 20
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
`surface: public` serves `GET /api/views/published-notes` and the `query_view_published_notes` MCP tool on `/mcp`; `surface: staff` moves both behind the staff gate. See [Reads: Views, REST and MCP](./views.md) and the [View reference](../reference/view.md).
|
|
123
|
-
|
|
124
|
-
## Procedure — the typed callable
|
|
125
|
-
|
|
126
|
-
A Procedure declares typed `input`, typed `output`, optional `requires`, and one `handler`. It is the only atom with a code seam. The handler is either `kind: builtin` — a CRUD shortcut over one Schema — or `kind: ref`, an opaque key into the `handlers` map your project registers.
|
|
127
|
-
|
|
128
|
-
```yaml
|
|
129
|
-
spec:
|
|
130
|
-
handler: { kind: builtin, op: create, schema: notes }
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Builtin ops are `create`, `update`, `upsert`, `delete` and `archive`. `request_publish` and `publish` are deliberately absent: they are lifecycle operations, not CRUD primitives. See [Writes: Procedures, Triggers and lifecycle hooks](./procedures-and-triggers.md) and the [Procedure reference](../reference/procedure.md).
|
|
134
|
-
|
|
135
|
-
## Trigger — the binding
|
|
136
|
-
|
|
137
|
-
A Trigger says "when X happens, run Procedure Y". Its `source` is one of three kinds, and its `target.procedure` names a declared Procedure.
|
|
138
|
-
|
|
139
|
-
```yaml
|
|
140
|
-
spec:
|
|
141
|
-
source: { kind: http, method: POST, path: /api/contact }
|
|
142
|
-
# or: { kind: mcp, surface: public }
|
|
143
|
-
# or: { kind: lifecycle, schema: notes, on: [before_create] }
|
|
144
|
-
target: { procedure: send-contact-message }
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Several Triggers may target one Procedure, which is how the same handler becomes an HTTP endpoint, an MCP tool and a lifecycle hook without duplicated logic. Adding a transport is additive; the Procedure never changes. See the [Trigger reference](../reference/trigger.md).
|
|
148
|
-
|
|
149
|
-
## How to think when extending
|
|
150
|
-
|
|
151
|
-
1. **What entities does the feature need?** One `Schema` each.
|
|
152
|
-
2. **What named queries?** One `View` each. Ad-hoc reads stay inside handler code; only sanctioned queries become Views, because a View is an external read endpoint.
|
|
153
|
-
3. **What operations?** One `Procedure` each. One Procedure equals one typed function call. Compose in handler code, not in YAML.
|
|
154
|
-
4. **What invokes them?** One `Trigger` per declared HTTP, MCP or lifecycle source.
|
|
155
|
-
5. **Who is allowed?** `requires.auth` for static identity and scope, plus one optional `requires.guard.procedure` for a live business check. See [Authorization](./authorization.md).
|
|
156
|
-
|
|
157
|
-
## The Postgres heuristic
|
|
158
|
-
|
|
159
|
-
When you are unsure whether something deserves to be an atom, ask what it would be in Postgres.
|
|
160
|
-
|
|
161
|
-
- If it is a `CREATE X` an **application developer** writes — `TABLE`, `VIEW`, `FUNCTION`, `TRIGGER` — it maps to one of the four.
|
|
162
|
-
- If it is a `CREATE X` Postgres only needs because it is a database engine — `INDEX`, `MATERIALIZED VIEW`, `TABLESPACE`, `STATISTICS`, `EVENT TRIGGER`, `EXTENSION`, `LANGUAGE`, `PUBLICATION`, `FOREIGN TABLE`, `RULE` — it does not map, and it belongs behind the storage adapter.
|
|
163
|
-
- If it is a type-system extension — `TYPE`, `DOMAIN`, `SEQUENCE`, `COLLATION`, `CAST`, `AGGREGATE`, `OPERATOR` — it folds into the JSON Schema inside a `Schema`.
|
|
164
|
-
|
|
165
|
-
Postgres exposes roughly twenty-five object kinds; an application developer writes four to six of them. That is the same ratio Mantle ships.
|
|
166
|
-
|
|
167
|
-
## Source
|
|
168
|
-
|
|
169
|
-
- [`docs/adr/0001-four-atom-manifest-model.md`](../../../docs/adr/0001-four-atom-manifest-model.md)
|
|
170
|
-
- [`docs/adr/0012-views-as-public-rest.md`](../../../docs/adr/0012-views-as-public-rest.md)
|
|
171
|
-
- [`packages/mantle-spec/src/domain/model/ManifestGrammar.ts`](../../../packages/mantle-spec/src/domain/model/ManifestGrammar.ts)
|
|
172
|
-
- [`packages/mantle-spec/src/domain/service/ManifestParser.ts`](../../../packages/mantle-spec/src/domain/service/ManifestParser.ts)
|
|
173
|
-
- [`docs/skills/develop/SKILL.md`](../../skills/develop/SKILL.md)
|
|
14
|
+
SQL is authored in PostgreSQL syntax and rendered for exactly postgres or sqlite. Generation applies row policy; the app supplies its own native handle and verified Caller. See [manifest reference](../reference/manifest.md) and [supported declarations](../reference/features.md).
|