@@ -46,9 +46,18 @@ export const isCopilotBillingAttributionV1Enabled = isTruthy(
4646export const isCopilotBillingProtocolRequired = isTruthy ( env . COPILOT_BILLING_PROTOCOL_REQUIRED )
4747
4848/**
49- * Is billing enforcement enabled
49+ * Is billing enforcement enabled.
50+ *
51+ * Server code reads `BILLING_ENABLED`. Server-only vars never reach browser
52+ * bundles, so client evaluation reads the `NEXT_PUBLIC_BILLING_ENABLED` twin
53+ * (via `window.__ENV`, populated by `<PublicEnvScript>`) — reading
54+ * `env.BILLING_ENABLED` in client code is always `undefined`. Deployments must
55+ * set both vars together.
5056 */
51- export const isBillingEnabled = isTruthy ( env . BILLING_ENABLED )
57+ export const isBillingEnabled =
58+ typeof window === 'undefined'
59+ ? isTruthy ( env . BILLING_ENABLED )
60+ : isTruthy ( getEnv ( 'NEXT_PUBLIC_BILLING_ENABLED' ) )
5261
5362/**
5463 * Block free-plan accounts from programmatic workflow execution (API key, public
@@ -154,18 +163,32 @@ export const isTriggerDevEnabled = isTruthy(env.TRIGGER_DEV_ENABLED)
154163export const isSsoEnabled = isTruthy ( env . SSO_ENABLED )
155164
156165/**
157- * Is access control (permission groups) enabled via env var override
158- * This bypasses plan requirements for self-hosted deployments
166+ * Is access control (permission groups) enabled via env var override.
167+ * This bypasses plan requirements for self-hosted deployments.
168+ *
169+ * Server code reads `ACCESS_CONTROL_ENABLED`; the browser reads the
170+ * `NEXT_PUBLIC_ACCESS_CONTROL_ENABLED` twin (see {@link isBillingEnabled}).
159171 */
160- export const isAccessControlEnabled = isTruthy ( env . ACCESS_CONTROL_ENABLED )
172+ export const isAccessControlEnabled =
173+ typeof window === 'undefined'
174+ ? isTruthy ( env . ACCESS_CONTROL_ENABLED )
175+ : isTruthy ( getEnv ( 'NEXT_PUBLIC_ACCESS_CONTROL_ENABLED' ) )
161176
162177/**
163- * Is organizations enabled
178+ * Is organizations enabled.
164179 * True if billing is enabled (orgs come with billing), OR explicitly enabled via env var,
165- * OR if access control is enabled (access control requires organizations)
180+ * OR if access control is enabled (access control requires organizations).
181+ *
182+ * Each term resolves through its `NEXT_PUBLIC_*` twin in the browser (see
183+ * {@link isBillingEnabled}), so client code — e.g. the better-auth
184+ * `organizationClient` plugin registration — sees the same value as the server.
166185 */
167186export const isOrganizationsEnabled =
168- isBillingEnabled || isTruthy ( env . ORGANIZATIONS_ENABLED ) || isAccessControlEnabled
187+ isBillingEnabled ||
188+ ( typeof window === 'undefined'
189+ ? isTruthy ( env . ORGANIZATIONS_ENABLED )
190+ : isTruthy ( getEnv ( 'NEXT_PUBLIC_ORGANIZATIONS_ENABLED' ) ) ) ||
191+ isAccessControlEnabled
169192
170193/**
171194 * Is inbox (Sim Mailer) enabled via env var override
0 commit comments