import { createEnv } from '@t3-oss/env-nextjs' import { env as runtimeEnv } from 'next-runtime-env' import { z } from 'zod' /** * Universal environment variable getter that works in both client and server contexts. * - Client-side: Uses next-runtime-env for runtime injection (supports Docker runtime vars) * - Server-side: Falls back to process.env when runtimeEnv returns undefined * - Provides seamless Docker runtime variable support for NEXT_PUBLIC_ vars */ const getEnv = (variable: string) => runtimeEnv(variable) ?? process.env[variable] // biome-ignore format: keep alignment for readability export const env = createEnv({ skipValidation: true, server: { // Core Database & Authentication DATABASE_URL: z.string().url(), // Primary database connection string BETTER_AUTH_URL: z.string().url(), // Base URL for Better Auth service BETTER_AUTH_SECRET: z.string().min(32), // Secret key for Better Auth JWT signing DISABLE_REGISTRATION: z.boolean().optional(), // Flag to disable new user registration EMAIL_PASSWORD_SIGNUP_ENABLED: z.boolean().optional().default(true), // Enable email/password authentication (server-side enforcement) DISABLE_AUTH: z.boolean().optional(), // Bypass authentication entirely (self-hosted only, creates anonymous session) ALLOWED_LOGIN_EMAILS: z.string().optional(), // Comma-separated list of allowed email addresses for login ALLOWED_LOGIN_DOMAINS: z.string().optional(), // Comma-separated list of allowed email domains for login ENCRYPTION_KEY: z.string().min(32), // Key for encrypting sensitive data API_ENCRYPTION_KEY: z.string().min(32).optional(), // Dedicated key for encrypting API keys (optional for OSS) INTERNAL_API_SECRET: z.string().min(32), // Secret for internal API authentication // Copilot COPILOT_API_KEY: z.string().min(1).optional(), // Secret for internal sim agent API authentication SIM_AGENT_API_URL: z.string().url().optional(), // URL for internal sim agent API AGENT_INDEXER_URL: z.string().url().optional(), // URL for agent training data indexer AGENT_INDEXER_API_KEY: z.string().min(1).optional(), // API key for agent indexer authentication COPILOT_STREAM_TTL_SECONDS: z.number().optional(), // Redis TTL for copilot SSE buffer COPILOT_STREAM_EVENT_LIMIT: z.number().optional(), // Max events retained per stream COPILOT_STREAM_RESERVE_BATCH: z.number().optional(), // Event ID reservation batch size COPILOT_STREAM_FLUSH_INTERVAL_MS: z.number().optional(), // Buffer flush interval in ms COPILOT_STREAM_FLUSH_MAX_BATCH: z.number().optional(), // Max events per flush batch // Database & Storage REDIS_URL: z.string().url().optional(), // Redis connection string for caching/sessions // Payment & Billing STRIPE_SECRET_KEY: z.string().min(1).optional(), // Stripe secret key for payment processing STRIPE_WEBHOOK_SECRET: z.string().min(1).optional(), // General Stripe webhook secret STRIPE_FREE_PRICE_ID: z.string().min(1).optional(), // Stripe price ID for free tier FREE_TIER_COST_LIMIT: z.number().optional(), // Cost limit for free tier users FREE_STORAGE_LIMIT_GB: z.number().optional().default(5), // Storage limit in GB for free tier users STRIPE_PRO_PRICE_ID: z.string().min(1).optional(), // Stripe price ID for pro tier PRO_TIER_COST_LIMIT: z.number().optional(), // Cost limit for pro tier users PRO_STORAGE_LIMIT_GB: z.number().optional().default(50), // Storage limit in GB for pro tier users STRIPE_TEAM_PRICE_ID: z.string().min(1).optional(), // Stripe price ID for team tier TEAM_TIER_COST_LIMIT: z.number().optional(), // Cost limit for team tier users TEAM_STORAGE_LIMIT_GB: z.number().optional().default(500), // Storage limit in GB for team tier organizations (pooled) STRIPE_ENTERPRISE_PRICE_ID: z.string().min(1).optional(), // Stripe price ID for enterprise tier ENTERPRISE_TIER_COST_LIMIT: z.number().optional(), // Cost limit for enterprise tier users ENTERPRISE_STORAGE_LIMIT_GB: z.number().optional().default(500), // Default storage limit in GB for enterprise tier (can be overridden per org) BILLING_ENABLED: z.boolean().optional(), // Enable billing enforcement and usage tracking OVERAGE_THRESHOLD_DOLLARS: z.number().optional().default(50), // Dollar threshold for incremental overage billing (default: $50) // Email & Communication EMAIL_VERIFICATION_ENABLED: z.boolean().optional(), // Enable email verification for user registration and login (defaults to false) RESEND_API_KEY: z.string().min(1).optional(), // Resend API key for transactional emails FROM_EMAIL_ADDRESS: z.string().min(1).optional(), // Complete from address (e.g., "Sim " or "noreply@domain.com") PERSONAL_EMAIL_FROM: z.string().min(1).optional(), // From address for personalized emails EMAIL_DOMAIN: z.string().min(1).optional(), // Domain for sending emails (fallback when FROM_EMAIL_ADDRESS not set) AZURE_ACS_CONNECTION_STRING: z.string().optional(), // Azure Communication Services connection string // SMS & Messaging TWILIO_ACCOUNT_SID: z.string().min(1).optional(), // Twilio Account SID for SMS sending TWILIO_AUTH_TOKEN: z.string().min(1).optional(), // Twilio Auth Token for API authentication TWILIO_PHONE_NUMBER: z.string().min(1).optional(), // Twilio phone number for sending SMS // AI/LLM Provider API Keys OPENAI_API_KEY: z.string().min(1).optional(), // Primary OpenAI API key OPENAI_API_KEY_1: z.string().min(1).optional(), // Additional OpenAI API key for load balancing OPENAI_API_KEY_2: z.string().min(1).optional(), // Additional OpenAI API key for load balancing OPENAI_API_KEY_3: z.string().min(1).optional(), // Additional OpenAI API key for load balancing MISTRAL_API_KEY: z.string().min(1).optional(), // Mistral AI API key ANTHROPIC_API_KEY_1: z.string().min(1).optional(), // Primary Anthropic Claude API key ANTHROPIC_API_KEY_2: z.string().min(1).optional(), // Additional Anthropic API key for load balancing ANTHROPIC_API_KEY_3: z.string().min(1).optional(), // Additional Anthropic API key for load balancing GEMINI_API_KEY_1: z.string().min(1).optional(), // Primary Gemini API key GEMINI_API_KEY_2: z.string().min(1).optional(), // Additional Gemini API key for load balancing GEMINI_API_KEY_3: z.string().min(1).optional(), // Additional Gemini API key for load balancing OLLAMA_URL: z.string().url().optional(), // Ollama local LLM server URL VLLM_BASE_URL: z.string().url().optional(), // vLLM self-hosted base URL (OpenAI-compatible) VLLM_API_KEY: z.string().optional(), // Optional bearer token for vLLM ELEVENLABS_API_KEY: z.string().min(1).optional(), // ElevenLabs API key for text-to-speech in deployed chat SERPER_API_KEY: z.string().min(1).optional(), // Serper API key for online search EXA_API_KEY: z.string().min(1).optional(), // Exa AI API key for enhanced online search BLACKLISTED_PROVIDERS: z.string().optional(), // Comma-separated provider IDs to hide (e.g., "openai,anthropic") BLACKLISTED_MODELS: z.string().optional(), // Comma-separated model names/prefixes to hide (e.g., "gpt-4,claude-*") // Azure Configuration - Shared credentials with feature-specific models AZURE_OPENAI_ENDPOINT: z.string().url().optional(), // Shared Azure OpenAI service endpoint AZURE_OPENAI_API_VERSION: z.string().optional(), // Shared Azure OpenAI API version AZURE_OPENAI_API_KEY: z.string().min(1).optional(), // Shared Azure OpenAI API key AZURE_ANTHROPIC_ENDPOINT: z.string().url().optional(), // Azure Anthropic service endpoint AZURE_ANTHROPIC_API_KEY: z.string().min(1).optional(), // Azure Anthropic API key AZURE_ANTHROPIC_API_VERSION: z.string().min(1).optional(), // Azure Anthropic API version (e.g. 2023-06-01) KB_OPENAI_MODEL_NAME: z.string().optional(), // Knowledge base OpenAI model name (works with both regular OpenAI and Azure OpenAI) WAND_OPENAI_MODEL_NAME: z.string().optional(), // Wand generation OpenAI model name (works with both regular OpenAI and Azure OpenAI) OCR_AZURE_ENDPOINT: z.string().url().optional(), // Azure Mistral OCR service endpoint OCR_AZURE_MODEL_NAME: z.string().optional(), // Azure Mistral OCR model name for document processing OCR_AZURE_API_KEY: z.string().min(1).optional(), // Azure Mistral OCR API key // Vertex AI Configuration VERTEX_PROJECT: z.string().optional(), // Google Cloud project ID for Vertex AI VERTEX_LOCATION: z.string().optional(), // Google Cloud location/region for Vertex AI (defaults to us-central1) // Monitoring & Analytics TELEMETRY_ENDPOINT: z.string().url().optional(), // Custom telemetry/analytics endpoint COST_MULTIPLIER: z.number().optional(), // Multiplier for cost calculations LOG_LEVEL: z.enum(['DEBUG', 'INFO', 'WARN', 'ERROR']).optional(), // Minimum log level to display (defaults to ERROR in production, DEBUG in development) DRIZZLE_ODS_API_KEY: z.string().min(1).optional(), // OneDollarStats API key for analytics tracking // External Services BROWSERBASE_API_KEY: z.string().min(1).optional(), // Browserbase API key for browser automation BROWSERBASE_PROJECT_ID: z.string().min(1).optional(), // Browserbase project ID GITHUB_TOKEN: z.string().optional(), // GitHub personal access token for API access // Admin API ADMIN_API_KEY: z.string().min(32).optional(), // Admin API key for self-hosted GitOps access (generate with: openssl rand -hex 32) // Infrastructure & Deployment NEXT_RUNTIME: z.string().optional(), // Next.js runtime environment DOCKER_BUILD: z.boolean().optional(), // Flag indicating Docker build environment // Background Jobs & Scheduling TRIGGER_PROJECT_ID: z.string().optional(), // Trigger.dev project ID TRIGGER_SECRET_KEY: z.string().min(1).optional(), // Trigger.dev secret key for background jobs TRIGGER_DEV_ENABLED: z.boolean().optional(), // Toggle to enable/disable Trigger.dev for async jobs CRON_SECRET: z.string().optional(), // Secret for authenticating cron job requests JOB_RETENTION_DAYS: z.string().optional().default('1'), // Days to retain job logs/data // Cloud Storage - AWS S3 AWS_REGION: z.string().optional(), // AWS region for S3 buckets AWS_ACCESS_KEY_ID: z.string().optional(), // AWS access key ID AWS_SECRET_ACCESS_KEY: z.string().optional(), // AWS secret access key S3_BUCKET_NAME: z.string().optional(), // S3 bucket for general file storage S3_LOGS_BUCKET_NAME: z.string().optional(), // S3 bucket for storing logs S3_KB_BUCKET_NAME: z.string().optional(), // S3 bucket for knowledge base files S3_EXECUTION_FILES_BUCKET_NAME: z.string().optional(), // S3 bucket for workflow execution files S3_CHAT_BUCKET_NAME: z.string().optional(), // S3 bucket for chat logos S3_COPILOT_BUCKET_NAME: z.string().optional(), // S3 bucket for copilot files S3_PROFILE_PICTURES_BUCKET_NAME: z.string().optional(), // S3 bucket for profile pictures S3_OG_IMAGES_BUCKET_NAME: z.string().optional(), // S3 bucket for OpenGraph images // Cloud Storage - Azure Blob AZURE_ACCOUNT_NAME: z.string().optional(), // Azure storage account name AZURE_ACCOUNT_KEY: z.string().optional(), // Azure storage account key AZURE_CONNECTION_STRING: z.string().optional(), // Azure storage connection string AZURE_STORAGE_CONTAINER_NAME: z.string().optional(), // Azure container for general files AZURE_STORAGE_KB_CONTAINER_NAME: z.string().optional(), // Azure container for knowledge base files AZURE_STORAGE_EXECUTION_FILES_CONTAINER_NAME: z.string().optional(), // Azure container for workflow execution files AZURE_STORAGE_CHAT_CONTAINER_NAME: z.string().optional(), // Azure container for chat logos AZURE_STORAGE_COPILOT_CONTAINER_NAME: z.string().optional(), // Azure container for copilot files AZURE_STORAGE_PROFILE_PICTURES_CONTAINER_NAME: z.string().optional(), // Azure container for profile pictures AZURE_STORAGE_OG_IMAGES_CONTAINER_NAME: z.string().optional(), // Azure container for OpenGraph images // Data Retention FREE_PLAN_LOG_RETENTION_DAYS: z.string().optional(), // Log retention days for free plan users // Rate Limiting Configuration RATE_LIMIT_WINDOW_MS: z.string().optional().default('60000'), // Rate limit window duration in milliseconds (default: 1 minute) MANUAL_EXECUTION_LIMIT: z.string().optional().default('999999'),// Manual execution bypass value (effectively unlimited) RATE_LIMIT_FREE_SYNC: z.string().optional().default('50'), // Free tier sync API executions per minute RATE_LIMIT_FREE_ASYNC: z.string().optional().default('200'), // Free tier async API executions per minute RATE_LIMIT_PRO_SYNC: z.string().optional().default('150'), // Pro tier sync API executions per minute RATE_LIMIT_PRO_ASYNC: z.string().optional().default('1000'), // Pro tier async API executions per minute RATE_LIMIT_TEAM_SYNC: z.string().optional().default('300'), // Team tier sync API executions per minute RATE_LIMIT_TEAM_ASYNC: z.string().optional().default('2500'), // Team tier async API executions per minute RATE_LIMIT_ENTERPRISE_SYNC: z.string().optional().default('600'), // Enterprise tier sync API executions per minute RATE_LIMIT_ENTERPRISE_ASYNC: z.string().optional().default('5000'), // Enterprise tier async API executions per minute // Timeout Configuration EXECUTION_TIMEOUT_FREE: z.string().optional().default('300'), // 5 minutes EXECUTION_TIMEOUT_PRO: z.string().optional().default('3000'), // 50 minutes EXECUTION_TIMEOUT_TEAM: z.string().optional().default('3000'), // 50 minutes EXECUTION_TIMEOUT_ENTERPRISE: z.string().optional().default('3000'), // 50 minutes EXECUTION_TIMEOUT_ASYNC_FREE: z.string().optional().default('5400'), // 90 minutes EXECUTION_TIMEOUT_ASYNC_PRO: z.string().optional().default('5400'), // 90 minutes EXECUTION_TIMEOUT_ASYNC_TEAM: z.string().optional().default('5400'), // 90 minutes EXECUTION_TIMEOUT_ASYNC_ENTERPRISE: z.string().optional().default('5400'), // 90 minutes // Isolated-VM Worker Pool Configuration IVM_POOL_SIZE: z.string().optional().default('4'), // Max worker processes in pool IVM_MAX_CONCURRENT: z.string().optional().default('10000'), // Max concurrent executions globally IVM_MAX_PER_WORKER: z.string().optional().default('2500'), // Max concurrent executions per worker IVM_WORKER_IDLE_TIMEOUT_MS: z.string().optional().default('60000'), // Worker idle cleanup timeout (ms) IVM_MAX_QUEUE_SIZE: z.string().optional().default('10000'), // Max pending queued executions in memory IVM_MAX_FETCH_RESPONSE_BYTES: z.string().optional().default('8388608'),// Max bytes read from sandbox fetch responses IVM_MAX_FETCH_RESPONSE_CHARS: z.string().optional().default('4000000'),// Max chars returned to sandbox from fetch body IVM_MAX_FETCH_OPTIONS_JSON_CHARS: z.string().optional().default('262144'), // Max JSON payload size for sandbox fetch options IVM_MAX_FETCH_URL_LENGTH: z.string().optional().default('8192'), // Max URL length accepted by sandbox fetch IVM_MAX_STDOUT_CHARS: z.string().optional().default('200000'), // Max captured stdout characters per execution IVM_MAX_ACTIVE_PER_OWNER: z.string().optional().default('200'), // Max active executions per owner (per process) IVM_MAX_QUEUED_PER_OWNER: z.string().optional().default('2000'), // Max queued executions per owner (per process) IVM_MAX_OWNER_WEIGHT: z.string().optional().default('5'), // Max accepted weight for weighted owner scheduling IVM_DISTRIBUTED_MAX_INFLIGHT_PER_OWNER:z.string().optional().default('2200'), // Max owner in-flight leases across replicas IVM_DISTRIBUTED_LEASE_MIN_TTL_MS: z.string().optional().default('120000'), // Min TTL for distributed in-flight leases (ms) IVM_QUEUE_TIMEOUT_MS: z.string().optional().default('300000'), // Max queue wait before rejection (ms) // Knowledge Base Processing Configuration - Shared across all processing methods KB_CONFIG_MAX_DURATION: z.number().optional().default(600), // Max processing duration in seconds (10 minutes) KB_CONFIG_MAX_ATTEMPTS: z.number().optional().default(3), // Max retry attempts KB_CONFIG_RETRY_FACTOR: z.number().optional().default(2), // Retry backoff factor KB_CONFIG_MIN_TIMEOUT: z.number().optional().default(1000), // Min timeout in ms KB_CONFIG_MAX_TIMEOUT: z.number().optional().default(10000), // Max timeout in ms KB_CONFIG_CONCURRENCY_LIMIT: z.number().optional().default(50), // Concurrent embedding API calls KB_CONFIG_BATCH_SIZE: z.number().optional().default(2000), // Chunks to process per embedding batch KB_CONFIG_DELAY_BETWEEN_BATCHES: z.number().optional().default(0), // Delay between batches in ms (0 for max speed) KB_CONFIG_DELAY_BETWEEN_DOCUMENTS: z.number().optional().default(50), // Delay between documents in ms KB_CONFIG_CHUNK_CONCURRENCY: z.number().optional().default(10), // Concurrent PDF chunk OCR processing // Real-time Communication SOCKET_SERVER_URL: z.string().url().optional(), // WebSocket server URL for real-time features SOCKET_PORT: z.number().optional(), // Port for WebSocket server PORT: z.number().optional(), // Main application port ALLOWED_ORIGINS: z.string().optional(), // CORS allowed origins // OAuth Integration Credentials - All optional, enables third-party integrations GOOGLE_CLIENT_ID: z.string().optional(), // Google OAuth client ID for Google services GOOGLE_CLIENT_SECRET: z.string().optional(), // Google OAuth client secret GITHUB_CLIENT_ID: z.string().optional(), // GitHub OAuth client ID for GitHub integration GITHUB_CLIENT_SECRET: z.string().optional(), // GitHub OAuth client secret GITHUB_REPO_CLIENT_ID: z.string().optional(), // GitHub OAuth client ID for repo access GITHUB_REPO_CLIENT_SECRET: z.string().optional(), // GitHub OAuth client secret for repo access X_CLIENT_ID: z.string().optional(), // X (Twitter) OAuth client ID X_CLIENT_SECRET: z.string().optional(), // X (Twitter) OAuth client secret CONFLUENCE_CLIENT_ID: z.string().optional(), // Atlassian Confluence OAuth client ID CONFLUENCE_CLIENT_SECRET: z.string().optional(), // Atlassian Confluence OAuth client secret JIRA_CLIENT_ID: z.string().optional(), // Atlassian Jira OAuth client ID JIRA_CLIENT_SECRET: z.string().optional(), // Atlassian Jira OAuth client secret ASANA_CLIENT_ID: z.string().optional(), // Asana OAuth client ID ASANA_CLIENT_SECRET: z.string().optional(), // Asana OAuth client secret AIRTABLE_CLIENT_ID: z.string().optional(), // Airtable OAuth client ID AIRTABLE_CLIENT_SECRET: z.string().optional(), // Airtable OAuth client secret APOLLO_API_KEY: z.string().optional(), // Apollo API key (optional system-wide config) SUPABASE_CLIENT_ID: z.string().optional(), // Supabase OAuth client ID SUPABASE_CLIENT_SECRET: z.string().optional(), // Supabase OAuth client secret NOTION_CLIENT_ID: z.string().optional(), // Notion OAuth client ID NOTION_CLIENT_SECRET: z.string().optional(), // Notion OAuth client secret DISCORD_CLIENT_ID: z.string().optional(), // Discord OAuth client ID DISCORD_CLIENT_SECRET: z.string().optional(), // Discord OAuth client secret MICROSOFT_CLIENT_ID: z.string().optional(), // Microsoft OAuth client ID for Office 365/Teams MICROSOFT_CLIENT_SECRET: z.string().optional(), // Microsoft OAuth client secret HUBSPOT_CLIENT_ID: z.string().optional(), // HubSpot OAuth client ID HUBSPOT_CLIENT_SECRET: z.string().optional(), // HubSpot OAuth client secret SALESFORCE_CLIENT_ID: z.string().optional(), // Salesforce OAuth client ID SALESFORCE_CLIENT_SECRET: z.string().optional(), // Salesforce OAuth client secret WEALTHBOX_CLIENT_ID: z.string().optional(), // WealthBox OAuth client ID WEALTHBOX_CLIENT_SECRET: z.string().optional(), // WealthBox OAuth client secret PIPEDRIVE_CLIENT_ID: z.string().optional(), // Pipedrive OAuth client ID PIPEDRIVE_CLIENT_SECRET: z.string().optional(), // Pipedrive OAuth client secret LINEAR_CLIENT_ID: z.string().optional(), // Linear OAuth client ID LINEAR_CLIENT_SECRET: z.string().optional(), // Linear OAuth client secret DROPBOX_CLIENT_ID: z.string().optional(), // Dropbox OAuth client ID DROPBOX_CLIENT_SECRET: z.string().optional(), // Dropbox OAuth client secret SLACK_CLIENT_ID: z.string().optional(), // Slack OAuth client ID SLACK_CLIENT_SECRET: z.string().optional(), // Slack OAuth client secret REDDIT_CLIENT_ID: z.string().optional(), // Reddit OAuth client ID REDDIT_CLIENT_SECRET: z.string().optional(), // Reddit OAuth client secret WEBFLOW_CLIENT_ID: z.string().optional(), // Webflow OAuth client ID WEBFLOW_CLIENT_SECRET: z.string().optional(), // Webflow OAuth client secret TRELLO_API_KEY: z.string().optional(), // Trello API Key LINKEDIN_CLIENT_ID: z.string().optional(), // LinkedIn OAuth client ID LINKEDIN_CLIENT_SECRET: z.string().optional(), // LinkedIn OAuth client secret SHOPIFY_CLIENT_ID: z.string().optional(), // Shopify OAuth client ID SHOPIFY_CLIENT_SECRET: z.string().optional(), // Shopify OAuth client secret ZOOM_CLIENT_ID: z.string().optional(), // Zoom OAuth client ID ZOOM_CLIENT_SECRET: z.string().optional(), // Zoom OAuth client secret WORDPRESS_CLIENT_ID: z.string().optional(), // WordPress.com OAuth client ID WORDPRESS_CLIENT_SECRET: z.string().optional(), // WordPress.com OAuth client secret SPOTIFY_CLIENT_ID: z.string().optional(), // Spotify OAuth client ID SPOTIFY_CLIENT_SECRET: z.string().optional(), // Spotify OAuth client secret CALCOM_CLIENT_ID: z.string().optional(), // Cal.com OAuth client ID // E2B Remote Code Execution E2B_ENABLED: z.string().optional(), // Enable E2B remote code execution E2B_API_KEY: z.string().optional(), // E2B API key for sandbox creation // Credential Sets (Email Polling) - for self-hosted deployments CREDENTIAL_SETS_ENABLED: z.boolean().optional(), // Enable credential sets on self-hosted (bypasses plan requirements) // Access Control (Permission Groups) - for self-hosted deployments ACCESS_CONTROL_ENABLED: z.boolean().optional(), // Enable access control on self-hosted (bypasses plan requirements) // Organizations - for self-hosted deployments ORGANIZATIONS_ENABLED: z.boolean().optional(), // Enable organizations on self-hosted (bypasses plan requirements) // Invitations - for self-hosted deployments DISABLE_INVITATIONS: z.boolean().optional(), // Disable workspace invitations globally (for self-hosted deployments) // Development Tools REACT_GRAB_ENABLED: z.boolean().optional(), // Enable React Grab for UI element debugging in Cursor/AI agents (dev only) REACT_SCAN_ENABLED: z.boolean().optional(), // Enable React Scan for performance debugging (dev only) // SSO Configuration (for script-based registration) SSO_ENABLED: z.boolean().optional(), // Enable SSO functionality SSO_PROVIDER_TYPE: z.enum(['oidc', 'saml']).optional(), // [REQUIRED] SSO provider type SSO_PROVIDER_ID: z.string().optional(), // [REQUIRED] SSO provider ID SSO_ISSUER: z.string().optional(), // [REQUIRED] SSO issuer URL SSO_DOMAIN: z.string().optional(), // [REQUIRED] SSO email domain SSO_USER_EMAIL: z.string().optional(), // [REQUIRED] User email for SSO registration SSO_ORGANIZATION_ID: z.string().optional(), // Organization ID for SSO registration (optional) // SSO Mapping Configuration (optional - sensible defaults provided) SSO_MAPPING_ID: z.string().optional(), // Custom ID claim mapping (default: sub for OIDC, nameidentifier for SAML) SSO_MAPPING_EMAIL: z.string().optional(), // Custom email claim mapping (default: email for OIDC, emailaddress for SAML) SSO_MAPPING_NAME: z.string().optional(), // Custom name claim mapping (default: name for both) SSO_MAPPING_IMAGE: z.string().optional(), // Custom image claim mapping (default: picture for OIDC) // SSO OIDC Configuration SSO_OIDC_CLIENT_ID: z.string().optional(), // [REQUIRED for OIDC] OIDC client ID SSO_OIDC_CLIENT_SECRET: z.string().optional(), // [REQUIRED for OIDC] OIDC client secret SSO_OIDC_SCOPES: z.string().optional(), // OIDC scopes (default: openid,profile,email) SSO_OIDC_PKCE: z.string().optional(), // Enable PKCE (default: true) SSO_OIDC_AUTHORIZATION_ENDPOINT: z.string().optional(), // OIDC authorization endpoint (optional, uses discovery) SSO_OIDC_TOKEN_ENDPOINT: z.string().optional(), // OIDC token endpoint (optional, uses discovery) SSO_OIDC_USERINFO_ENDPOINT: z.string().optional(), // OIDC userinfo endpoint (optional, uses discovery) SSO_OIDC_JWKS_ENDPOINT: z.string().optional(), // OIDC JWKS endpoint (optional, uses discovery) SSO_OIDC_DISCOVERY_ENDPOINT: z.string().optional(), // OIDC discovery endpoint (default: {issuer}/.well-known/openid-configuration) // SSO SAML Configuration SSO_SAML_ENTRY_POINT: z.string().optional(), // [REQUIRED for SAML] SAML IdP SSO URL SSO_SAML_CERT: z.string().optional(), // [REQUIRED for SAML] SAML IdP certificate SSO_SAML_CALLBACK_URL: z.string().optional(), // SAML callback URL (default: {issuer}/callback) SSO_SAML_SP_METADATA: z.string().optional(), // SAML SP metadata XML (auto-generated if not provided) SSO_SAML_IDP_METADATA: z.string().optional(), // SAML IdP metadata XML (optional) SSO_SAML_AUDIENCE: z.string().optional(), // SAML audience restriction (default: issuer URL) SSO_SAML_WANT_ASSERTIONS_SIGNED: z.string().optional(), // Require signed SAML assertions (default: false) SSO_SAML_SIGNATURE_ALGORITHM: z.string().optional(), // SAML signature algorithm (optional) SSO_SAML_DIGEST_ALGORITHM: z.string().optional(), // SAML digest algorithm (optional) SSO_SAML_IDENTIFIER_FORMAT: z.string().optional(), // SAML identifier format (optional) }, client: { // Core Application URLs - Required for frontend functionality NEXT_PUBLIC_APP_URL: z.string().url(), // Base URL of the application (e.g., https://www.sim.ai) // Client-side Services NEXT_PUBLIC_SOCKET_URL: z.string().url().optional(), // WebSocket server URL for real-time features // Billing NEXT_PUBLIC_BILLING_ENABLED: z.boolean().optional(), // Enable billing enforcement and usage tracking (client-side) // Analytics & Tracking NEXT_PUBLIC_POSTHOG_ENABLED: z.boolean().optional(), // Enable PostHog analytics (client-side) NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(), // PostHog project API key // UI Branding & Whitelabeling NEXT_PUBLIC_BRAND_NAME: z.string().optional(), // Custom brand name (defaults to "Sim") NEXT_PUBLIC_BRAND_LOGO_URL: z.string().url().optional(), // Custom logo URL NEXT_PUBLIC_BRAND_FAVICON_URL: z.string().url().optional(), // Custom favicon URL NEXT_PUBLIC_CUSTOM_CSS_URL: z.string().url().optional(), // Custom CSS stylesheet URL NEXT_PUBLIC_SUPPORT_EMAIL: z.string().email().optional(), // Custom support email NEXT_PUBLIC_E2B_ENABLED: z.string().optional(), NEXT_PUBLIC_COPILOT_TRAINING_ENABLED: z.string().optional(), NEXT_PUBLIC_ENABLE_PLAYGROUND: z.string().optional(), // Enable component playground at /playground NEXT_PUBLIC_DOCUMENTATION_URL: z.string().url().optional(), // Custom documentation URL NEXT_PUBLIC_TERMS_URL: z.string().url().optional(), // Custom terms of service URL NEXT_PUBLIC_PRIVACY_URL: z.string().url().optional(), // Custom privacy policy URL // Theme Customization NEXT_PUBLIC_BRAND_PRIMARY_COLOR: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(), // Primary brand color (hex format, e.g., "#701ffc") NEXT_PUBLIC_BRAND_PRIMARY_HOVER_COLOR: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(), // Primary brand hover state (hex format) NEXT_PUBLIC_BRAND_ACCENT_COLOR: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(), // Accent brand color (hex format) NEXT_PUBLIC_BRAND_ACCENT_HOVER_COLOR: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(), // Accent brand hover state (hex format) NEXT_PUBLIC_BRAND_BACKGROUND_COLOR: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(), // Brand background color (hex format) // Feature Flags NEXT_PUBLIC_SSO_ENABLED: z.boolean().optional(), // Enable SSO login UI components NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED: z.boolean().optional(), // Enable credential sets (email polling) on self-hosted NEXT_PUBLIC_ACCESS_CONTROL_ENABLED: z.boolean().optional(), // Enable access control (permission groups) on self-hosted NEXT_PUBLIC_ORGANIZATIONS_ENABLED: z.boolean().optional(), // Enable organizations on self-hosted (bypasses plan requirements) NEXT_PUBLIC_DISABLE_INVITATIONS: z.boolean().optional(), // Disable workspace invitations globally (for self-hosted deployments) NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED: z.boolean().optional().default(true), // Control visibility of email/password login forms }, // Variables available on both server and client shared: { NODE_ENV: z.enum(['development', 'test', 'production']).optional(), // Runtime environment NEXT_TELEMETRY_DISABLED: z.string().optional(), // Disable Next.js telemetry collection }, experimental__runtimeEnv: { NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL, NEXT_PUBLIC_BILLING_ENABLED: process.env.NEXT_PUBLIC_BILLING_ENABLED, NEXT_PUBLIC_SOCKET_URL: process.env.NEXT_PUBLIC_SOCKET_URL, NEXT_PUBLIC_BRAND_NAME: process.env.NEXT_PUBLIC_BRAND_NAME, NEXT_PUBLIC_BRAND_LOGO_URL: process.env.NEXT_PUBLIC_BRAND_LOGO_URL, NEXT_PUBLIC_BRAND_FAVICON_URL: process.env.NEXT_PUBLIC_BRAND_FAVICON_URL, NEXT_PUBLIC_CUSTOM_CSS_URL: process.env.NEXT_PUBLIC_CUSTOM_CSS_URL, NEXT_PUBLIC_SUPPORT_EMAIL: process.env.NEXT_PUBLIC_SUPPORT_EMAIL, NEXT_PUBLIC_DOCUMENTATION_URL: process.env.NEXT_PUBLIC_DOCUMENTATION_URL, NEXT_PUBLIC_TERMS_URL: process.env.NEXT_PUBLIC_TERMS_URL, NEXT_PUBLIC_PRIVACY_URL: process.env.NEXT_PUBLIC_PRIVACY_URL, NEXT_PUBLIC_BRAND_PRIMARY_COLOR: process.env.NEXT_PUBLIC_BRAND_PRIMARY_COLOR, NEXT_PUBLIC_BRAND_PRIMARY_HOVER_COLOR: process.env.NEXT_PUBLIC_BRAND_PRIMARY_HOVER_COLOR, NEXT_PUBLIC_BRAND_ACCENT_COLOR: process.env.NEXT_PUBLIC_BRAND_ACCENT_COLOR, NEXT_PUBLIC_BRAND_ACCENT_HOVER_COLOR: process.env.NEXT_PUBLIC_BRAND_ACCENT_HOVER_COLOR, NEXT_PUBLIC_BRAND_BACKGROUND_COLOR: process.env.NEXT_PUBLIC_BRAND_BACKGROUND_COLOR, NEXT_PUBLIC_SSO_ENABLED: process.env.NEXT_PUBLIC_SSO_ENABLED, NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED: process.env.NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED, NEXT_PUBLIC_ACCESS_CONTROL_ENABLED: process.env.NEXT_PUBLIC_ACCESS_CONTROL_ENABLED, NEXT_PUBLIC_ORGANIZATIONS_ENABLED: process.env.NEXT_PUBLIC_ORGANIZATIONS_ENABLED, NEXT_PUBLIC_DISABLE_INVITATIONS: process.env.NEXT_PUBLIC_DISABLE_INVITATIONS, NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED: process.env.NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED, NEXT_PUBLIC_E2B_ENABLED: process.env.NEXT_PUBLIC_E2B_ENABLED, NEXT_PUBLIC_COPILOT_TRAINING_ENABLED: process.env.NEXT_PUBLIC_COPILOT_TRAINING_ENABLED, NEXT_PUBLIC_ENABLE_PLAYGROUND: process.env.NEXT_PUBLIC_ENABLE_PLAYGROUND, NEXT_PUBLIC_POSTHOG_ENABLED: process.env.NEXT_PUBLIC_POSTHOG_ENABLED, NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, NODE_ENV: process.env.NODE_ENV, NEXT_TELEMETRY_DISABLED: process.env.NEXT_TELEMETRY_DISABLED, }, }) // Need this utility because t3-env is returning string for boolean values. export const isTruthy = (value: string | boolean | number | undefined) => typeof value === 'string' ? value.toLowerCase() === 'true' || value === '1' : Boolean(value) // Utility to check if a value is explicitly false (defaults to false only if explicitly set) export const isFalsy = (value: string | boolean | number | undefined) => typeof value === 'string' ? value.toLowerCase() === 'false' || value === '0' : value === false export { getEnv }