@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
|
@@ -1,355 +1,213 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
# Auth in Mantle
|
|
2
|
+
|
|
3
|
+
Your application configures `betterAuth()` and its official plugins in `auth.ts`.
|
|
4
|
+
Mantle supplies the database adapter, schema generation and caller resolution:
|
|
5
|
+
|
|
6
|
+
| Mantle | What it does |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| `@aotter/mantle/auth/adapter` | `d1Adapter(env.DB)` / `postgresAdapter(sql)`: Better Auth's `database` on the same native handle as your data, no Kysely. `postgresAdapter` takes the PostgreSQL client the data layer uses, postgres.js (`cloudflare-postgres`, usually through Hyperdrive) or Bun.SQL; on Workers create that client per request, or per isolate with `max: 1`. Both pass Better Auth's official adapter suite (`@better-auth/test-utils/adapter`). |
|
|
9
|
+
| `mantle generate … --auth ./auth.ts` | Adds the tables Better Auth declares for your options (`getSchema`) to `schema.sql`. |
|
|
10
|
+
| `@aotter/mantle/generate/auth` | Programmatic `generateFromSources({ target, sources, auth: authOptions, ... })`; the selected entry types `auth` as Better Auth options. |
|
|
11
|
+
| `@aotter/mantle/auth` | `authLoader` and `resolveCaller(request, loader, { audience })`: the `Caller` that generated queries take. |
|
|
12
|
+
|
|
13
|
+
## `auth.ts`
|
|
5
14
|
|
|
6
|
-
|
|
15
|
+
> **Verification status:** the GitHub social-provider and magic-link recipes have not yet been exercised end to end against real deployed provider flows.
|
|
16
|
+
|
|
17
|
+
Copy `src/auth.ts` from the [app shell](../../examples/app-shell/README.md), which CI runs from the packed packages;
|
|
18
|
+
installed, it is `node_modules/@aotter/mantle/docs/examples/app-shell/src/auth.ts`. Its essentials:
|
|
7
19
|
|
|
8
|
-
|
|
20
|
+
- Imports: `betterAuth` from `better-auth/minimal`; `admin`, `emailOTP` (and `jwt` for MCP) from `better-auth/plugins`;
|
|
21
|
+
`createAccessControl` from `better-auth/plugins/access`; `defaultStatements` from `better-auth/plugins/admin/access`;
|
|
22
|
+
`d1Adapter` from `@aotter/mantle/auth/adapter`; the hardening and bootstrap helpers below. For MCP it also imports `jwt`,
|
|
23
|
+
`mcp` from `@better-auth/mcp`, `cimd` from `@better-auth/cimd` and `oauthGrantExtension`/`oauthGrantRevocation` from
|
|
24
|
+
`@aotter/mantle/auth/oauth`; the file marks that block as deletable.
|
|
25
|
+
- `authOptions(env)` returns plain options and is the module's export for `--auth src/auth.ts`. Generation calls it with an
|
|
26
|
+
empty `env`, so it must not throw: fall back to placeholders for `secret` and origin there.
|
|
27
|
+
- The bootstrap admin: `d1BootstrapAdmin(env.DB, { match: "email", value: env.ADMIN_EMAIL }, () => ({ options: { plugins } }))`
|
|
28
|
+
is assigned to `databaseHooks.user.create.after` and `.update.after` ([Identity helpers](#identity-helpers)).
|
|
29
|
+
- `createAuth(env)` is the same module's second export: it checks its three variables are set and returns
|
|
30
|
+
`betterAuth(authOptions(env))`. The Worker's `authLoader(() => import("./auth.js").then(m => m.createAuth(env)))`
|
|
31
|
+
([service composition](service-entry.md)) is what calls it.
|
|
9
32
|
|
|
10
|
-
The
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
loopback origin. If you author the Worker yourself, pass `auth` to
|
|
15
|
-
`createMantleWorker` with `email-otp`, `ConsoleEmailSender`, and
|
|
16
|
-
`bootstrapOwner.match: "email"`; that factory does not read the conventional
|
|
17
|
-
mode matrix. The one-time code is printed in wrangler logs. See
|
|
18
|
-
[Quickstart: local Admin](../start/quickstart-admin.md). `ConsoleEmailSender`
|
|
19
|
-
is for `wrangler dev` only; production needs a real sender. Match
|
|
20
|
-
`PUBLIC_ORIGIN` to wrangler's actual `127.0.0.1:8787` origin or Better Auth
|
|
21
|
-
will reject OTP with `INVALID_ORIGIN`.
|
|
33
|
+
The application owns Better Auth and its optional plugins. Schema generation reads plain options without constructing
|
|
34
|
+
Better Auth; apply `schema.sql` during deployment, never at boot. OAuth resource
|
|
35
|
+
rows are created on first OAuth use from runtime configuration; they require no
|
|
36
|
+
resource deployment step.
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
including its required provider credentials and `ADMIN_GITHUB_LOGIN`, instead
|
|
26
|
-
of editing the CLI-owned Worker. An application that needs production email
|
|
27
|
-
delivery should author its own Worker Auth factory and real `EmailSender`.
|
|
38
|
+
OSS uses `admin({adminRoles: ["admin"], ac, roles: {admin: ac.newRole(defaultStatements), user: ac.newRole({user: [], session: []})}})`.
|
|
39
|
+
Mantle reads the official admin plugin’s `adminRoles` when auth loads (default `["admin"]`); it has no role setting of its own. `Caller.role` preserves comma-separated roles and `Caller.admin` carries the layer. A caller holding any configured admin role is an admin; a member’s role names never grant privilege. Publishing, Admin and `/mcp/admin` read this layer. Better Auth still checks permissions for its identity API calls.
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
`@aotter/mantle/auth/hardening` supplies optional helpers:
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
`
|
|
33
|
-
`
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
deploy console delivery.
|
|
43
|
+
- `hashEmailOtp(secret, otp)` is keyed HMAC-SHA-256. Pass it as emailOTP's `storeOTP.hash`; an unkeyed hash permits enumeration of six-digit codes after a database leak.
|
|
44
|
+
- `authHardening({ipAddressHeaders, baseURL, errorURL})` refuses missing host-trusted ingress headers and cross-origin error redirects, and enables Better Auth's rate limiter with `storage: "database"`, which the Better Auth docs recommend for serverless: counts are shared across isolates, and Better Auth's defaults and plugin rules (for example 3 per minute per email-OTP path) set the limits. The generated schema includes the `rateLimit` table. Never assume client X-Forwarded-For is trusted.
|
|
45
|
+
- `guardGithubLogin(user, context, trustedProviders)` goes in user create/update before-hooks. Only explicitly trusted provider callbacks can populate this field. GitHub maps `githubLogin: profile.login`; generic OAuth ids need trusted profile mappers.
|
|
46
|
+
- `withAuthTasks(ctx.waitUntil.bind(ctx), () => auth.handler(request))` retains background sends/cleanup for the current request, with concurrent requests isolated. `authHardening` installs `retainAuthTask` as Better Auth's background handler. Failure logs are generic and contain no OTPs/credentials.
|
|
47
|
+
- `authSetupIncomplete()` returns 503/no-store while secrets/providers are unconfigured; do not authenticate by fallback.
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
Better Auth config is app-owned, so restrict who may sign up (an allowlist, an invitation) with a
|
|
50
|
+
`databaseHooks.user.create.before` hook that throws `APIError("FORBIDDEN")` from `better-auth/api` (the app shell calls
|
|
51
|
+
`src/signup.ts` there; returning `false` instead breaks email-OTP sign-in in Better Auth 1.7.7, which does not expect a refused create).
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
Magic links use hashed tokens, 15-minute expiry and the same task retention. Email callbacks pass the first
|
|
54
|
+
Accept-Language tag (fallback `en`) to the app's sender. `authRoutes` (as the app shell mounts it) also guards raw Better Auth `/admin/*` routes with the fresh database-backed Caller and the shared session origin check, so a stale KV role cannot bypass the directory gate. Mount that handler when selecting session KV. Configure ordinary Better Auth social providers;
|
|
55
|
+
Apple needs `https://appleid.apple.com` trusted origin and secure SameSite=None callback cookies. Generic OAuth login
|
|
56
|
+
uses `genericOAuth`, distinct provider ids and discovery or explicit authorization/token URLs. Cookie prefixes,
|
|
57
|
+
account linking and trusted origins remain app-owned. Host-only cookies require HTTPS, secure/path=/, a `__Host-`
|
|
58
|
+
prefix and no Domain/cross-subdomain sharing.
|
|
44
59
|
|
|
45
|
-
|
|
60
|
+
For OAuth/MCP add `jwt({disableSettingJwtHeader:true})`, official `mcp` plus `cimd` for client metadata discovery,
|
|
61
|
+
or `oauthProvider` for ordinary OAuth. These are optional app dependencies behind the dynamic auth import.
|
|
62
|
+
Use the live grant/session hooks and route wiring in [OAuth](../guides/oauth.md); merge verification hooks with the app shell's
|
|
63
|
+
user hooks. `genericOAuth` is a login plugin, distinct from the OAuth provider.
|
|
46
64
|
|
|
47
|
-
|
|
48
|
-
- Self-managed uses the site's own GitHub OAuth app. Register its callback URL as `<PUBLIC_ORIGIN>/api/auth/callback/github`.
|
|
49
|
-
- Hosted is a public PKCE client with no client secret. `MANTLE_HOSTED_AUTH_ISSUER` must be an HTTPS root origin (no path, query or fragment; `http` only for loopback). `MANTLE_HOSTED_AUTH_CLIENT_ID` must be a URL on that same origin shaped `/clients/<id>`.
|
|
50
|
-
- `ADMIN_GITHUB_LOGIN` must be a valid GitHub login.
|
|
65
|
+
## Identity helpers
|
|
51
66
|
|
|
52
|
-
|
|
67
|
+
`@aotter/mantle/auth/identity` exports `d1Identity(db, auth, caller, request.headers)` and
|
|
68
|
+
`postgresIdentity(sql, auth, caller, request.headers)` (Bun.SQL/postgres.js). Helpers use the template's default
|
|
69
|
+
Better Auth table/field names. Every operation requires a current admin cookie session.
|
|
53
70
|
|
|
54
|
-
|
|
71
|
+
Methods: `listUsers()` lists all admin; `listMembers({search,limit,cursor,cursorDirection})` searches id/name/email
|
|
72
|
+
(literal LIKE), pages 1–100 and supports forward/backward opaque cursors bound to normalized search;
|
|
73
|
+
`setUserRole(id, "admin" | null)`, `inviteUser(email)`, `revokeInvite(id)`, `deleteUser(id)`.
|
|
74
|
+
Only identity fields reach outputs, never tokens/internal columns. Self-demotion/deletion is refused. Inviting an existing
|
|
75
|
+
member grants admin only after email verification; an unverified existing account is refused. Revocation deletes only an unverified admin invitation with no linked account. Deletion clears OTP
|
|
76
|
+
records first and delegates user/session/cache cleanup to Better Auth. Invitation email is an optional email-module step
|
|
77
|
+
after success; invitations still work when email is absent.
|
|
55
78
|
|
|
56
|
-
|
|
79
|
+
`d1BootstrapAdmin(db, {match: "email" | "github-login", value}, () => auth)` / `postgresBootstrapAdmin(sql, rule, () => auth)` return
|
|
80
|
+
`databaseHooks.user.create.after` (and user.update.after for a later email verification). Only the first matching signup wins (case-insensitive); email matching requires emailVerified=true (required for bootstrap).
|
|
81
|
+
D1 uses one conditional UPDATE; PostgreSQL uses a transaction advisory lock before UPDATE, avoiding concurrent winners.
|
|
82
|
+
A blocked promotion logs a warning. A GitHub rule requires a configured trusted GitHub provider/profile mapper.
|
|
83
|
+
The promotion runs after Better Auth creates the user, so the first sign-in response (and a cached session payload) can still show the previous role, while authorization re-reads the role on every request and `/admin/api/me` and later sessions show the admin role.
|
|
84
|
+
Helpers run no boot queries or DDL.
|
|
57
85
|
|
|
58
|
-
|
|
59
|
-
{ "error": "setup_incomplete", "message": "Self-managed Auth configuration errors: BETTER_AUTH_SECRET is not set; ..." }
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
with status `503` and `private, no-store`. The affected paths are `/admin` and `/admin/*`, `/api/auth` and `/api/auth/*`, `/oauth` and `/oauth/*`, `/.well-known/oauth*`, `/mcp` and `/mcp/*`. The message lists every failing check.
|
|
63
|
-
|
|
64
|
-
## Secrets and local values
|
|
65
|
-
|
|
66
|
-
```sh
|
|
67
|
-
wrangler secret put BETTER_AUTH_SECRET
|
|
68
|
-
wrangler secret put GITHUB_CLIENT_SECRET # self-managed only
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Non-secret vars go in `wrangler.jsonc` under `vars`. For local development put the same names in `.dev.vars`, which the minimal reference ignores in git (`.dev.vars*`). Never commit a secret.
|
|
72
|
-
|
|
73
|
-
## First owner
|
|
86
|
+
## Worker entry
|
|
74
87
|
|
|
75
|
-
|
|
88
|
+
Keep auth behind authLoader with a dynamic import of the app-owned auth module. Pass resolveCaller the request, loader, expected MCP audience and outgoing responseHeaders. Preserve returned Set-Cookie headers so sessions can slide. Share a lazy request-local resolver with auth/consent/Admin/MCP/REST; public anonymous routes can avoid auth. See [service composition](service-entry.md) for wiring.
|
|
76
89
|
|
|
77
|
-
|
|
90
|
+
Invalid presented bearer credentials fail rather than becoming anonymous. Fresh session authority comes from Better Auth's primary result; role, ban and revocation checks remain live. Custom cookiePrefix must match the loader; renamed session_token cookie names are refused.
|
|
78
91
|
|
|
79
|
-
|
|
92
|
+
## Build: keep Better Auth out of startup
|
|
80
93
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| `POST /admin/api/staff/invitations`, `DELETE /admin/api/staff/invitations/:id` | `owner` |
|
|
85
|
-
| `GET /admin/api/site-settings`, `PATCH /admin/api/site-settings` | `owner` |
|
|
86
|
-
| `GET /admin/api/members` | `editor` |
|
|
87
|
-
| `POST /admin/api/entries/:id/publish`, `POST /admin/api/entries/:id/unpublish`, `DELETE /admin/api/entries/:id` | `editor` |
|
|
94
|
+
Use the [Workers build instructions](../../../README.md#workers-build).
|
|
95
|
+
The metafile startup guard rejects static Better Auth imports; keep auth behind
|
|
96
|
+
`authLoader(() => import(...))`.
|
|
88
97
|
|
|
89
|
-
|
|
98
|
+
## Schema
|
|
90
99
|
|
|
91
|
-
|
|
100
|
+
`mantle generate <target> <out> <manifest>… --auth ./auth.ts` calls the module's `authOptions` with an empty env
|
|
101
|
+
(the tables depend on plugins and options, not env values; nothing is constructed) and appends Better Auth's tables to `schema.sql`. Nothing runs DDL at runtime; migrations
|
|
102
|
+
are yours (#1463).
|
|
103
|
+
|
|
104
|
+
For programmatic auth generation, import `generateFromSources` from
|
|
105
|
+
`@aotter/mantle/generate/auth`, not the bare `/generate` entry. Both use the
|
|
106
|
+
same generator; the selected entry preserves Better Auth's option types while
|
|
107
|
+
bare generation requires no optional auth peer in its public declarations.
|
|
108
|
+
Pass plain options rather than constructing Better Auth to obtain the schema.
|
|
109
|
+
The CLI's `--auth` selects this path automatically.
|
|
92
110
|
|
|
93
|
-
|
|
94
|
-
|---|---|
|
|
95
|
-
| `/admin`, `/admin/api/*` | Staff session; role gates per route |
|
|
96
|
-
| `/api/auth/*`, `/oauth/*`, `/.well-known/oauth*` | Auth-owned; public endpoints of the OAuth flow |
|
|
97
|
-
| `/mcp` | Same caller resolution as HTTP routes (bearer, same-origin cookie session, or anonymous); each tool's `requires` gates the call, and a call that needs identity answers `401` with a `WWW-Authenticate` challenge |
|
|
98
|
-
| `/mcp/staff` | Authenticated caller with a staff role |
|
|
99
|
-
| `/<locale>/<segment>/<slug>?preview=1` | Staff session (`401` without a session, `403` without a staff role) |
|
|
100
|
-
| Public Views, public HTTP Triggers, public pages, `.md`, `llms.txt`, sitemap | None, unless the manifest declares `requires` |
|
|
111
|
+
Native adapters enable Better Auth joins by default. Without secondaryStorage, fresh cookie authorization uses Better Auth's primary session/user result with disableCookieCache. Passing responseHeaders permits the same call to slide a due cookie. An explicit advanced.database.joins:false retains separate-read behavior. Authority reflects the primary result at that read; a later concurrent revocation applies on the next request.
|
|
101
112
|
|
|
102
|
-
|
|
113
|
+
### Explicit read-only cookie snapshots
|
|
103
114
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
canonical store, not a second identity authority. Auth prefixes keys with
|
|
108
|
-
`better-auth:<store-instance-id>:`. Preparing a new store gives it a distinct
|
|
109
|
-
identity, so reusing the same KV namespace after replacing D1 cannot resurrect
|
|
110
|
-
the previous store's cached sessions. Ordinary preparation of the same store
|
|
111
|
-
preserves its identity.
|
|
112
|
-
|
|
113
|
-
Custom low-level Auth composition must prepare the Mantle store before cached
|
|
114
|
-
Auth operations; do not construct cache keys or seed the identity yourself.
|
|
115
|
-
OTP verification remains in the primary database and rate limiting remains
|
|
116
|
-
isolate-local. Revocation/user-update cache invalidation still follows KV
|
|
117
|
-
propagation; the namespace change is isolation across stores, not a promise of
|
|
118
|
-
instant global invalidation. See the [Auth decision](../../adr/0014-auth-better-auth-and-multi-tenant-mcp.md).
|
|
119
|
-
|
|
120
|
-
## Better Auth configuration
|
|
121
|
-
|
|
122
|
-
`createAuth()` owns the Worker lifecycle, Admin metadata, sender integration,
|
|
123
|
-
bootstrap rules, roles and MCP invariants. Method-specific configuration stays
|
|
124
|
-
native to Better Auth under `options`, so provider updates and type inference do
|
|
125
|
-
not need a matching Mantle DSL update. There is deliberately no
|
|
126
|
-
`Partial<BetterAuthOptions>` deep merge.
|
|
115
|
+
Authority remains fresh by default. An application may pass `readOnly: true`
|
|
116
|
+
only for a positively classified read. Enable Better Auth's official cache in
|
|
117
|
+
app-owned auth options:
|
|
127
118
|
|
|
128
119
|
```ts
|
|
129
|
-
|
|
130
|
-
{
|
|
131
|
-
|
|
132
|
-
provider: "google",
|
|
133
|
-
options: { clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET, accessType: "offline" },
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
kind: "social",
|
|
137
|
-
provider: "apple",
|
|
138
|
-
options: async () => ({ clientId: env.APPLE_CLIENT_ID, clientSecret: await loadAppleSecret(env) }),
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
kind: "email-otp",
|
|
142
|
-
sender,
|
|
143
|
-
options: { otpLength: 8 },
|
|
144
|
-
},
|
|
145
|
-
]
|
|
120
|
+
session: {
|
|
121
|
+
cookieCache: { enabled: true, maxAge: 60, refreshCache: false, version: "read-60-v1" },
|
|
122
|
+
}
|
|
146
123
|
```
|
|
147
124
|
|
|
148
|
-
|
|
149
|
-
`BETTER_AUTH_SECRET`. Magic-link tokens remain `hashed` (high-entropy).
|
|
150
|
-
Explicit official overrides remain available, including `plain` and custom
|
|
151
|
-
hashing/encryption:
|
|
152
|
-
|
|
153
|
-
```ts
|
|
154
|
-
{ kind: "email-otp", sender, options: {
|
|
155
|
-
storeOTP: { encrypt: encryptOtp, decrypt: decryptOtp },
|
|
156
|
-
} }
|
|
157
|
-
{ kind: "magic-link", sender, options: {
|
|
158
|
-
storeToken: { type: "custom-hasher", hash: hashMagicToken },
|
|
159
|
-
} }
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
For complete callback ownership, register an official plugin instance directly.
|
|
163
|
-
Raw plugins do not add a button to `Auth.methods`; the application owns that UI.
|
|
164
|
-
Duplicate plugin ids fail at construction rather than silently replacing one:
|
|
165
|
-
|
|
166
|
-
```ts
|
|
167
|
-
createAuth({
|
|
168
|
-
database: env.DB,
|
|
169
|
-
baseURL: env.PUBLIC_ORIGIN,
|
|
170
|
-
secret: env.BETTER_AUTH_SECRET,
|
|
171
|
-
methods: [],
|
|
172
|
-
plugins: [emailOTP({ sendVerificationOTP, storeOTP: "encrypted" })],
|
|
173
|
-
});
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
When several first-party apps share one parent domain that the same party controls, configure shared cookies explicitly. `cookiePrefix` is required whenever more than one Better Auth app writes cookies under that domain; `trustedOrigins` is the auth-flow trust list, not a CORS policy.
|
|
177
|
-
|
|
178
|
-
```ts
|
|
179
|
-
const auth = createAuth({
|
|
180
|
-
database: env.DB,
|
|
181
|
-
baseURL: "https://platform.example.com",
|
|
182
|
-
secret: env.BETTER_AUTH_SECRET,
|
|
183
|
-
methods,
|
|
184
|
-
trustedOrigins: ["https://example.com", "https://www.example.com"],
|
|
185
|
-
cookiePrefix: "example-platform",
|
|
186
|
-
crossSubDomainCookies: { enabled: true, domain: "example.com" },
|
|
187
|
-
});
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Shared cookies do not cross registrable domains. A browser never sends an `example.com` cookie to `customer.com`. For a customer-owned domain, use an OAuth/OIDC broker flow: the customer site redirects to the identity provider's authorize endpoint, receives the callback, verifies the response and creates its own local session. The broker returns identity; the customer site remains the authority for its members and grants.
|
|
191
|
-
|
|
192
|
-
### Account linking across providers
|
|
193
|
-
|
|
194
|
-
One person signing in with Google, then with GitHub, may land on one user row
|
|
195
|
-
or be refused — Better Auth decides this, and `createAuth()` does not override
|
|
196
|
-
it. Left unconfigured, Better Auth's own defaults apply: implicit linking is
|
|
197
|
-
on, so a social sign-in whose provider reports a verified email attaches to the
|
|
198
|
-
existing row carrying that email. It never creates a second row for the same
|
|
199
|
-
address; when linking is not permitted the sign-in fails with
|
|
200
|
-
`account not linked`.
|
|
201
|
-
|
|
202
|
-
Two defaults are worth knowing before you change anything. `requireLocalEmailVerified`
|
|
203
|
-
is on, so linking is refused while the *local* row is still unverified — this is
|
|
204
|
-
what stops someone pre-registering an unverified row at your user's address and
|
|
205
|
-
having that user's Google identity attach to it. It is also why a staff invitation
|
|
206
|
-
(`inviteUser` writes `emailVerified: 0`) cannot be claimed by a social sign-in
|
|
207
|
-
until the invitee verifies by email once. Separately, `trustedProviders` is
|
|
208
|
-
empty, so every provider must supply `email_verified` to link at all.
|
|
209
|
-
|
|
210
|
-
Pass `accountLinking` to scope this. It is forwarded verbatim:
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
const auth = createAuth({
|
|
214
|
-
database: env.DB,
|
|
215
|
-
baseURL: env.PUBLIC_ORIGIN,
|
|
216
|
-
secret: env.BETTER_AUTH_SECRET,
|
|
217
|
-
methods,
|
|
218
|
-
accountLinking: {
|
|
219
|
-
// Accept these providers' word without an `email_verified` claim.
|
|
220
|
-
trustedProviders: ["google", "github"],
|
|
221
|
-
},
|
|
222
|
-
});
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
Listing a provider in `trustedProviders` asserts that it verifies the addresses
|
|
226
|
-
it returns; a provider that does not turns the list into an account-takeover
|
|
227
|
-
path. To go the other way and keep every identity separate, set
|
|
228
|
-
`disableImplicitLinking: true` (users may still link deliberately via
|
|
229
|
-
`linkSocial()` while signed in) or `enabled: false` to refuse linking outright.
|
|
230
|
-
|
|
231
|
-
## Self-hosted and hosted
|
|
232
|
-
|
|
233
|
-
A free self-hosted site runs every method `createAuth()` exposes: Better Auth social providers, email OTP, magic link, and parent-domain SSO. The owner supplies provider credentials, email sending and cookie policy. Hosted auth is an operations convenience: the platform holds provider and email configuration and registers the site as a PKCE client, while the site still owns grants, members, content and `ctx.user`/`ctx.staff` mapping. Neither mode changes the runtime's authorization vocabulary.
|
|
234
|
-
|
|
235
|
-
## Replacing Auth construction
|
|
236
|
-
|
|
237
|
-
```ts
|
|
238
|
-
createMantleWorker({
|
|
239
|
-
plan,
|
|
240
|
-
auth: (env) => createAuth({ /* curated, site-specific methods */ }),
|
|
241
|
-
});
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
With `auth` set, `MANTLE_AUTH_MODE` and the mode variables are not read. Core still owns the Auth routes: the factory's `basePath` joins the reserved paths, the MCP resource defaults to `auth.mcpResource ?? <PUBLIC_ORIGIN>/mcp`, and a rejected `auth.ready` evicts the isolate's assembly. Keep the D1 `DB` binding; Better Auth tables live there.
|
|
245
|
-
|
|
246
|
-
## OAuth resource primitives
|
|
247
|
-
|
|
248
|
-
When one Mantle site is an OAuth client of another, request a stable RFC 8707
|
|
249
|
-
resource and use standard `offline_access` when refresh is needed:
|
|
250
|
-
|
|
251
|
-
```ts
|
|
252
|
-
const clientAuth = createAuth({
|
|
253
|
-
// database, baseURL, secret, other methods...
|
|
254
|
-
methods: [{
|
|
255
|
-
kind: "oauth",
|
|
256
|
-
options: {
|
|
257
|
-
providerId: "mantle-platform",
|
|
258
|
-
clientId: env.PLATFORM_CLIENT_ID,
|
|
259
|
-
discoveryUrl: "https://platform.example.com/api/auth/.well-known/openid-configuration",
|
|
260
|
-
scopes: ["openid", "offline_access", "accounts:read"],
|
|
261
|
-
authorizationUrlParams: { resource: "https://api.example.com" },
|
|
262
|
-
tokenUrlParams: { resource: "https://api.example.com" },
|
|
263
|
-
refreshTokenParams: { resource: "https://api.example.com" },
|
|
264
|
-
},
|
|
265
|
-
}],
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
const { accessToken, accessTokenExpiresAt, scopes } =
|
|
269
|
-
await clientAuth.getProviderAccessToken(request, "mantle-platform");
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
The server-side getter is bound to the current local session request and never
|
|
273
|
-
returns a refresh token or account row. On the provider:
|
|
274
|
-
|
|
275
|
-
```ts
|
|
276
|
-
const providerAuth = createAuth({
|
|
277
|
-
// database, baseURL, secret, methods...
|
|
278
|
-
oauthProvider: {
|
|
279
|
-
loginPage: "/sign-in",
|
|
280
|
-
consentPage: "/consent",
|
|
281
|
-
scopes: ["openid", "offline_access", "accounts:read"],
|
|
282
|
-
resources: ["https://api.example.com"],
|
|
283
|
-
},
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
const verification = await providerAuth.verifyOAuthAccessToken(request, {
|
|
287
|
-
audience: "https://api.example.com",
|
|
288
|
-
scopes: ["accounts:read"],
|
|
289
|
-
});
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
The verifier accepts JWT access tokens only and checks the configured issuer,
|
|
293
|
-
JWKS/signature, audience, time claims, required scopes, and—when passed the
|
|
294
|
-
request—DPoP proof binding with database-backed replay protection. It returns
|
|
295
|
-
only `userId`, `clientId`, `credentialId`, and scopes. Opaque tokens are
|
|
296
|
-
rejected; there is no introspection fallback.
|
|
297
|
-
|
|
298
|
-
## Enterprise-Managed Authorization
|
|
125
|
+
A valid signed cache hit avoids Caller DB reads. Missing, tampered, expired or version-mismatched snapshots fall back to primary authority. The SDK accepts this opt-in only without secondaryStorage, with explicit maxAge from 1 to 60 seconds and refreshCache:false. KV gains no cache-authority guarantee; bearer grant checks remain live.
|
|
299
126
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
assertion authorization grant) and presents it to the server's token endpoint
|
|
304
|
-
as `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`; no consent page
|
|
305
|
-
is shown. Everything after that is an ordinary access token.
|
|
127
|
+
Revocation, bans and role changes reach cached reads within `maxAge` (at most
|
|
128
|
+
60 seconds). Sign-out clears both the session-token and cache cookies. Bearer
|
|
129
|
+
tokens still use their live grant query on every request.
|
|
306
130
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
`oauthProvider.extensions`, appended after Core's own claims extension:
|
|
131
|
+
Generated REST, MCP and web boundaries pass `{ readOnly }` as the third argument
|
|
132
|
+
to the application's caller callback. Forward it to `resolveCaller`:
|
|
310
133
|
|
|
311
134
|
```ts
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const auth = createAuth({
|
|
315
|
-
// database, baseURL, secret, methods...
|
|
316
|
-
oauthProvider: {
|
|
317
|
-
loginPage: "/admin/sign-in",
|
|
318
|
-
consentPage: "/oauth/consent",
|
|
319
|
-
scopes: ["mcp", "offline_access"],
|
|
320
|
-
mcpResource: env.PUBLIC_ORIGIN + "/mcp",
|
|
321
|
-
extensions: [
|
|
322
|
-
identityAssertionAuthorizationGrant({
|
|
323
|
-
issuer: env.ENTERPRISE_IDP_ISSUER,
|
|
324
|
-
jwksUrl: env.ENTERPRISE_IDP_JWKS_URL,
|
|
325
|
-
authorizationServer: env.PUBLIC_ORIGIN,
|
|
326
|
-
resource: env.PUBLIC_ORIGIN + "/mcp",
|
|
327
|
-
scopes: ["mcp"],
|
|
328
|
-
fetchJwks: (input, init) => fetch(input, { ...init, redirect: "manual" }),
|
|
329
|
-
}),
|
|
330
|
-
],
|
|
331
|
-
},
|
|
332
|
-
});
|
|
135
|
+
const caller = (request: Request, _env: Env, authority = { readOnly: false }) =>
|
|
136
|
+
resolveCaller(request, loader, { ...authority, responseHeaders });
|
|
333
137
|
```
|
|
334
138
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
139
|
+
REST permits cached authority only on GET/HEAD Views; HEAD returns no body.
|
|
140
|
+
Web permits it only on GET/HEAD page and index reads. For Cookie requests without
|
|
141
|
+
an Authorization header, MCP classifies parsed
|
|
142
|
+
`initialize`, `tools/list`, `resources/list`, `resources/read`, generated View
|
|
143
|
+
tools and generated native Admin media reads before resolving Caller. Generated
|
|
144
|
+
metadata identifies Views versus Procedures; names and `readOnlyHint` annotations
|
|
145
|
+
never make a Procedure eligible. A batch uses cached authority only if every
|
|
146
|
+
message is a known read. Unknown/malformed methods or tools, mutations,
|
|
147
|
+
publishing transitions and Admin writes use fresh authority. Dynamic admin tool
|
|
148
|
+
implementations still load only after the admin gate; their read catalogue is
|
|
149
|
+
generated separately. App-composed tool tables default to fresh authority, including tools with a
|
|
150
|
+
`page` callback or read annotations, unless the
|
|
151
|
+
app supplies equivalent trusted read metadata to `mcp`'s fourth argument.
|
|
152
|
+
|
|
153
|
+
Auth/admin endpoints (including GET) and consent keep the default fresh resolver.
|
|
154
|
+
Do not infer authority from HTTP method alone or pass `readOnly: true` for a whole
|
|
155
|
+
surface. A caller callback that ignores the third argument retains the default
|
|
156
|
+
primary checks. In request-local memoization, keep cached reads separate from
|
|
157
|
+
fresh authority so a prior read cannot supply authority for a write.
|
|
158
|
+
|
|
159
|
+
**Bump `cookieCache.version` when enabling this policy or changing its TTL or
|
|
160
|
+
semantics.** A cache minted under an earlier 300-second setting retains its old
|
|
161
|
+
signed expiry; changing `maxAge` alone does not shorten it. Better Auth's version
|
|
162
|
+
check invalidates that snapshot. The SDK cannot infer prior deployment config.
|
|
163
|
+
Sign-out clears the session-token and cache cookies; pass SDK Set-Cookie headers
|
|
164
|
+
through to the browser. A copied pre-sign-out cookie can remain usable for
|
|
165
|
+
read-only snapshots until TTL, while fresh authority rejects revoked sessions.
|
|
166
|
+
|
|
167
|
+
With secondaryStorage, disableCookieCache does not bypass KV. Caller keeps a primary authority check before any second SDK call refreshes/forwards cookies. Stale snapshots cannot revive deleted/expired primary sessions or retain obsolete role/ban state.
|
|
168
|
+
|
|
169
|
+
`authRoutes` may be imported statically: its consent/provider integration loads
|
|
170
|
+
only for a matching consent request, after Caller/origin/body guards. Optional
|
|
171
|
+
Better Auth adapter code stays outside the Worker startup graph.
|
|
172
|
+
|
|
173
|
+
`@aotter/mantle/auth/admin` exports `guardAuthAdminRequest(request, resolveFreshCaller, basePath?)` for selected auth transports. It resolves authority only for raw/encoded admin paths, requires an admin cookie session and checks mutation origin. Both the app-owned template and OAuth routes use this same guard.
|
|
174
|
+
|
|
175
|
+
Bare `ctx.admin` admits any verified admin layer classified by Better Auth's
|
|
176
|
+
`adminRoles` / `adminUserIds`. Role lists in `requires` are refused; decide
|
|
177
|
+
per-target permissions in `authorize(target, caller)`, using `hasAdminRole`
|
|
178
|
+
when appropriate. Verified role names match `^[a-z][a-zA-Z0-9_-]{0,62}$`.
|
|
179
|
+
Member role names alone grant no privilege. See
|
|
180
|
+
[authorization requirements](../reference/authorization.md).
|
|
181
|
+
|
|
182
|
+
The generated auth DDL adds a native `(createdAt, id)` member-paging index (respecting schema field/table names). Unfiltered forward/backward paging orders native timestamps through that index; PostgreSQL cursors retain all six fractional digits and bind as text before timestamp casting. Member search (`%term%` over id/name/email, casting UUID ids to text only in the search expression) is a full scan; the index does not accelerate it.
|
|
183
|
+
|
|
184
|
+
## Several Better Auth instances in one app
|
|
185
|
+
|
|
186
|
+
The default is one instance with `admin` and `user` roles. An app that keeps two populations apart (for example
|
|
187
|
+
staff and customers) can run one Better Auth instance per population in the same Worker; every Mantle primitive
|
|
188
|
+
takes the instance it works on:
|
|
189
|
+
|
|
190
|
+
- Give each instance its own `basePath`, `advanced.cookiePrefix`, `secret` and core table names (`user.modelName`,
|
|
191
|
+
`session.modelName`, `account.modelName`, `verification.modelName`). Keep each instance's options in its own
|
|
192
|
+
module and pass one `--auth` per module to `mantle generate`; the tables are merged into one migration, and two
|
|
193
|
+
instances that define the same table differently are refused. The native adapter follows each instance's mapping.
|
|
194
|
+
- Better Auth plugin factories share their schema object, so differing plugin field/model mappings across instances
|
|
195
|
+
in one Worker override each other. Use one of two supported ways:
|
|
196
|
+
1. Separate the instances by core table names only and leave every plugin mapping at its default.
|
|
197
|
+
2. When plugin tables must differ (`jwt`'s `jwks`, `@better-auth/oauth-provider`'s `oauth*` tables), map them
|
|
198
|
+
explicitly in every instance, the default one included, and give each instance its own copy of the plugin's
|
|
199
|
+
schema right after constructing it:
|
|
200
|
+
`const keys = jwt({ schema: { jwks: { modelName: "staff_jwks" } } }); const plugin = { ...keys, schema: structuredClone(keys.schema) };`
|
|
201
|
+
|
|
202
|
+
`mantle generate` refuses anything else when two or more `--auth` instances use the same plugin (`jwt`, `admin`,
|
|
203
|
+
`oauth-provider`, `two-factor`, `phone-number`, `anonymous`, `siwe`, `device-authorization`) and a mapping exists
|
|
204
|
+
while an instance keeps the shared schema object, or two instances resolve a mapping to the same name (generate
|
|
205
|
+
cannot tell an identical mapping from an inherited one, so mapped names must differ between instances). Build
|
|
206
|
+
every instance's plugins in the same order in the Worker as in `--auth`; an instance built earlier than a mapped
|
|
207
|
+
one can still inherit its mapping, which generate cannot see.
|
|
208
|
+
- One `authLoader(load, cookiePrefix)` per instance; call `resolveCaller(request, loader)` with the instance the
|
|
209
|
+
route belongs to, and mount `authRoutes(loader, ...)` per instance (it guards that instance's `/admin/*`). The
|
|
210
|
+
loader takes `cookiePrefix` separately on purpose: it checks the cookie before loading Better Auth, so anonymous
|
|
211
|
+
requests never pay for it. Read both from one constant.
|
|
212
|
+
- Admin signs in with the instance passed to `adminShell(request, assets, { authBasePath })`; its identity
|
|
213
|
+
directory is the instance you pass to `d1Identity` / `postgresIdentity`.
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Mantle on ChatGPT Sites now has its own integration guide."
|
|
3
|
-
---
|
|
4
1
|
# Mantle on ChatGPT Sites
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
[Mantle on ChatGPT Sites](../chatgpt-sites/index.md) for installation, supported
|
|
8
|
-
capabilities, and your first published article. This page preserves earlier
|
|
9
|
-
links to the host checklist.
|
|
3
|
+
> **Verification status:** this guide is experimental and has not yet been exercised end to end on a real ChatGPT Site.
|
|
10
4
|
|
|
11
|
-
|
|
5
|
+
Treat the Site as an existing TypeScript host. Import selected generated Views/Procedures, pass the host's native handle and verified Caller, and compose only the surfaces it supports. Use [partial integration](../../skills/integrate/SKILL.md); Mantle supplies no Sites-specific identity or deployment adapter.
|
|
12
6
|
|
|
13
|
-
|
|
7
|
+
Choose postgres or sqlite according to the host's actual SQL executor. Custom handles are app-owned; a structural match alone does not guarantee driver semantics. [Database boundaries](../concepts/runtime-and-adapters.md) defines the supported drivers.
|
|
14
8
|
|
|
15
|
-
|
|
9
|
+
The runtime never creates, alters or checks tables. Apply initial `schema.sql` and selected auth/site DDL only to an empty database. For later changes the agent diffs regenerated SQL, writes and rehearses a host-native migration on a copy, and applies it before publishing new code. Destructive/type changes require explicit data migration; Mantle drops nothing.
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## Remote MCP is a separate gate
|
|
20
|
-
|
|
21
|
-
See [MCP support and authentication](../chatgpt-sites/host-reference.md#remote-mcp-is-a-separate-gate).
|
|
22
|
-
|
|
23
|
-
## Capability boundaries
|
|
24
|
-
|
|
25
|
-
See [host capability boundaries](../chatgpt-sites/host-reference.md#capability-boundaries).
|
|
26
|
-
|
|
27
|
-
## Source
|
|
28
|
-
|
|
29
|
-
See [integration sources](../chatgpt-sites/host-reference.md#source).
|
|
11
|
+
Use the Site's native publication workflow. Configure trusted origin and verified identity there; never construct a system/admin Caller from browser input. Serving Admin also requires its app-built assets and shell security contract. Scheduling and storage provisioning belong to the host.
|