Skip to content

Commit e268562

Browse files
fix(merge): restore staging's env-flags exports dropped by the merge commit
The 79d0dd0 merge recorded the pre-merge env-flags.ts: the path was reset out of the index mid-merge to keep a local debug edit unstaged, which also discarded staging's isSessionPoliciesEnabled / isCopilotToolPermissionsEnabled exports and broke six importers added by the desktop-app PR (#5998). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M563cbFy2S74GvSDf2C3R
1 parent 79d0dd0 commit e268562

1 file changed

Lines changed: 139 additions & 34 deletions

File tree

apps/sim/lib/core/config/env-flags.ts

Lines changed: 139 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
/**
22
* Environment utility functions for consistent environment detection across the application
33
*/
4-
import { env, getEnv, isFalsy, isTruthy } from './env'
4+
import {
5+
ENTERPRISE_FEATURE_LEGACY_DEFAULTS,
6+
type EnterpriseFeature,
7+
resolveEnterpriseEntitlement,
8+
} from './enterprise-entitlements'
9+
import { env, envBoolean, getEnv, isFalsy, isTruthy } from './env'
510

611
/**
712
* Is the application running in production mode
@@ -45,6 +50,18 @@ export const isCopilotBillingAttributionV1Enabled = isTruthy(
4550
*/
4651
export const isCopilotBillingProtocolRequired = isTruthy(env.COPILOT_BILLING_PROTOCOL_REQUIRED)
4752

53+
/**
54+
* Holds tools the catalog marks `requiresApproval` — shell commands, workflow
55+
* runs, sandboxed code, deployments, integration calls — behind an explicit
56+
* Allow / Skip prompt, blocking the mothership turn until the user answers.
57+
*
58+
* Off by default: turning it on makes the copilot prompt on its most frequently
59+
* used tools, so it is an opt-in change in how the product feels, not just a
60+
* safety toggle. With it off nothing is stamped, gated, or persisted, and an
61+
* approval stamp arriving from Go is cleared on the way to the client.
62+
*/
63+
export const isCopilotToolPermissionsEnabled = isTruthy(env.COPILOT_TOOL_PERMISSIONS_ENABLED)
64+
4865
/**
4966
* Is billing enforcement enabled.
5067
*
@@ -172,73 +189,161 @@ export const isSlackExtendedScopesEnabled =
172189
export const isTriggerDevEnabled = isTruthy(env.TRIGGER_DEV_ENABLED)
173190

174191
/**
175-
* Is SSO enabled for enterprise authentication
192+
* Turns on the whole enterprise suite for a deployment that does not run
193+
* billing. Individual feature flags below still win where they are set, so an
194+
* operator can enable everything and then switch one feature back off.
195+
*
196+
* Server code reads `ENTERPRISE_ENABLED`; the browser reads the
197+
* `NEXT_PUBLIC_ENTERPRISE_ENABLED` twin (see {@link isBillingEnabled}).
198+
* Deployments must set both together.
199+
*/
200+
export const isEnterpriseEnabled =
201+
typeof window === 'undefined'
202+
? isTruthy(env.ENTERPRISE_ENABLED)
203+
: isTruthy(getEnv('NEXT_PUBLIC_ENTERPRISE_ENABLED'))
204+
205+
/**
206+
* Reads a feature's own flag as a tri-state, picking the server var or its
207+
* browser twin for the current runtime. `undefined` means the operator left it
208+
* unset, which is what lets the master switch and legacy default apply.
176209
*/
177-
export const isSsoEnabled = isTruthy(env.SSO_ENABLED)
210+
function explicitEnterpriseFlag(
211+
serverValue: boolean | string | undefined,
212+
clientKey: string
213+
): boolean | undefined {
214+
return typeof window === 'undefined' ? envBoolean(serverValue) : envBoolean(getEnv(clientKey))
215+
}
178216

179217
/**
180-
* Is access control (permission groups) enabled via env var override.
181-
* This bypasses plan requirements for self-hosted deployments.
218+
* Resolves one enterprise feature for this deployment.
182219
*
183-
* Server code reads `ACCESS_CONTROL_ENABLED`; the browser reads the
184-
* `NEXT_PUBLIC_ACCESS_CONTROL_ENABLED` twin (see {@link isBillingEnabled}).
220+
* When billing runs, subscription plans decide entitlement and these flags are
221+
* only explicit overrides — so an unset flag stays `false` and never widens
222+
* access on Sim Cloud. When billing is off there is no plan to consult, so
223+
* resolution falls through the master switch to the feature's legacy default
224+
* (see {@link ENTERPRISE_FEATURE_LEGACY_DEFAULTS}).
225+
*/
226+
function enterpriseFeatureEnabled(
227+
feature: EnterpriseFeature,
228+
serverValue: boolean | string | undefined,
229+
clientKey: string
230+
): boolean {
231+
const explicit = explicitEnterpriseFlag(serverValue, clientKey)
232+
if (isBillingEnabled) return explicit ?? false
233+
return resolveEnterpriseEntitlement({
234+
explicit,
235+
masterEnabled: isEnterpriseEnabled,
236+
legacyDefault: ENTERPRISE_FEATURE_LEGACY_DEFAULTS[feature],
237+
})
238+
}
239+
240+
/**
241+
* Is SSO enabled for enterprise authentication
185242
*/
186-
export const isAccessControlEnabled =
187-
typeof window === 'undefined'
188-
? isTruthy(env.ACCESS_CONTROL_ENABLED)
189-
: isTruthy(getEnv('NEXT_PUBLIC_ACCESS_CONTROL_ENABLED'))
243+
export const isSsoEnabled = enterpriseFeatureEnabled(
244+
'sso',
245+
env.SSO_ENABLED,
246+
'NEXT_PUBLIC_SSO_ENABLED'
247+
)
248+
249+
/**
250+
* Is access control (permission groups) enabled.
251+
* Required for permission-group enforcement to run at all off-hosted.
252+
*/
253+
export const isAccessControlEnabled = enterpriseFeatureEnabled(
254+
'accessControl',
255+
env.ACCESS_CONTROL_ENABLED,
256+
'NEXT_PUBLIC_ACCESS_CONTROL_ENABLED'
257+
)
190258

191259
/**
192260
* Is organizations enabled.
193-
* True if billing is enabled (orgs come with billing), OR explicitly enabled via env var,
194-
* OR if access control is enabled (access control requires organizations).
261+
* True if billing is enabled (orgs come with billing), OR resolved on for this
262+
* deployment, OR if access control is enabled (access control requires
263+
* organizations).
195264
*
196265
* Each term resolves through its `NEXT_PUBLIC_*` twin in the browser (see
197266
* {@link isBillingEnabled}), so client code — e.g. the better-auth
198267
* `organizationClient` plugin registration — sees the same value as the server.
199268
*/
200269
export const isOrganizationsEnabled =
201270
isBillingEnabled ||
202-
(typeof window === 'undefined'
203-
? isTruthy(env.ORGANIZATIONS_ENABLED)
204-
: isTruthy(getEnv('NEXT_PUBLIC_ORGANIZATIONS_ENABLED'))) ||
271+
enterpriseFeatureEnabled(
272+
'organizations',
273+
env.ORGANIZATIONS_ENABLED,
274+
'NEXT_PUBLIC_ORGANIZATIONS_ENABLED'
275+
) ||
205276
isAccessControlEnabled
206277

207278
/**
208-
* Is inbox (Sim Mailer) enabled via env var override
209-
* This bypasses hosted requirements for self-hosted deployments
279+
* Is inbox (Sim Mailer) enabled
210280
*/
211-
export const isInboxEnabled = isTruthy(env.INBOX_ENABLED)
281+
export const isInboxEnabled = enterpriseFeatureEnabled(
282+
'inbox',
283+
env.INBOX_ENABLED,
284+
'NEXT_PUBLIC_INBOX_ENABLED'
285+
)
212286

213287
/**
214-
* Is whitelabeling enabled via env var override
215-
* This bypasses hosted requirements for self-hosted deployments
288+
* Is whitelabeling enabled
216289
*/
217-
export const isWhitelabelingEnabled = isTruthy(env.WHITELABELING_ENABLED)
290+
export const isWhitelabelingEnabled = enterpriseFeatureEnabled(
291+
'whitelabeling',
292+
env.WHITELABELING_ENABLED,
293+
'NEXT_PUBLIC_WHITELABELING_ENABLED'
294+
)
218295

219296
/**
220-
* Is audit logs enabled via env var override
221-
* This bypasses hosted requirements for self-hosted deployments
297+
* Is audit log reading enabled.
298+
*
299+
* Off-hosted this replaces the enterprise-subscription check that audit access
300+
* used to require, which no billing-free deployment could ever satisfy.
222301
*/
223-
export const isAuditLogsEnabled = isTruthy(env.AUDIT_LOGS_ENABLED)
302+
export const isAuditLogsEnabled = enterpriseFeatureEnabled(
303+
'auditLogs',
304+
env.AUDIT_LOGS_ENABLED,
305+
'NEXT_PUBLIC_AUDIT_LOGS_ENABLED'
306+
)
307+
308+
/**
309+
* Is retention *deletion* enabled.
310+
*
311+
* Configuring retention has always been possible with billing off; this flag
312+
* governs whether the cleanup pass actually expires data. Opt-in on purpose —
313+
* see the note on `dataRetention` in {@link ENTERPRISE_FEATURE_LEGACY_DEFAULTS}.
314+
*/
315+
export const isDataRetentionEnabled = enterpriseFeatureEnabled(
316+
'dataRetention',
317+
env.DATA_RETENTION_ENABLED,
318+
'NEXT_PUBLIC_DATA_RETENTION_ENABLED'
319+
)
224320

225321
/**
226-
* Is data retention enabled via env var override
227-
* This bypasses hosted requirements for self-hosted deployments
322+
* Is data drains enabled
228323
*/
229-
export const isDataRetentionEnabled = isTruthy(env.DATA_RETENTION_ENABLED)
324+
export const isDataDrainsEnabled = enterpriseFeatureEnabled(
325+
'dataDrains',
326+
env.DATA_DRAINS_ENABLED,
327+
'NEXT_PUBLIC_DATA_DRAINS_ENABLED'
328+
)
230329

231330
/**
232-
* Is data drains enabled via env var override
233-
* This bypasses hosted requirements for self-hosted deployments
331+
* Are organization session policies enabled
234332
*/
235-
export const isDataDrainsEnabled = isTruthy(env.DATA_DRAINS_ENABLED)
333+
export const isSessionPoliciesEnabled = enterpriseFeatureEnabled(
334+
'sessionPolicies',
335+
env.SESSION_POLICIES_ENABLED,
336+
'NEXT_PUBLIC_SESSION_POLICIES_ENABLED'
337+
)
236338

237339
/**
238-
* Is workspace forking enabled via env var override
239-
* This bypasses hosted (Enterprise) requirements for self-hosted deployments
340+
* Is workspace forking enabled
240341
*/
241-
export const isForkingEnabled = isTruthy(env.FORKING_ENABLED)
342+
export const isForkingEnabled = enterpriseFeatureEnabled(
343+
'forking',
344+
env.FORKING_ENABLED,
345+
'NEXT_PUBLIC_FORKING_ENABLED'
346+
)
242347

243348
/**
244349
* The selected remote sandbox provider (`SANDBOX_PROVIDER`), defaulting to E2B.

0 commit comments

Comments
 (0)