d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
355 lines
12 KiB
TypeScript
355 lines
12 KiB
TypeScript
/**
|
|
* Environment utility functions for consistent environment detection across the application
|
|
*/
|
|
import { env, getEnv, isFalsy, isTruthy } from './env'
|
|
|
|
/**
|
|
* Is the application running in production mode
|
|
*/
|
|
export const isProd = env.NODE_ENV === 'production'
|
|
|
|
/**
|
|
* Is the application running in development mode
|
|
*/
|
|
export const isDev = env.NODE_ENV === 'development'
|
|
|
|
/**
|
|
* Is the application running in test mode
|
|
*/
|
|
export const isTest = env.NODE_ENV === 'test'
|
|
|
|
/**
|
|
* Is this the hosted version of the application.
|
|
* True for sim.ai and any subdomain of sim.ai (e.g. staging.sim.ai, dev.sim.ai).
|
|
*/
|
|
const appUrl = getEnv('NEXT_PUBLIC_APP_URL')
|
|
let appHostname = ''
|
|
try {
|
|
appHostname = appUrl ? new URL(appUrl).hostname : ''
|
|
} catch {
|
|
// invalid URL — isHosted stays false
|
|
}
|
|
export const isHosted = appHostname === 'sim.ai' || appHostname.endsWith('.sim.ai')
|
|
|
|
/**
|
|
* Is billing enforcement enabled
|
|
*/
|
|
export const isBillingEnabled = isTruthy(env.BILLING_ENABLED)
|
|
|
|
/**
|
|
* Block free-plan accounts from programmatic workflow execution (API key, public
|
|
* API, MCP server, generic webhooks, cross-origin chat embeds).
|
|
* Gated behind {@link isBillingEnabled}; off by default so the paywall can ship
|
|
* dark and be enabled per-deployment once verified.
|
|
*/
|
|
export const isFreeApiDeploymentGateEnabled = isTruthy(env.FREE_API_DEPLOYMENT_GATE_ENABLED)
|
|
|
|
/**
|
|
* Is email verification enabled
|
|
*/
|
|
export const isEmailVerificationEnabled = isTruthy(env.EMAIL_VERIFICATION_ENABLED)
|
|
|
|
/**
|
|
* Is authentication disabled (for self-hosted deployments behind private networks)
|
|
* This flag is blocked when isHosted is true.
|
|
*/
|
|
export const isAuthDisabled = isTruthy(env.DISABLE_AUTH) && !isHosted
|
|
|
|
if (isTruthy(env.DISABLE_AUTH)) {
|
|
import('@sim/logger')
|
|
.then(({ createLogger }) => {
|
|
const logger = createLogger('EnvFlags')
|
|
if (isHosted) {
|
|
logger.error(
|
|
'DISABLE_AUTH is set but ignored on hosted environment. Authentication remains enabled for security.'
|
|
)
|
|
} else {
|
|
logger.warn(
|
|
'DISABLE_AUTH is enabled. Authentication is bypassed and all requests use an anonymous session. Only use this in trusted private networks.'
|
|
)
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// Fallback during config compilation when logger is unavailable
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Whether database/connector tools may connect to private, reserved, or loopback
|
|
* hosts (e.g. Docker/K8s service names, localhost). Off by default: the SSRF guard
|
|
* in {@link validateDatabaseHost} blocks these so an untrusted user cannot pivot
|
|
* into the deployment's internal network. Self-hosted operators can opt in when
|
|
* their database lives on the same private network. Blocked on the hosted platform
|
|
* regardless of the env var, mirroring {@link isAuthDisabled}.
|
|
*/
|
|
export const isPrivateDatabaseHostsAllowed = isTruthy(env.ALLOW_PRIVATE_DATABASE_HOSTS) && !isHosted
|
|
|
|
if (isTruthy(env.ALLOW_PRIVATE_DATABASE_HOSTS)) {
|
|
import('@sim/logger')
|
|
.then(({ createLogger }) => {
|
|
const logger = createLogger('EnvFlags')
|
|
if (isHosted) {
|
|
logger.error(
|
|
'ALLOW_PRIVATE_DATABASE_HOSTS is set but ignored on hosted environment. Private/reserved database hosts remain blocked for security.'
|
|
)
|
|
} else {
|
|
logger.warn(
|
|
'ALLOW_PRIVATE_DATABASE_HOSTS is enabled. Database/connector tools may reach private, reserved, and loopback hosts. Only use this in trusted private networks.'
|
|
)
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// Fallback during config compilation when logger is unavailable
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Is user registration disabled
|
|
*/
|
|
export const isRegistrationDisabled = isTruthy(env.DISABLE_REGISTRATION)
|
|
|
|
/**
|
|
* Is email/password authentication enabled (defaults to true)
|
|
*/
|
|
export const isEmailPasswordEnabled = !isFalsy(env.EMAIL_PASSWORD_SIGNUP_ENABLED)
|
|
|
|
/**
|
|
* Is MX-based signup validation enabled (blocks no-MX domains and denylisted shared spam
|
|
* mail backends). Opt-in to avoid adding a DNS dependency or blocking legitimate signups on
|
|
* self-hosted deployments with non-standard mail setups; enable on abuse-targeted deployments.
|
|
*/
|
|
export const isSignupMxValidationEnabled = isTruthy(env.SIGNUP_MX_VALIDATION_ENABLED)
|
|
|
|
/**
|
|
* Is AWS AppConfig the source of truth for the signup/login gating lists.
|
|
* Hosted-only and requires both AppConfig identifiers (injected by the infra
|
|
* stack). Self-hosted/OSS deployments always use the env-var fallback, so the
|
|
* AppConfig client is never reached off-hosted.
|
|
*/
|
|
export const isAppConfigEnabled =
|
|
isHosted && Boolean(env.APPCONFIG_APPLICATION && env.APPCONFIG_ENVIRONMENT)
|
|
|
|
/**
|
|
* Is Trigger.dev enabled for async job processing
|
|
*/
|
|
export const isTriggerDevEnabled = isTruthy(env.TRIGGER_DEV_ENABLED)
|
|
|
|
/**
|
|
* Is SSO enabled for enterprise authentication
|
|
*/
|
|
export const isSsoEnabled = isTruthy(env.SSO_ENABLED)
|
|
|
|
/**
|
|
* Is access control (permission groups) enabled via env var override
|
|
* This bypasses plan requirements for self-hosted deployments
|
|
*/
|
|
export const isAccessControlEnabled = isTruthy(env.ACCESS_CONTROL_ENABLED)
|
|
|
|
/**
|
|
* Is organizations enabled
|
|
* True if billing is enabled (orgs come with billing), OR explicitly enabled via env var,
|
|
* OR if access control is enabled (access control requires organizations)
|
|
*/
|
|
export const isOrganizationsEnabled =
|
|
isBillingEnabled || isTruthy(env.ORGANIZATIONS_ENABLED) || isAccessControlEnabled
|
|
|
|
/**
|
|
* Is inbox (Sim Mailer) enabled via env var override
|
|
* This bypasses hosted requirements for self-hosted deployments
|
|
*/
|
|
export const isInboxEnabled = isTruthy(env.INBOX_ENABLED)
|
|
|
|
/**
|
|
* Is whitelabeling enabled via env var override
|
|
* This bypasses hosted requirements for self-hosted deployments
|
|
*/
|
|
export const isWhitelabelingEnabled = isTruthy(env.WHITELABELING_ENABLED)
|
|
|
|
/**
|
|
* Is audit logs enabled via env var override
|
|
* This bypasses hosted requirements for self-hosted deployments
|
|
*/
|
|
export const isAuditLogsEnabled = isTruthy(env.AUDIT_LOGS_ENABLED)
|
|
|
|
/**
|
|
* Is data retention enabled via env var override
|
|
* This bypasses hosted requirements for self-hosted deployments
|
|
*/
|
|
export const isDataRetentionEnabled = isTruthy(env.DATA_RETENTION_ENABLED)
|
|
|
|
/**
|
|
* Is data drains enabled via env var override
|
|
* This bypasses hosted requirements for self-hosted deployments
|
|
*/
|
|
export const isDataDrainsEnabled = isTruthy(env.DATA_DRAINS_ENABLED)
|
|
|
|
/**
|
|
* Is workspace forking enabled via env var override
|
|
* This bypasses hosted (Enterprise) requirements for self-hosted deployments
|
|
*/
|
|
export const isForkingEnabled = isTruthy(env.FORKING_ENABLED)
|
|
|
|
/**
|
|
* Is E2B enabled for remote code execution
|
|
*/
|
|
export const isE2bEnabled = isTruthy(env.E2B_ENABLED)
|
|
|
|
/**
|
|
* Whether the E2B document-generation sandbox is enabled.
|
|
*
|
|
* Requires E2B (with an API key) AND a dedicated doc-generation template id.
|
|
* When true, ALL four formats compile in the E2B doc sandbox: pptx/docx via Node
|
|
* (pptxgenjs/docx + react-icons/sharp icons), pdf/xlsx via Python
|
|
* (reportlab/openpyxl). When false, compilation stays on the JavaScript
|
|
* (isolated-vm) path, byte-identical to its prior behavior (and xlsx is
|
|
* unavailable). Drives both the Sim compile backend and the `docCompiler` flag
|
|
* sent to the copilot file subagent so the agent's output and compiler agree.
|
|
*/
|
|
export const isE2BDocEnabled =
|
|
isE2bEnabled && Boolean(env.E2B_API_KEY) && Boolean(env.MOTHERSHIP_E2B_DOC_TEMPLATE_ID)
|
|
|
|
/**
|
|
* Whether Ollama is configured (OLLAMA_URL is set).
|
|
* When true, models that are not in the static cloud model list and have no
|
|
* slash-prefixed provider namespace are assumed to be Ollama models
|
|
* and do not require an API key.
|
|
*/
|
|
export const isOllamaConfigured = Boolean(env.OLLAMA_URL)
|
|
|
|
/**
|
|
* Whether Azure OpenAI / Azure Anthropic credentials are pre-configured at the server level
|
|
* (via AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_ANTHROPIC_ENDPOINT, etc.).
|
|
* When true, the endpoint, API key, and API version fields are hidden in the Agent block UI.
|
|
* Set NEXT_PUBLIC_AZURE_CONFIGURED=true in self-hosted deployments on Azure.
|
|
*/
|
|
export const isAzureConfigured = isTruthy(getEnv('NEXT_PUBLIC_AZURE_CONFIGURED'))
|
|
|
|
/**
|
|
* Whether a Cohere API key is pre-configured server-side for the Knowledge block reranker
|
|
* (`COHERE_API_KEY` or `COHERE_API_KEY_1/2/3`). When true, the Cohere API Key field is hidden
|
|
* in the Knowledge block UI.
|
|
* Set NEXT_PUBLIC_COHERE_CONFIGURED=true in self-hosted deployments that ship a Cohere key.
|
|
*/
|
|
export const isCohereConfigured = isTruthy(getEnv('NEXT_PUBLIC_COHERE_CONFIGURED'))
|
|
|
|
/**
|
|
* Are invitations disabled globally
|
|
* When true, workspace invitations are disabled for all users
|
|
*/
|
|
export const isInvitationsDisabled = isTruthy(env.DISABLE_INVITATIONS)
|
|
|
|
/**
|
|
* Is public API access disabled globally
|
|
* When true, the public API toggle is hidden and public API access is blocked
|
|
*/
|
|
export const isPublicApiDisabled = isTruthy(env.DISABLE_PUBLIC_API)
|
|
|
|
/**
|
|
* Is Google OAuth login disabled
|
|
* When true, the Google OAuth login button is hidden even when credentials are configured
|
|
*/
|
|
export const isGoogleAuthDisabled = isTruthy(env.DISABLE_GOOGLE_AUTH)
|
|
|
|
/**
|
|
* Is GitHub OAuth login disabled
|
|
* When true, the GitHub OAuth login button is hidden even when credentials are configured
|
|
*/
|
|
export const isGithubAuthDisabled = isTruthy(env.DISABLE_GITHUB_AUTH)
|
|
|
|
/**
|
|
* Is Microsoft OAuth login disabled
|
|
* When true, the Microsoft OAuth login button is hidden even when credentials are configured
|
|
*/
|
|
export const isMicrosoftAuthDisabled = isTruthy(env.DISABLE_MICROSOFT_AUTH)
|
|
|
|
/**
|
|
* Is email/password signup disabled
|
|
* When true, new registrations via email/password are blocked at the server level.
|
|
* Existing users can still sign in with email/password.
|
|
*/
|
|
export const isEmailSignupDisabled = isTruthy(env.DISABLE_EMAIL_SIGNUP)
|
|
|
|
/**
|
|
* Is React Grab enabled for UI element debugging
|
|
* When true and in development mode, enables React Grab for copying UI element context to clipboard
|
|
*/
|
|
export const isReactGrabEnabled = isDev && isTruthy(env.REACT_GRAB_ENABLED)
|
|
|
|
/**
|
|
* Is React Scan enabled for performance debugging
|
|
* When true and in development mode, enables React Scan for detecting render performance issues
|
|
*/
|
|
export const isReactScanEnabled = isDev && isTruthy(env.REACT_SCAN_ENABLED)
|
|
|
|
/**
|
|
* Returns the parsed allowlist of integration block types from the environment variable.
|
|
* If not set or empty, returns null (meaning all integrations are allowed).
|
|
*/
|
|
export function getAllowedIntegrationsFromEnv(): string[] | null {
|
|
if (!env.ALLOWED_INTEGRATIONS) return null
|
|
const parsed = env.ALLOWED_INTEGRATIONS.split(',')
|
|
.map((i) => i.trim().toLowerCase())
|
|
.filter(Boolean)
|
|
return parsed.length > 0 ? parsed : null
|
|
}
|
|
|
|
/**
|
|
* Returns the preview block types revealed via the environment variable — the
|
|
* off-AppConfig reveal path for self-hosters and local dev. If not set or empty,
|
|
* returns an empty array (all `preview: true` blocks stay hidden). Block types
|
|
* are already lowercase snake_case, so entries are trimmed but not lowercased.
|
|
*/
|
|
export function getPreviewBlocksFromEnv(): string[] {
|
|
if (!env.PREVIEW_BLOCKS) return []
|
|
return env.PREVIEW_BLOCKS.split(',')
|
|
.map((t) => t.trim())
|
|
.filter(Boolean)
|
|
}
|
|
|
|
/**
|
|
* Returns the list of blacklisted provider IDs from the environment variable.
|
|
* If not set or empty, returns an empty array (meaning no providers are blacklisted).
|
|
*/
|
|
export function getBlacklistedProvidersFromEnv(): string[] {
|
|
if (!env.BLACKLISTED_PROVIDERS) return []
|
|
return env.BLACKLISTED_PROVIDERS.split(',')
|
|
.map((p) => p.trim().toLowerCase())
|
|
.filter(Boolean)
|
|
}
|
|
|
|
/**
|
|
* Normalizes a domain entry from the ALLOWED_MCP_DOMAINS env var.
|
|
* Accepts bare hostnames (e.g., "mcp.company.com") or full URLs (e.g., "https://mcp.company.com").
|
|
* Extracts the hostname in either case.
|
|
*/
|
|
function normalizeDomainEntry(entry: string): string {
|
|
const trimmed = entry.trim().toLowerCase()
|
|
if (!trimmed) return ''
|
|
if (trimmed.includes('://')) {
|
|
try {
|
|
return new URL(trimmed).hostname
|
|
} catch {
|
|
return trimmed
|
|
}
|
|
}
|
|
return trimmed
|
|
}
|
|
|
|
/**
|
|
* Get allowed MCP server domains from the ALLOWED_MCP_DOMAINS env var.
|
|
* Returns null if not set (all domains allowed), or parsed array of lowercase hostnames.
|
|
* Accepts both bare hostnames and full URLs in the env var value.
|
|
*/
|
|
export function getAllowedMcpDomainsFromEnv(): string[] | null {
|
|
if (!env.ALLOWED_MCP_DOMAINS) return null
|
|
const parsed = env.ALLOWED_MCP_DOMAINS.split(',').map(normalizeDomainEntry).filter(Boolean)
|
|
return parsed.length > 0 ? parsed : null
|
|
}
|
|
|
|
/**
|
|
* Get cost multiplier based on environment
|
|
*/
|
|
export function getCostMultiplier(): number {
|
|
return isProd ? (env.COST_MULTIPLIER ?? 1) : 1
|
|
}
|