Skip to content

[v3 RC3 DX] Centralize client runtime configuration and normalize all plugin interfaces #202

Description

@olliethedev

Problem Statement

BTST currently makes a consumer repeat framework/runtime configuration at several independent seams. The backend stack owns an API base path, every client plugin factory independently receives API URL/path, site URL/path, React Query client, and optional SSR headers, and StackProvider receives overlapping API and site-path configuration again. The generated applications therefore have multiple sources of truth for /api/data, /pages, the current origin, the query client, and request headers.

This is more than boilerplate. SSR loaders and metadata use the values captured by each client plugin factory, while browser resource hooks use StackProvider. A plugin-specific API override can consequently send SSR to one origin and client-side refetches to another. The existing top-level client stack config also advertises baseURL and basePath, but baseURL is not consumed and basePath is only passed into an introspection context; it does not provide the inheritance a consumer would reasonably expect.

The surrounding plugin interface has accumulated additional historical inconsistencies:

  • the backend constructor is named differently from the client constructor;
  • plugin registration keys, intrinsic plugin names, override keys, package slugs, and generated examples alternate between camelCase and kebab-case;
  • provider override types are manually recreated even though the registered client plugins already carry the necessary types;
  • backend factories variously accept positional hooks, flat option objects, nested hooks, required config objects, or non-factory constants in extension fixtures;
  • lifecycle phases use inconsistent names such as onPostCreated, onAfterCreate, and onAfterFormCreated;
  • server execution surfaces and lower-level escape hatches do not use one uniformly obvious trust vocabulary;
  • one-sided/composed plugins such as OpenAPI, Route Docs, and UI Builder are legitimate, but the integration story often implies that every feature always has matching backend and client halves.

This work is the final core-wide breaking developer-experience change in the coordinated v3 RC3 cohort. The authorization delivery map in #180 and its contraction ticket #193 are complete. Their landed authorization contract is now a frozen input: this issue audits and normalizes the settled post-#193 interfaces without reopening the authorization design or restoring transitional RC surfaces.

Solution

Provide one coherent stack and plugin developer experience in which applications configure shared runtime values once, plugins declare only what differs, and the same resolved client runtime drives SSR loaders, metadata, browser resource hooks, and provider context.

The client stack owns top-level api, site, queryClient, and request-header defaults. Every client plugin inherits those values. A plugin may explicitly replace its API or site endpoint when it talks to another compatible service, but the override is resolved once and used identically by server and browser consumers. Changing origin is treated as a transport boundary: application cookies and authorization headers are not silently forwarded to the new origin.

Client plugin factories become server-import-safe definitions whose plugin-specific options can be combined with stack runtime later. The browser creates its own resolved client stack instance; SSR/SSG creates a request-specific instance with request headers. StackProvider consumes the resolved browser instance, or a provider projection produced by it, so applications do not repeat API/site configuration. No server-created object or request headers are serialized into the browser.

Normalize all first-party plugin identifiers, factory shapes, lifecycle conventions, override inference, generated examples, and final server trust vocabulary. Preserve real asymmetries—required AI/storage/CMS configuration and intentional backend-only/client-only plugins—rather than forcing fake symmetry.

User Stories

  1. As an application developer, I want to configure my API origin and mount path once, so that every installed plugin reaches the same backend without repeated fields.
  2. As an application developer, I want to configure my site origin and pages mount path once, so that routes, metadata, sitemap entries, and links cannot drift.
  3. As an SSR developer, I want request headers supplied once per client stack instance, so that all plugin loaders consistently forward authentication.
  4. As a browser developer, I want browser hooks to use the same resolved endpoint as SSR loaders, so that hydration and refetch cannot silently switch servers.
  5. As a React Query user, I want one query client shared by the complete client stack, so that loaders, hooks, hydration, mutations, and invalidation use one cache.
  6. As a plugin consumer, I want plugin factories to contain only plugin-specific choices, so that adding CMS after Blog does not repeat six framework fields.
  7. As a developer using a separate compatible service, I want to override one plugin's API endpoint explicitly, so that most plugins can use the application backend while one uses another deployment.
  8. As a security-conscious developer, I want cross-origin plugin overrides not to inherit cookies or authorization headers automatically, so that credentials are not leaked to another service.
  9. As a developer configuring a separate service, I want clear errors when required endpoint or authentication information is missing, so that SSR and CSR do not fail differently.
  10. As a Next.js developer, I want plugin definitions to remain server-import-safe, so that creating the SSR client stack never pulls client-only hooks or server secrets across the wrong module seam.
  11. As a React Router developer, I want the same client stack interface and inheritance rules as Next.js, so that framework choice does not change plugin configuration.
  12. As a TanStack Start developer, I want the same client stack interface and inheritance rules as the other frameworks, so that generated integrations stay equivalent.
  13. As a consumer, I want backend and client constructors to use an obvious symmetric naming convention, so that I do not have to remember which side is a noun and which side is a factory.
  14. As a consumer, I want compound plugin identifiers to follow one convention everywhere, so that aiChat, ai-chat, and quoted override keys do not refer to the same plugin differently.
  15. As a TypeScript user, I want provider override types inferred from registered plugins, so that adding or removing a plugin does not require maintaining a second type map.
  16. As a TypeScript user, I want an invalid or duplicate plugin registration identifier rejected at compile time where possible and at startup otherwise, so that namespaces and overrides cannot collide.
  17. As a plugin author, I want one backend factory convention, so that optional hooks and required domain configuration have predictable placement.
  18. As a plugin author, I want one client definition convention, so that stack runtime and plugin-specific options remain separate.
  19. As a hook author, I want lifecycle phase names to follow one documented vocabulary, so that before, after, and error behavior are easy to discover across plugins.
  20. As a maintainer, I want domain-specific lifecycle events preserved where they carry meaning, so that normalization does not invent fake CRUD symmetry.
  21. As a server developer, I want authorized, trusted, and lower-level calls to use distinct names, so that I can identify their trust contract at the call site.
  22. As a job or seed author, I want trusted operation calls to retain validation and lifecycle behavior, so that "internal" does not mean raw database mutation.
  23. As an advanced server developer, I want lower-level getter/mutation escape hatches to remain explicit, so that SSG, migrations, and infrastructure code can deliberately bypass composition without masquerading as authorized calls.
  24. As a UI Builder consumer, I want its client-only composition over CMS documented as intentional, so that I do not look for a nonexistent UI Builder backend plugin.
  25. As an OpenAPI consumer, I want its backend-only status documented as intentional, so that I do not register a meaningless client half.
  26. As a Route Docs consumer, I want its client-only runtime needs inherited correctly, so that a simpler plugin is not forced to accept irrelevant API fields.
  27. As a third-party plugin author, I want a fixture proving the normalized interfaces outside BTST's built-in plugins, so that the design is not accidentally coupled to internal implementation.
  28. As a CLI/codegen user, I want newly generated applications to demonstrate only the canonical interface, so that old and new DX do not coexist in copied code.
  29. As a migrating consumer, I want a complete before/after guide covering constructor, plugin ID, factory, provider, and lifecycle changes, so that the breaking migration is mechanical.
  30. As a maintainer, I want every built-in plugin checked against an explicit audit matrix, so that the cleanup cannot be declared complete after migrating only Blog and CMS.

Implementation Decisions

Sequencing and release boundary

Canonical stack constructors

  • Use symmetric canonical constructor names for the two sides: createBackendStack and createClientStack.
  • Documentation, generated applications, examples, type names, and diagnostics use the canonical terminology consistently.
  • Do not maintain two equally documented constructor stories. Any compatibility aliases required by release policy are deprecated, tested as forwarding aliases, omitted from new examples, and assigned a removal release.
  • StackProvider remains the React runtime provider but receives a resolved client stack instance or a stable provider projection from that instance instead of separately receiving duplicate API/site wiring.

Client runtime ownership and inheritance

  • The client stack's public interface owns four shared concerns: API transport, site location, React Query client, and optional SSR request headers.
  • API and site locations are distinct named objects. An unqualified top-level baseURL or basePath is not sufficient because data requests and page/SEO URLs are separate concepts.
  • The query client is stack-wide. Built-in plugins cannot silently substitute another query client; a genuinely isolated cache would require an explicit future design.
  • Request headers belong to the API transport and are resolved per stack instance. Server request headers are never stored in a module-global definition or serialized into the browser.
  • Client plugin factories return server-import-safe definitions containing plugin-specific options. The stack resolves each definition against the runtime after registration.
  • SSR loaders, metadata, sitemap generation, SSG helpers, browser resource hooks, mutations, and provider context consume the same resolved values for a plugin.
  • The resolved client stack exposes only the browser-safe projection needed by StackProvider. Server-only request data remains inaccessible from the browser projection.
  • Remove or repurpose stale client stack fields only when their behavior is real and covered. No public option may be declared while being ignored by the implementation.

Per-plugin endpoint overrides

  • A plugin with no endpoint override inherits the complete top-level API and site locations.
  • A same-origin path-only override may inherit the top-level origin and request headers.
  • Supplying a different API origin establishes a new transport. The plugin override replaces the endpoint as a coherent unit and does not inherit cookies, authorization headers, or other sensitive request headers implicitly.
  • Cross-origin credentials must be supplied explicitly through a browser-safe mechanism. Server secrets remain behind the backend or a server-side proxy.
  • A separate API endpoint is supported only when it implements that plugin's BTST HTTP contract. This feature does not adapt CMS, Media, or other plugins to unrelated third-party protocols.
  • A plugin may override its site location independently when its rendered/public pages genuinely live elsewhere.
  • Resolution and header behavior are deterministic and identical across Next.js, React Router, and TanStack Start.

Plugin identifiers and registration

  • Canonical programmatic built-in identifiers use camelCase: aiChat, blog, cms, comments, formBuilder, kanban, media, openApi, routeDocs, and uiBuilder.
  • Package export paths and URL slugs may remain kebab-case; they are transport/package slugs, not programmatic registration identifiers.
  • Registration namespaces, intrinsic plugin IDs, provider override keys, inferred types, resource declarations, diagnostics, and generated examples agree on the resolved programmatic identifier.
  • Components and resource hooks do not hard-code a second divergent identifier. They consume the identifier bound by plugin registration/definition.
  • Registration must reject a map key that conflicts with the plugin's resolved ID, duplicate IDs, and backend/client key drift in generated full-stack fixtures.
  • Preserve the ability of third-party plugins to declare their own stable ID. Multiple-instance/alias support must be explicitly tested if retained; do not preserve accidental alias behavior without defining how overrides, routes, and namespaces bind to it.

Provider overrides and inference

  • The registered client plugin definitions are the source of the provider override type.
  • Consumers do not manually import every override type and rebuild a parallel PluginOverrides map.
  • The client stack result carries enough type information for StackProvider to infer valid plugin keys and each plugin's override shape.
  • Framework services such as router, API transport, auth, notifications, and i18n remain stack/provider services rather than being copied into plugin overrides.
  • Plugin overrides remain browser-runtime customization: render slots, localization, upload helpers, component registries, and other genuinely plugin-specific values.
  • Invalid override keys and invalid values fail compile-time fixtures. Plugins with no overrides do not require empty blocks.

Backend plugin factory convention

  • Every first-party backend plugin is exposed as a factory, including zero-config examples and extension fixtures.
  • Every backend factory accepts one options object. If every field is optional, the argument defaults to an empty object and the zero-argument call remains valid.
  • Lifecycle callbacks live under a hooks property. Domain configuration remains alongside it: AI model/tools/mode for AI Chat, content types for CMS, storage and tenant resolution for Media, behavior flags/user resolution for Comments, and OpenAPI presentation options for OpenAPI.
  • Required domain dependencies remain required. Normalization must not make AI models, storage adapters, or other real requirements optional merely for visual symmetry.
  • Positional hook arguments are removed from Blog and Kanban, and flat lifecycle callbacks are removed from mixed option objects such as Comments.
  • Client plugin definitions similarly accept only plugin-specific options. Common API/site/query/header fields are supplied exclusively by the client stack runtime.

Lifecycle vocabulary

  • Establish and document one lifecycle naming grammar for backend operation hooks and one for client loader hooks.
  • Backend operation phases use discoverable before, after, and error prefixes with entity/action terminology. Client loading phases use the same phase ordering vocabulary while retaining the explicit SSR/CSR loader context.
  • Migrate every first-party lifecycle type, implementation, generated example, test, and documentation reference to the chosen grammar.
  • Preserve meaningful domain events such as chat completion, submission receipt, moderation transitions, and upload finalization. Do not rename distinct domain events into misleading CRUD names merely to make a table uniform.
  • Hook ordering and authorization semantics landed by [v3 auth] Ship one-rule full-stack authorization #180 remain unchanged. This ticket changes the interface vocabulary, not when security checks execute.
  • Publish a complete old-to-new hook migration table covering every renamed hook.

Server trust surfaces

  • The post-[v3 auth] Remove RC authorization paths and publish the v3 contract #193 composed stack has one obvious authorized request surface and one obvious trusted operation surface. A top-level property named api must not continue to mean a lower-level bypass while a nested property named api means authorized execution.
  • Preserve the [v3 auth] Ship one-rule full-stack authorization #180 distinction between request-authorized operations and trusted internal operations. Trusted internal calls continue to run validation, domain behavior, and lifecycle hooks.
  • Lower-level getter/mutation/adapter access remains explicitly named and documented as bypassing authorization and lifecycle composition. Prefer explicit server-only plugin imports or an unmistakable raw namespace; do not call that surface the normal application API.
  • This ticket audits the result of [v3 auth] Remove RC authorization paths and publish the v3 contract #193 and removes residual naming/documentation inconsistencies. It must not reintroduce ambiguous direct server calls for compatibility.

All-plugin audit matrix

The implementation PR must include and complete an audit matrix for every row below. Intentional one-sided plugins are valid and must not receive fake counterparts.

Plugin Supported halves Configuration that must remain expressible Known inconsistency to remove or verify
AI Chat Backend and client Across backend, client, and page-context surfaces: model, access/mode, server tools, page-tool enablement/schemas, per-page client handlers, hooks, SEO, page components ai-chat/aiChat ID drift; shared client runtime repetition; public/authenticated mode parity
Blog Backend and client Hooks, SEO, page components Positional backend hooks; repeated client runtime; historical after-hook naming
CMS Backend and client Content-type definitions, pagination limits, hooks, page components Repeated client runtime; verify post-#180 raw/authorized/trusted surfaces
Comments Backend and client Posting/editing flags, moderation/user resolution, hooks Flat mixed options and lifecycle callbacks; repeated client runtime
Form Builder Backend and client Hooks, page components form-builder/formBuilder drift; repeated client runtime; hook naming
Kanban Backend and client Hooks, SEO, user resolution/search, page components Positional backend hooks; repeated client runtime; lifecycle naming
Media Backend and client Storage adapter, tenant resolver, upload behavior, hooks, page components Shared runtime duplicated between factory/provider; ensure storage transport remains server-side; hook naming
OpenAPI Backend only Schema/reference options open-api/openApi drift; document intentional lack of client plugin
Route Docs Client only Title, description, route/site location route-docs/routeDocs drift; inherits only relevant site/query runtime; document intentional lack of backend plugin
UI Builder Client only over CMS Component registry, hooks, page components ui-builder/uiBuilder drift; duplicated registry wiring; document CMS as persistence/enforcement backend
Generated extension fixture Consumer-defined backend and client where applicable Minimal third-party options and overrides Must prove the public authoring interface without relying on internal plugin types or constant plugin values

Shared plugin infrastructure modules are audited as infrastructure, not counted as additional user-facing plugins.

Generated integrations, documentation, and migration

  • Update all generated Next.js, React Router, and TanStack integrations to use one shared client runtime and inferred provider overrides.
  • Update manual installation documentation, README examples, plugin-specific docs, plugin-authoring docs, API reference, registry output, playground, and codegen templates in the same migration.
  • Generated projects must not retain quoted kebab-case programmatic IDs, duplicated API/site constants, manually assembled override maps, or old factory forms.
  • Provide before/after migration guidance for constructors, client runtime placement, provider wiring, plugin IDs, factory arguments, hooks, and server trust surfaces.
  • Add a repository-level check that prevents deprecated configuration fields, old programmatic IDs, and removed positional factory forms from returning in maintained examples.

Acceptance Criteria

  • [v3 auth] Ship one-rule full-stack authorization #180 and every delivery-map issue through [v3 auth] Remove RC authorization paths and publish the v3 contract #193 are closed before implementation begins.
  • [v3 RC3 DX] Centralize client runtime configuration and normalize all plugin interfaces #202 lands as part of the coordinated v3 RC3 core cohort before Better Auth UI migration work begins.
  • One client stack runtime supplies API, site, query client, and SSR headers to every applicable built-in client plugin.
  • SSR loaders/metadata and browser resource hooks resolve the same effective endpoint for every plugin.
  • StackProvider no longer requires applications to repeat API/site configuration already present in the resolved client stack.
  • A same-origin plugin path override works consistently in SSR and CSR.
  • A cross-origin plugin endpoint override works consistently in SSR and CSR and does not inherit sensitive headers implicitly.
  • Client stack configuration contains no accepted-but-ignored public fields.
  • Provider override keys and value types are inferred from registered client plugins without a manually maintained application type map.
  • All programmatic built-in plugin identifiers follow the canonical camelCase list; package/URL slugs remain separately documented.
  • Backend and client registration, resource hooks, provider overrides, diagnostics, codegen, and docs use the same resolved plugin identifier.
  • Every first-party backend plugin uses a single options-object factory; optional-only configurations support a zero-argument call.
  • Every client plugin definition contains only plugin-specific options and inherits applicable stack runtime values.
  • Backend and client lifecycle hooks follow documented naming grammars, with a complete migration table for every rename.
  • Authorized, trusted internal, and lower-level raw server calls remain semantically distinct and unmistakably named after [v3 auth] Remove RC authorization paths and publish the v3 contract #193.
  • AI Chat, Blog, CMS, Comments, Form Builder, Kanban, Media, OpenAPI, Route Docs, and UI Builder each have a completed audit row with focused tests.
  • OpenAPI backend-only, Route Docs client-only, and UI Builder client-only-over-CMS behavior are documented as intentional.
  • A consumer-defined fixture proves the normalized plugin authoring interface and override inference outside built-in plugins.
  • Next.js, React Router, and TanStack generated applications use the canonical DX and pass production builds and E2E tests.
  • Docs, playground, registry artifacts, package exports, declaration output, lint, typecheck, build, tests, and strict unused-export checks are green.
  • Repository searches and compile-time fixtures prove removed IDs, positional hooks, duplicated runtime fields, manual override maps, and ambiguous old constructor examples are absent from maintained consumers.

Testing Decisions

  • Test at the highest consumer seam: create complete client/backend stacks and exercise generated framework applications rather than unit-testing private config-merging helpers alone.
  • Add a framework-neutral client-stack contract suite that registers every client plugin and observes resolved loader, metadata, sitemap, browser-resource, provider, and override behavior through public interfaces.
  • Add SSR/CSR parity tests proving that a plugin's effective API and site destinations are identical across loader prefetch, hydration, browser refetch, mutation, metadata, and sitemap paths.
  • Add same-origin inheritance tests, path-only override tests, full endpoint replacement tests, and cross-origin header-isolation tests. Explicitly prove cookies and authorization headers do not cross a new origin unless supplied by that plugin's transport.
  • Add React Query identity tests proving all built-in plugins use the stack query client and hydrated cache rather than constructing or accepting a hidden second cache.
  • Add compile-time tests for inferred override keys and values, exact plugin IDs, duplicate IDs, invalid registration keys, one-sided plugins, required plugin-specific config, and third-party plugin definitions.
  • Add provider tests proving consumers do not need a manual override type map and that invalid plugin override blocks fail at compile time.
  • Add backend factory contract tests for every built-in backend plugin: options object, zero-config behavior where valid, required dependencies where real, nested hooks, and declaration output.
  • Add lifecycle contract tests through public operations/loaders for each renamed hook. Assert phase ordering and result/error behavior, not private dispatch implementation.
  • Reuse the operation/lifecycle parity coverage established by [v3 auth] Ship one-rule full-stack authorization #180 to ensure naming cleanup does not change authorization, validation, request/internal, or error semantics.
  • Add server-surface tests proving request-authorized calls, trusted internal calls, and raw lower-level calls retain their documented trust and lifecycle contracts.
  • Include focused cases for AI Chat public/authenticated modes, CMS/UI Builder composition, Media storage/upload flows, Route Docs introspection, and OpenAPI generation.
  • Update and run generated Next.js, React Router, and TanStack production builds, client-bundle scans, hydration/E2E flows, and framework-specific route factories.
  • Run registry generation/install/build tests and ensure generated registry artifacts are committed and reproducible.
  • Test migration aliases only if release policy requires them; canonical examples and fixtures must never depend on aliases.
  • Add repository scans for deprecated fields and shapes as guardrails, but do not use textual scans as a substitute for behavioral/type tests.

Out of Scope

  • Changing authorization permissions, rules, trust semantics, operation ordering, or public/protected behavior delivered by [v3 auth] Ship one-rule full-stack authorization #180.
  • Migrating any [v3 auth] Ship one-rule full-stack authorization #180 child ticket inside this issue or using this issue to delay [v3 auth] Migrate Form Builder authorization #188[v3 auth] Remove RC authorization paths and publish the v3 contract #193.
  • Redesigning plugin business behavior, schemas, UI, routes, query keys, caching policies, or storage/provider protocols.
  • Adapting BTST plugins to unrelated third-party APIs. A separate endpoint must implement the corresponding BTST plugin HTTP contract.
  • Sending server secrets to browser plugins or automatically forwarding credentials cross-origin.
  • Adding a backend plugin for UI Builder, a client plugin for OpenAPI, or a backend plugin for Route Docs solely for symmetry.
  • Adding plugin-instance aliasing or multiple instances unless the current supported behavior is explicitly proven and retained by the registration design.
  • Introducing authorization-result caching, row/tenant filtering, or new framework-specific configuration paths.
  • Automating unrelated CSS installation or package dependency management.

Further Notes

Completed prerequisites

Their final v3 authorization/server-call contract is settled and must be preserved during this interface normalization.

Suggested execution order

  1. Capture the final post-[v3 auth] Remove RC authorization paths and publish the v3 contract #193 all-plugin inventory and failing consumer-level contract tests.
  2. Land the canonical client runtime resolution and provider projection at one external seam.
  3. Migrate every client plugin and generated framework integration to inherited runtime configuration.
  4. Normalize programmatic plugin IDs and inferred provider overrides.
  5. Normalize backend/client factories and lifecycle vocabulary across the audit matrix.
  6. Audit final server trust-surface names without changing [v3 auth] Ship one-rule full-stack authorization #180 semantics.
  7. Complete documentation, migration guidance, registry/codegen regeneration, repository scans, and full verification.

The ticket is complete only when the entire audit matrix is checked. A Blog/CMS tracer may guide implementation, but it is not sufficient evidence of completion.

RC3 coordination amendment

This section records the final decisions made after the original specification was written. It is authoritative where release sequencing or scope wording above is older.

Target post-#202 consumer DX

One resolved client stack owns shared runtime and registered plugin definitions. The provider consumes that stack and accepts only framework/runtime services, the already-settled optional authorization inputs, and genuine presentation overrides:

const stack = createClientStack({
  api: {
    baseURL,
    basePath: "/api/data",
  },
  site: {
    baseURL,
    basePath: "/pages",
  },
  queryClient,
  plugins: {
    blog: blogClientPlugin(),
    comments: commentsClientPlugin(),
  },
})

<StackProvider
  stack={stack}
  router={router}
  auth={clientAuth}
  initialIdentity={initialIdentity}
  overrides={{
    blog: {
      post: { /* real presentation customization */ },
    },
  }}
>
  {children}
</StackProvider>

The exact customization fields remain plugin-owned, but these ownership boundaries are normative:

  1. createClientStack owns API/site/query runtime and plugin registration.
  2. StackProvider owns browser/framework services and the optional BTST authorization values already established by [v3 auth] Ship one-rule full-stack authorization #180[v3 auth] Remove RC authorization paths and publish the v3 contract #193.
  3. overrides contains only actual plugin-specific presentation or browser behavior customization.
  4. Omitting overrides entirely is the canonical zero-customization path.

Folded scope from #171: zero-customization providers

#171 is fully subsumed here because provider override inference and the centralized client runtime are the correct seam for solving it. The implementation must satisfy all of the following:

  • A standalone built-in plugin component with no customization can use StackProvider without a generic override map and without an overrides prop.
  • Type fixtures cover both the zero-override path and adding a correctly typed plugin override later.
  • Truly required plugin-specific values remain required and produce an actionable TypeScript error; zero-customization support must not silently default real requirements.
  • Documentation explicitly distinguishes plugin registration, provider/runtime services, and plugin-specific presentation/behavior overrides.
  • Installation examples, plugin examples, generated integrations, codegen templates, and test fixtures emit no empty override blocks merely to identify or activate a plugin.
  • usePluginOverrides() resolves a safe empty/default value when no override block is supplied.
  • No additional runtime provider, registration API, or client bundle dependency is introduced solely to support zero-customization usage.
  • Docs builds, package typechecks, and relevant provider tests must pass at the end of this migration.

Additional acceptance criteria:

  • A standalone built-in plugin component works with no generic override map and no overrides prop when it has no plugin-specific customization.
  • Type fixtures prove the zero-override path and later addition of a valid, inferred plugin override.
  • Type fixtures prove genuinely required plugin-specific values still fail with an actionable error.
  • usePluginOverrides() safely resolves a default when no block is supplied.
  • Docs explicitly distinguish registration, provider/runtime services, and plugin overrides.
  • Maintained examples and generated artifacts contain no empty override blocks used as activation signals.
  • No new provider, registration API, or bundle dependency exists solely for this path.

Frozen authorization boundary

#202 normalizes ownership, names, construction, and type inference around the final #180#193 authorization work. It does not redesign authorization. Preserve typed plugin-owned permission descriptors, application-owned rules, local browser checks, authoritative backend enforcement, managed-backend contracts, tri-state initialIdentity hydration, and the request/trusted/raw lifecycle split. Do not restore removed global open-string permission APIs or introduce provider-specific auth adapters in core.

Coordinated RC3 dependency and release chain

  1. Land and fully verify core [v3 RC3 DX] Centralize client runtime configuration and normalize all plugin interfaces #202.
  2. Only after [v3 RC3 DX] Centralize client runtime configuration and normalize all plugin interfaces #202 lands, migrate the maintained Better Auth UI companion against the finalized API and prepare @btst/better-auth-ui@2.0.0-rc.3.
  3. Restore the main-repository Better Auth UI CLI catalog, minimal scaffold, docs, and clean-install coverage against that migrated companion.
  4. Complete the coordinated cross-repository gate and publish @btst/stack@3.0.0-rc.3 and @btst/better-auth-ui@2.0.0-rc.3 under next.
  5. Hand the published RC3 to the user for incremental RC2-to-RC3 real-project validation in Better Stack Web and Bio Mogging.

#202 owns step 1. Better Auth UI implementation must not start early, and core RC3 must not be published independently from the coordinated gate. Real-project validation is deliberately post-publication and does not block RC3.

Explicitly out of scope for #202

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified and ready for an agent to implement

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions