Feature Flags
Strategy for migrating runtime config to managed feature flags without UI drift.
Why Feature Flags Here
Use feature flags when you need runtime control without redeploying:
progressive rollout of behavior
emergency disable switches
environment-specific behavior without config forks
short-lived experiments with explicit ownership
Keep static config for constants that are not product behavior (timeouts, hard security defaults, protocol-level settings).
Current Runtime Flags
Managed flags are declared in src/config/flags.config.ts, seeded into
feature_flags, and exposed in Admin → Site Settings when they are wired to
application behavior.
theme-managementenable-passkey-authenable-magic-linkauth-multi-session-enabledenable-two-factor-authrequire-2faauth-signup-enabledauth-api-keys-enabledorg-self-serve-creation-enabledorg-invitations-enabledenable-scim-pluginbilling-enabledbilling-enforcement-enabledcms-page-view-analytics-enabledtoast-duration-secondstoast-stack-limittoast-gap-pxtoast-expandtoast-positionsettings-layout-max-widthactions-as-iconsdashboard-breadcrumbs-enableddashboard-section-navigation-enabledai-components-enabledmedia-image-generation-enabledcontrol-plane-mcp-enabled
enable-two-factor-auth controls new TOTP enrollment. Turning it off never
removes the sign-in challenge from already-enrolled accounts, and their 2FA
management remains available. require-2fa is the separate dashboard
enrollment policy: signed-in users must enroll before using the dashboard.
Required enrollment also overrides a disabled enrollment flag, and required
users cannot disable 2FA. Impersonation sessions are exempt so an administrator
never enrolls a factor on behalf of the impersonated account; all 2FA management
endpoints are blocked during impersonation. Until enrollment is complete, the
Security page exposes only the 2FA enrollment card. Dashboard Server Actions,
cookie-authenticated application API mutations, and account-security mutations
also reject stale tabs. Token-authenticated integrations and public provider
callbacks keep their own authentication contracts.
require-2fa is an account-enrollment policy, not an additional TOTP step-up
on every passwordless authentication. Better Auth keeps its normal challenge
for credential sign-ins by enrolled accounts, while passkey and magic-link
flows retain their upstream passwordless behavior. Security-policy reads fail
closed: if the managed flag store is unavailable, dashboard access and 2FA
policy mutations are blocked instead of falling back to a disabled policy.
enable-scim-plugin also requires the server-only
SCIM_CREDENTIAL_HASH_SECRET to contain at least 32 characters. Enabling the
flag without that independent HMAC secret fails closed; disabling the flag does
not delete managed connections or credentials.
settings-layout-max-width controls the width of dashboard settings-style
surfaces, including Better Auth cards, Products & services, Authorization, and
other admin configuration panels that use the settings wrapper. Dashboard
Content catalog and library surfaces also read this value so dense working
pages can use the same width policy. Dashboard section roots, including
/dashboard, /dashboard/admin, /dashboard/content,
/dashboard/organization, /dashboard/settings, and /dashboard/test, also
inherit this policy through the shared page dashboard wrapper. It supports the
standard Tailwind max-width scale, the dashboard container width, and full.
actions-as-icons switches supported dashboard action groups between full
text buttons and compact icon buttons with tooltips, including Content
catalog actions and Yayaw Table toolbars wired to the runtime flag.
dashboard-breadcrumbs-enabled and dashboard-section-navigation-enabled
control the dashboard shell navigation chrome. Both are server-side experience
flags, seeded as enabled by default, and can be disabled independently from
Admin → Site Settings when evaluating alternate dashboard navigation patterns.
cms-page-view-analytics-enabled controls whether the CMS page runtime emits
cms_page_viewed events. It gates both the client analytics component and the
server-side page-view tracker, while provider credentials and capture mode stay
in deployment environment variables.
control-plane-mcp-enabled is the server-side kill switch for Codex-facing MCP
control-plane tools. It is seeded as disabled by default and should be checked
by MCP/API entry points before allowing Yayaw operations through the control
plane.
Runtime Access Pattern
The Flags SDK is the canonical declaration and discovery contract. Yayaw keeps
feature_flags as the internal site-settings provider, exposes metadata through
the Vercel Flags discovery endpoint, and can evaluate PostHog-backed keys through
the PostHog adapter when the slug is listed in POSTHOG_FLAG_KEYS.
Use getManagedFlagValues([...]) or getManagedBooleanFlagValues([...]) in
server layouts, providers, dashboard navigation, auth runtime config, and any
other hot path that needs multiple flags. Single-flag helpers are kept for narrow
compatibility paths, but repeated unit reads should not be added to request
critical code.
Client components may only consume flags whose managed definition has
exposeClient: true. Those values are loaded server-side with
loadClientRuntimeFlags(), hydrated into RuntimeFlagsProvider, and revalidated
with TanStack Query. Security, auth, authz, billing enforcement, and MCP flags
must remain server-side sources of truth.
Custom Flags
Flags that are present in feature_flags but are not declared in
MANAGED_FEATURE_FLAGS are treated as Custom Flags. The admin page generates
controls for them automatically from the stored DB value:
booleans render as switches
numbers render as numeric inputs
strings render as text inputs
objects, arrays, and null values render as JSON text areas
Custom Flags are free-form runtime values. They are only used by the
application when code explicitly calls getFlag("custom-slug") or one of the
typed DB fallback helpers with that slug.
Deprecated rollout flags such as blocks-ai-plan-v1,
blocks-ai-plan-persistence-v1, blocks-ai-plan-trigger-worker-v1,
page-ai-deep-refinement-enabled, and pages-wysiwyg-v1 are hidden from the
admin UI and removed by the seed because they no longer represent useful site
settings.
Non-Negotiable Contract: Logic + UI
Any flag that controls a feature must gate both:
backend/plugin behavior
user-facing UI entry points
Example: auth flags now control Better Auth plugins and visible UI cards/views. If a plugin is off but UI still renders controls, users can reach dead paths.
Priority Auth/Org Flags
Current rollout priorities:
auth-signup-enabled
Controls sign-up availability in auth UI and auth server options.
Replaces static toggling in auth config when running invitation-only periods.
auth-api-keys-enabled
Controls API key plugin and API key screens.
Useful when rolling out API access gradually by customer tier.
auth-multi-session-enabled
Controls the Better Auth multi-session plugin, the security sessions card, and the account-switching card in account settings.
Useful when you want a single active session model for stricter environments.
org-self-serve-creation-enabled
Controls whether users can create organizations themselves.
Useful for enterprise setups where organizations are provisioned centrally.
org-invitations-enabled
Controls invitation flows for adding members.
Allows temporarily freezing growth while keeping existing organizations usable.
What Should Stay Config/Env (Not Flags)
crypto/secrets and provider credentials
DB connection/runtime infra knobs
protocol/security invariants that must not drift at runtime
static routes, locale contracts, build config, and image optimizer config
billing catalog data, Stripe price IDs, and plan limits
true emergency maintenance hard switch (
MAINTENANCE_MODE)
Maintenance mode in Yayaw is intentionally env-based and middleware-driven:
MAINTENANCE_MODEMAINTENANCE_MODE_END_DATE
Implementation Pattern
Add the managed definition in
src/config/flags.config.tswith:
stable slug
category
explicit default value
clear description
Read through one server-side accessor:
getFlag(key)for unified PostHog-first lookup with DB fallbackgetManagedBooleanFlagValue(slug)for typed boolean gates
Apply dual gating:
server behavior
UI visibility
Add docs:
include flag in this page and architecture notes
explain whether it appears in Runtime settings or Custom Flags
Validate:
enabled and disabled states
permission boundaries
no dead links/routes in UI
Seed Contract
The seed creates missing managed flags and repairs name, description, and
category metadata. It does not overwrite enabled or value for existing rows,
so admin choices made in Site Settings survive deploys and reseeds.
Definition of Done for New Flags
slug and description added in seed
server behavior gated
UI surface gated
no broken routes when disabled
docs updated
content/llm/llm-source.mdupdated when assistant guidance changesgenerated assistant docs refreshed with
bun run docs:llm:generateCI checks pass (
check,tsc,build, docs integrity)