Files
Ruslan KonviserandClaude Opus 5.5 ba62a02001 fix(billing): scope Stripe linking, paywall and billing pages to this deployment's product (#10331)
Product-scoped (hosted plan only) Stripe webhook linking, signup paywall, lazy tenant link and /billing routes; checkout-session proof at register/onboarding; BILLING_PRODUCT, BILLING_SIGNUP_PAYWALL and BILLING_WEBHOOK_LINKING (default off); 402 payment_method_required on a paid upgrade without a card. See the PR description for details.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:34:26 +02:00

971 lines
38 KiB
Bash

# The name of the application.
APP_NAME="Gauzy"
# The URL for the application logo.
APP_LOGO="http://localhost:4200/assets/images/logos/logo_Gauzy.png"
# The signature or tagline for the application.
APP_SIGNATURE="Gauzy"
# The link to the application.
APP_LINK="http://localhost:4200"
# The URL for an email confirmation in the application.
APP_EMAIL_CONFIRMATION_URL="http://localhost:4200/#/auth/confirm-email"
# The URL for magic sign-in in the application.
APP_MAGIC_SIGN_URL="http://localhost:4200/#/auth/magic-sign-in"
# Set true if running inside the Docker container
IS_DOCKER=false
# Deployed release version (git tag, e.g. v111.2.10) and full commit SHA, shown in the
# web UI footer and returned by GET /api/version. Normally injected automatically at
# Docker build time (build-args, see .deploy/*/Dockerfile); leave empty for source runs.
GAUZY_APP_VERSION=
GAUZY_APP_COMMIT=
# Format: https://hook.{region}.make.com/{webhook-id}
GAUZY_MAKE_WEBHOOK_URL=
# Make.com Platforms integration
GAUZY_MAKE_API_URL="https://hook.us2.make.com/api/v2"
GAUZY_MAKE_BASE_URL="https://www.make.com"
GAUZY_MAKE_CLIENT_ID=
GAUZY_MAKE_CLIENT_SECRET=
GAUZY_MAKE_REDIRECT_URL="${API_BASE_URL}/api/integration/make-com/oauth/callback"
GAUZY_MAKE_POST_INSTALL_URL="${CLIENT_BASE_URL}/#/pages/integrations/make"
GAUZY_MAKE_DEFAULT_SCOPES="offline_access"
# ActivePieces platforms integration
ACTIVEPIECES_BASE_URL="https://cloud.activepieces.com"
GAUZY_ACTIVEPIECES_API_KEY=
#SIM platform integration
SIM_DEFAULT_BASE_URL="https://www.sim.ai"
GAUZY_SIM_API_KEY=
# Set true if running as a Demo
DEMO=false
# DO (DIGITALOCEAN), AWS, AZURE, CIVO, CW (COREWEAVE), HEROKU, LINODE, LOCAL, OVH, SCALEWAY, VULTR, etc
CLOUD_PROVIDER=
ALLOW_SUPER_ADMIN_ROLE=true
# Set to Gauzy API base URL
API_BASE_URL=http://localhost:3000
# Set to Gauzy UI base URL
CLIENT_BASE_URL=http://localhost:4200
# Set to Website Platform
PLATFORM_WEBSITE_URL=https://gauzy.co
PLATFORM_WEBSITE_DOWNLOAD_URL=https://gauzy.co/downloads
# DB_ORM: typeorm | mikro-orm
DB_ORM=typeorm
# DB_TYPE: sqlite | postgres | better-sqlite3 | mysql
DB_TYPE=better-sqlite3
# DB Connection Parameters
# DB_HOST=localhost
## DB Port. The default for PostgreSQL - 5432, for MySQL - 3306
# DB_PORT=5432
# DB_NAME=gauzy
## DB Username. The default for PostgreSQL is 'postgres', for MySQL it's 'root'
# DB_USER=postgres
# DB_PASS=root
# DB_LOGGING=all
# DB_POOL_SIZE=40
# DB_POOL_SIZE_KNEX=10
# DB_CONNECTION_TIMEOUT=5000
# DB_IDLE_TIMEOUT=10000
# DB_SLOW_QUERY_LOGGING_TIMEOUT=10000
# DB_SSL_MODE=false
## If you want to use SSL and set DB_SSL_MODE=true, set the following environment variable
## with base64 encoded SSL certificate for DB
# DB_CA_CERT=
# Configuration for Worker Queue and Scheduler
WORKER_QUEUE_ENABLED=false
WORKER_SCHEDULER_ENABLED=false
WORKER_DEFAULT_QUEUE=gauzy_worker_default_queue
WORKER_TIMEZONE=UTC
# Redis Connection Parameters
REDIS_ENABLED=false
REDIS_HOST=
REDIS_PASSWORD=
REDIS_PORT=
REDIS_USER=
REDIS_TLS=false
# redis[s]://[[username][:password]@][host][:port][/db-number]
REDIS_URL=redis://localhost:6379
# ============================================================================
# SECURITY: Authentication & session secrets
# Set each of these to a strong, UNIQUE, random value before deploying, e.g.:
# openssl rand -hex 64
# The API refuses to start in production (NODE_ENV=production and DEMO != true)
# while any of these is empty or left at a well-known default value.
# Outside production (and outside DEMO=true) an empty value makes the API use a
# random secret generated for that process only: sign-ins, emailed links and
# sessions then stop working on every restart, and every OTHER process that must
# accept the same tokens (API replicas, `yarn seed`, which signs seeded invite and
# estimate links) cannot verify them. Set explicit values shared by all of them.
# ============================================================================
EXPRESS_SESSION_SECRET=
# JWT Configuration
JWT_SECRET=
JWT_TOKEN_EXPIRATION_TIME=86400
# JWT Refresh Token Configuration
JWT_REFRESH_TOKEN_SECRET=
JWT_REFRESH_TOKEN_EXPIRATION_TIME=86400
# Email Verification Config
JWT_VERIFICATION_TOKEN_SECRET=
JWT_VERIFICATION_TOKEN_EXPIRATION_TIME=86400
# Password Less Authentication Configuration
MAGIC_CODE_EXPIRATION_TIME=600
# Join Request Organization Team Configuration
TEAM_JOIN_REQUEST_EXPIRATION_TIME=86400
# Rate Limiting
THROTTLE_ENABLED=true
THROTTLE_TTL=60000 # 1 minute
THROTTLE_LIMIT=60000
# ============================================================================
# SECURITY: Seeded account credentials
# The FIRST boot against an empty database creates three accounts from these
# variables. Their shipped defaults (admin@ever.co / admin, local.admin@ever.co
# / admin, employee@ever.co / 12345678) are published in this repository, so a
# deployment that leaves the passwords unset is exposed to publicly known
# passwords. The API therefore REFUSES TO SEED in production (NODE_ENV=production
# or a production build, DEMO != true, non-Electron) while any of the three
# *_PASSWORD variables is unset or left at its default; the *_EMAIL variables
# are optional. The `yarn seed:ever` / `yarn seed:all` seeds are refused there
# outright, because they also create fixture accounts with a hard-coded password.
# The check runs only when a seed runs: an existing database (users present) is
# never seeded again, so it is never refused. Demo deployments (DEMO=true) and
# the desktop Gauzy Server are exempt.
# ============================================================================
DEMO_SUPER_ADMIN_EMAIL=
DEMO_SUPER_ADMIN_PASSWORD=
DEMO_ADMIN_EMAIL=
DEMO_ADMIN_PASSWORD=
DEMO_EMPLOYEE_EMAIL=
DEMO_EMPLOYEE_PASSWORD=
# Emergency escape hatch; seeds the well-known defaults anyway. Strongly discouraged.
# ALLOW_INSECURE_SEED_CREDENTIALS=false
# ============================================================================
# SECURITY: Proxy trust & brute-force controls
# TRUST_PROXY is the Express `trust proxy` setting and decides what `req.ip`
# resolves to - which is what the rate limiter counts against. Use the NUMBER OF
# PROXY HOPS in front of the API (1 for a single nginx/ingress, 2 for
# Cloudflare Tunnel -> ingress -> api), or a comma-separated list of trusted proxy
# CIDRs. `true` trusts the whole X-Forwarded-For chain and lets any client pick
# its own rate-limit bucket - do not use it. A hop count trusts whoever connects
# to the API socket to write one X-Forwarded-For entry, so if the API port is
# reachable WITHOUT going through your proxy (e.g. docker-compose publishing
# 3000), use your proxy CIDRs instead.
TRUST_PROXY=1
# Set to true when every request reaches the API through Cloudflare (including
# a Cloudflare Tunnel) and nothing else can reach the origin; the
# CF-Connecting-IP header is client-writable otherwise. Behind Cloudflare with
# this left false, every client shares the proxy's address and therefore ONE
# rate-limit bucket.
THROTTLE_TRUST_CF_CONNECTING_IP=false
# Per-ACCOUNT brute-force control, independent of the client IP. Once this many
# consecutive failed sign-ins for one account have come from at least TWO
# different client addresses, that account is blocked for AUTH_LOCKOUT_SECONDS
# (HTTP 429 with Retry-After). Failures from a single address never block the
# account - the per-address throttle covers that - so one client cannot lock a
# known email out. At most this many checks per account may run concurrently.
# Trade-off: an attacker with several addresses can still block a known account
# for AUTH_LOCKOUT_SECONDS at a time. Set AUTH_MAX_FAILED_ATTEMPTS=0 to switch
# the per-account control off and rely on the per-address throttle alone.
AUTH_MAX_FAILED_ATTEMPTS=10
AUTH_LOCKOUT_SECONDS=900
# CORS Allowed Origins (comma-separated list of trusted origins)
# If not set, all origins (*) are allowed. In production, set this to your trusted domains.
# ALLOWED_ORIGINS=https://app.gauzy.co,https://gauzy.co
# Twitter OAuth Configuration
TWITTER_CLIENT_ID=XXXXXXX
TWITTER_CLIENT_SECRET=XXXXXXX
TWITTER_CALLBACK_URL=http://localhost:3000/api/auth/twitter/callback
# Google OAuth Configuration
GOOGLE_CLIENT_ID=XXXXXXX
GOOGLE_CLIENT_SECRET=XXXXXXX
GOOGLE_CALLBACK_URL=http://localhost:3000/api/auth/google/callback
# Facebook OAuth Configuration
FACEBOOK_CLIENT_ID=XXXXXXX
FACEBOOK_CLIENT_SECRET=XXXXXXX
FACEBOOK_CALLBACK_URL=http://localhost:3000/api/auth/facebook/callback
FACEBOOK_GRAPH_VERSION=v3.0
# Github OAuth App Integration
GAUZY_GITHUB_OAUTH_CLIENT_ID=XXXXXXX
GAUZY_GITHUB_OAUTH_CLIENT_SECRET=XXXXXXX
GAUZY_GITHUB_OAUTH_CALLBACK_URL="http://localhost:3000/api/auth/github/callback"
# Social sign-in by provider access token (POST /api/auth/signin.email.social, /api/auth/signup.link.account).
# Tokens are only accepted when issued to an allowed OAuth client. Gauzy's own apps above (GOOGLE_CLIENT_ID,
# GAUZY_GITHUB_OAUTH_CLIENT_ID/SECRET, FACEBOOK_CLIENT_ID/SECRET) are always allowed; list OTHER first-party
# clients (e.g. Ever Teams) here. Google: comma-separated client ids. GitHub/Facebook: comma-separated
# clientId:clientSecret pairs (the secret is needed to introspect the token). A provider with no client rejects all tokens.
GAUZY_SOCIAL_AUTH_GOOGLE_CLIENT_IDS=
GAUZY_SOCIAL_AUTH_GITHUB_APPS=
GAUZY_SOCIAL_AUTH_FACEBOOK_APPS=
# LinkedIn OAuth Configuration
LINKEDIN_CLIENT_ID=XXXXXXX
LINKEDIN_CLIENT_SECRET=XXXXXXX
LINKEDIN_CALLBACK_URL=http://localhost:3000/api/auth/linkedin/callback
# Microsoft OAuth Configuration
MICROSOFT_GRAPH_API_URL=https://graph.microsoft.com/v1.0
MICROSOFT_AUTHORIZATION_URL=https://login.microsoftonline.com/common/oauth2/v2.0/authorize
MICROSOFT_TOKEN_URL=https://login.microsoftonline.com/common/oauth2/v2.0/token
MICROSOFT_CLIENT_ID=XXXXXXX
MICROSOFT_CLIENT_SECRET=XXXXXXX
MICROSOFT_CALLBACK_URL=http://localhost:3000/api/auth/microsoft/callback
# Github Apps Integration
GAUZY_GITHUB_CLIENT_ID=XXXXXXX
GAUZY_GITHUB_CLIENT_SECRET=XXXXXXX
# Github App Install Integration
GAUZY_GITHUB_APP_NAME=
GAUZY_GITHUB_APP_ID=XXXXXXX
GAUZY_GITHUB_APP_PRIVATE_KEY=
# Github Webhook Configuration
# GAUZY_GITHUB_WEBHOOK_SECRET is REQUIRED whenever the GitHub App integration is enabled: the
# receiver at POST /api/integration/github/webhook verifies GitHub's `x-hub-signature-256` HMAC and
# rejects every delivery (403) when the secret is unset or does not match. Set it to the exact
# "Webhook secret" configured on the GitHub App (Settings -> Developer settings -> GitHub Apps).
GAUZY_GITHUB_WEBHOOK_URL=http://localhost:3000/api/auth/github/webhook
GAUZY_GITHUB_WEBHOOK_SECRET=XXXXXXX
# Github Redirect URL
GAUZY_GITHUB_REDIRECT_URL=http://localhost:3000/api/integration/github/callback
GAUZY_GITHUB_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/github/setup/installation"
GAUZY_GITHUB_API_VERSION="2022-11-28"
# Zapier Apps Integration
GAUZY_ZAPIER_CLIENT_ID=XXXXXXXXX
GAUZY_ZAPIER_CLIENT_SECRET=XXXXXXX
GAUZY_ZAPIER_REDIRECT_URL=http://localhost:3000/api/integration/zapier/oauth/callback
GAUZY_ZAPIER_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/zapier"
# Comma-separated list of domains allowed for OAuth redirects (security feature)
GAUZY_ZAPIER_ALLOWED_DOMAINS=gauzy.co,*.gauzy.co,ever.co,*.ever.co,zapier.com,*.zapier.com,localhost
# Maximum number of OAuth authorization codes to store in memory
GAUZY_ZAPIER_MAX_AUTH_CODES=1000
# Number of server instances (affects auth code cleanup behavior)
GAUZY_ZAPIER_INSTANCE_COUNT=1
FIVERR_CLIENT_ID=XXXXXXX
FIVERR_CLIENT_SECRET=XXXXXXX
AUTH0_CLIENT_ID=XXXXXXX
AUTH0_CLIENT_SECRET=XXXXXXX
AUTH0_DOMAIN=XXXXXXX
# Keycloak OAuth
KEYCLOAK_CLIENT_ID=XXXXXXX
KEYCLOAK_CLIENT_SECRET=XXXXXXX
KEYCLOAK_REALM=
KEYCLOAK_COOKIE_KEY=XXXXXXX
KEYCLOAK_AUTH_SERVER_URL=https://keycloak.example.com/auth
KEYCLOAK_CALLBACK_URL=http://localhost:3000/api/auth/keycloak/callback
INTEGRATED_HUBSTAFF_USER_PASS=hubstaffPassword
# Upwork Integration Config
UPWORK_API_KEY=XXXXXXX
UPWORK_API_SECRET=XXXXXXX
UPWORK_REDIRECT_URL="http://localhost:3000/api/integrations/upwork/callback"
UPWORK_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/upwork"
# Hubstaff Integration Configuration
HUBSTAFF_CLIENT_ID=XXXXXXX
HUBSTAFF_CLIENT_SECRET=XXXXXXX
HUBSTAFF_REDIRECT_URL="http://localhost:3000/api/integration/hubstaff/callback"
HUBSTAFF_POST_INSTALL_URL="http://localhost:4200/#/pages/integrations/hubstaff"
# File System: LOCAL | S3 | WASABI | CLOUDINARY
FILE_PROVIDER=LOCAL
# AWS Config
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
AWS_S3_BUCKET=gauzy
# WASABI Config (optional)
WASABI_ACCESS_KEY_ID=
WASABI_SECRET_ACCESS_KEY=
WASABI_REGION=us-east-1
WASABI_SERVICE_URL=https://s3.wasabisys.com
WASABI_S3_BUCKET=gauzy
WASABI_S3_FORCE_PATH_STYLE=true
# DIGITALOCEAN Spaces Config (optional)
DIGITALOCEAN_ACCESS_KEY_ID=
DIGITALOCEAN_SECRET_ACCESS_KEY=
DIGITALOCEAN_REGION=us-east-1
DIGITALOCEAN_SERVICE_URL=
DIGITALOCEAN_CDN_URL=
DIGITALOCEAN_S3_BUCKET=gauzy
DIGITALOCEAN_S3_FORCE_PATH_STYLE=false
# Cloudinary Config (optional)
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
CLOUDINARY_API_SECURE=true
CLOUDINARY_CDN_URL=https://res.cloudinary.com
# Gauzy AI Endpoints (optional, do not set unless you subscribed to Gauzy AI)
GAUZY_AI_GRAPHQL_ENDPOINT=http://localhost:3005/graphql
GAUZY_AI_REST_ENDPOINT=http://localhost:3005/api
# Gauzy AI Key/Secret pair authentication
GAUZY_AI_API_KEY=
GAUZY_AI_API_SECRET=
# Gauzy Cloud
GAUZY_CLOUD_ENDPOINT=https://api.gauzy.co
GAUZY_CLOUD_APP=https://app.gauzy.co
# SMTP Mail Config
MAIL_FROM_ADDRESS=gauzy@ever.co
MAIL_HOST=smtp.gmail.com
MAIL_PORT=465
MAIL_USERNAME=
MAIL_PASSWORD=
# Sentry Client Key
SENTRY_DSN=
SENTRY_HTTP_TRACING_ENABLED=
SENTRY_POSTGRES_TRACKING_ENABLED=
SENTRY_PROFILING_ENABLED=
SENTRY_TRACES_SAMPLE_RATE=
SENTRY_PROFILE_SAMPLE_RATE=
# Nest log levels that become Sentry events, comma-separated (default: error). Other levels are
# attached to the next event as breadcrumbs. Example: SENTRY_LOG_LEVELS=error,warn
SENTRY_LOG_LEVELS=
# PostHog Configuration
POSTHOG_KEY=
POSTHOG_HOST=
POSTHOG_ENABLED=
POSTHOG_FLUSH_INTERVAL=
# Default Currency
DEFAULT_CURRENCY=USD
# Default Country
DEFAULT_COUNTRY=US
# Google Maps API Key
GOOGLE_MAPS_API_KEY=
# Chatwoot SDK Token
CHATWOOT_SDK_TOKEN=
# Restrict Access to Google Place Autocomplete
GOOGLE_PLACE_AUTOCOMPLETE=false
# Nebular CHAT API key for a map message type (which is required by Google Maps)
CHAT_MESSAGE_GOOGLE_MAP=
# Default Latitude and Longitude
DEFAULT_LATITUDE=
DEFAULT_LONGITUDE=
# Keymetrics settings (optional)
WEB_CONCURRENCY=1
WEB_MEMORY=4096
# Unleash Configuration for Features management (optional)
UNLEASH_APP_NAME=Gauzy
UNLEASH_API_URL=
UNLEASH_INSTANCE_ID=
UNLEASH_REFRESH_INTERVAL=15000
UNLEASH_METRICS_INTERVAL=60000
UNLEASH_API_KEY=
# Defines feature flags and settings related to user authentication methods.
FEATURE_EMAIL_PASSWORD_LOGIN=true
FEATURE_MAGIC_LOGIN=true
FEATURE_GITHUB_LOGIN=true
FEATURE_FACEBOOK_LOGIN=true
FEATURE_GOOGLE_LOGIN=true
FEATURE_TWITTER_LOGIN=true
FEATURE_MICROSOFT_LOGIN=true
FEATURE_LINKEDIN_LOGIN=true
# Features Toggles
FEATURE_DASHBOARD=true
FEATURE_TIME_TRACKING=true
FEATURE_ESTIMATE=true
FEATURE_ESTIMATE_RECEIVED=true
FEATURE_INVOICE=true
FEATURE_INVOICE_RECURRING=true
FEATURE_INVOICE_RECEIVED=true
FEATURE_INCOME=true
FEATURE_EXPENSE=true
FEATURE_PAYMENT=true
FEATURE_PROPOSAL=true
FEATURE_PROPOSAL_TEMPLATE=true
FEATURE_PIPELINE=true
FEATURE_PIPELINE_DEAL=true
FEATURE_DASHBOARD_TASK=true
FEATURE_TEAM_TASK=true
FEATURE_MY_TASK=true
FEATURE_JOB=true
FEATURE_EMPLOYEES=true
FEATURE_EMPLOYEE_TIME_ACTIVITY=true
FEATURE_EMPLOYEE_TIMESHEETS=true
FEATURE_EMPLOYEE_APPOINTMENT=true
FEATURE_EMPLOYEE_APPROVAL=true
FEATURE_EMPLOYEE_APPROVAL_POLICY=true
FEATURE_EMPLOYEE_LEVEL=true
FEATURE_EMPLOYEE_POSITION=true
FEATURE_EMPLOYEE_TIMEOFF=true
FEATURE_EMPLOYEE_RECURRING_EXPENSE=true
FEATURE_EMPLOYEE_CANDIDATE=true
FEATURE_MANAGE_INTERVIEW=true
FEATURE_MANAGE_INVITE=true
FEATURE_ORGANIZATION=true
FEATURE_ORGANIZATION_EQUIPMENT=true
FEATURE_ORGANIZATION_INVENTORY=true
FEATURE_ORGANIZATION_TAG=true
FEATURE_ORGANIZATION_VENDOR=true
FEATURE_ORGANIZATION_PROJECT=true
FEATURE_ORGANIZATION_DEPARTMENT=true
FEATURE_ORGANIZATION_TEAM=true
FEATURE_ORGANIZATION_DOCUMENT=true
FEATURE_ORGANIZATION_EMPLOYMENT_TYPE=true
FEATURE_ORGANIZATION_RECURRING_EXPENSE=true
FEATURE_ORGANIZATION_HELP_CENTER=true
FEATURE_CONTACT=true
FEATURE_GOAL=true
FEATURE_GOAL_REPORT=true
FEATURE_GOAL_SETTING=true
FEATURE_REPORT=true
FEATURE_USER=true
FEATURE_ORGANIZATIONS=true
FEATURE_APP_INTEGRATION=true
FEATURE_SETTING=true
FEATURE_EMAIL_HISTORY=true
FEATURE_EMAIL_TEMPLATE=true
FEATURE_IMPORT_EXPORT=true
FEATURE_FILE_STORAGE=true
FEATURE_PAYMENT_GATEWAY=true
FEATURE_SMS_GATEWAY=true
FEATURE_SMTP=true
FEATURE_ROLES_PERMISSION=true
# Email Verification
FEATURE_EMAIL_VERIFICATION=false
# Set the environment variable to enable/disable the global stats endpoint
FEATURE_OPEN_STATS=false
# Mac Code Sign
# Required for signing the macOS app with your Developer ID certificate
#
# CSC_LINK: Base64 encoded .p12 certificate file
# Generate with: base64 -i /path/to/certificate.p12 | tr -d '\n'
# The certificate must include both the Developer ID Application certificate
# and its private key (exported from Keychain Access as .p12)
CSC_LINK=
# CSC_KEY_PASSWORD: Password for the .p12 file
# Set to empty string if no password was used when exporting the .p12
CSC_KEY_PASSWORD=
# Notarize MacOS
# Required for notarization to avoid Gatekeeper warnings on macOS 10.15+
#
# APPLE_API_KEY: Base64 encoded .p8 key file from App Store Connect
APPLE_API_KEY=
# APPLE_API_KEY_ID: The Key ID from App Store Connect (e.g., AB12CD34EF)
APPLE_API_KEY_ID=
# APPLE_API_ISSUER: The Issuer ID from App Store Connect
APPLE_API_ISSUER=
# GitHub App Integration
GITHUB_INTEGRATION_APP_ID=
GITHUB_INTEGRATION_CLIENT_ID=
GITHUB_INTEGRATION_CLIENT_SECRET=
GITHUB_INTEGRATION_PRIVATE_KEY=
# NOTE: nothing reads GITHUB_INTEGRATION_WEBHOOK_SECRET. The GitHub App webhook receiver reads
# GAUZY_GITHUB_WEBHOOK_SECRET (above) — setting this one instead leaves the receiver unconfigured,
# which now rejects every delivery.
GITHUB_INTEGRATION_WEBHOOK_SECRET=
# HubStaff Integration
HUBSTAFF_CLIENT_ID=
HUBSTAFF_CLIENT_SECRET=
HUBSTAFF_PERSONAL_ACCESS_TOKEN=
# Jitsu Browser Configuration
JITSU_BROWSER_URL=
JITSU_BROWSER_WRITE_KEY=
# Jitsu Server Configuration
JITSU_SERVER_URL=
JITSU_SERVER_WRITE_KEY=
JITSU_SERVER_DEBUG=
JITSU_SERVER_ECHO_EVENTS=
# Tracing Configuration
OTEL_ENABLED=false
OTEL_PROVIDER=zipkin
OTEL_SERVICE_NAME=
OTEL_EXPORTER_OTLP_PROTOCOL=
OTEL_EXPORTER_OTLP_HEADERS=
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=
OTEL_EXPORTER_OTLP_ENDPOINT=
ASPECTO_API_KEY=
HONEYCOMB_API_KEY=
HONEYCOMB_ENABLE_LOCAL_VISUALIZATIONS=
# Platform Logo resource URL (SVG is Recommended)
PLATFORM_LOGO='assets/images/logos/logo_Gauzy.svg'
# Desktop App 512x512 icon
GAUZY_DESKTOP_LOGO_512X512='assets/icons/icon_512x512.png'
# Platform Privacy URL
PLATFORM_PRIVACY_URL='https://gauzy.co/privacy'
# Platform terms of Services URL
PLATFORM_TOS_URL='https://gauzy.co/tos'
# Platform no internet logo
NO_INTERNET_LOGO='assets/images/logos/logo_Gauzy.svg'
# Company Information
COMPANY_NAME='Ever Co. LTD'
COMPANY_LINK='https://ever.co'
COMPANY_SITE_NAME='Gauzy'
COMPANY_SITE_LINK='https://gauzy.co'
COMPANY_GITHUB_LINK='https://github.com/ever-co'
COMPANY_GITLAB_LINK='https://gitlab.com/ever-co'
COMPANY_FACEBOOK_LINK='https://www.facebook.com/gauzyplatform'
COMPANY_TWITTER_LINK='https://twitter.com/gauzyplatform'
COMPANY_IN_LINK='https://www.linkedin.com/company/everhq'
# Desktop download links
DESKTOP_APP_DOWNLOAD_LINK_APPLE='https://gauzy.co/downloads#desktop/apple'
DESKTOP_APP_DOWNLOAD_LINK_WINDOWS='https://gauzy.co/downloads#desktop/windows'
DESKTOP_APP_DOWNLOAD_LINK_LINUX='https://gauzy.co/downloads#desktop/linux'
MOBILE_APP_DOWNLOAD_LINK='https://gauzy.co/downloads#mobile'
EXTENSION_DOWNLOAD_LINK='https://gauzy.co/downloads#extensions'
# Desktop Timer Application Configuration
PROJECT_REPO='https://github.com/ever-co/ever-gauzy.git'
DESKTOP_TIMER_APP_NAME='gauzy-desktop-timer'
DESKTOP_TIMER_APP_DESCRIPTION='Gauzy Desktop Timer'
DESKTOP_TIMER_APP_ID='com.ever.gauzydesktoptimer'
DESKTOP_TIMER_APP_REPO_NAME='ever-gauzy-desktop-timer'
DESKTOP_TIMER_APP_REPO_OWNER='ever-co'
DESKTOP_TIMER_APP_WELCOME_TITLE=
DESKTOP_TIMER_APP_WELCOME_CONTENT=
DESKTOP_TIMER_APP_PROTOCOL='gauzy-timer'
# Desktop Application Configuration
DESKTOP_APP_NAME='gauzy-desktop'
DESKTOP_APP_DESCRIPTION='Gauzy Desktop'
DESKTOP_APP_ID='com.ever.gauzydesktop'
DESKTOP_APP_REPO_NAME='ever-gauzy-desktop'
DESKTOP_APP_REPO_OWNER='ever-co'
DESKTOP_APP_WELCOME_TITLE=
DESKTOP_APP_WELCOME_CONTENT=
DESKTOP_APP_PROTOCOL='gauzy-desktop'
# Desktop Server Application Configuration
DESKTOP_SERVER_APP_NAME='gauzy-server'
DESKTOP_SERVER_APP_DESCRIPTION='Gauzy Server'
DESKTOP_SERVER_APP_ID='com.ever.gauzyserver'
DESKTOP_SERVER_APP_REPO_NAME='ever-gauzy-server'
DESKTOP_SERVER_APP_REPO_OWNER='ever-co'
DESKTOP_SERVER_APP_WELCOME_TITLE=
DESKTOP_SERVER_APP_WELCOME_CONTENT=
DESKTOP_SERVER_APP_PROTOCOL='gauzy-server'
# Desktop API Server Application Configuration
DESKTOP_API_SERVER_APP_NAME='gauzy-api-server'
DESKTOP_API_SERVER_APP_DESCRIPTION='Gauzy API Server'
DESKTOP_API_SERVER_APP_ID='com.ever.gauzyapiserver'
DESKTOP_API_SERVER_APP_REPO_NAME='ever-gauzy-api-server'
DESKTOP_API_SERVER_APP_REPO_OWNER='ever-co'
DESKTOP_API_SERVER_APP_WELCOME_TITLE=
DESKTOP_API_SERVER_APP_WELCOME_CONTENT=
DESKTOP_API_SERVER_APP_PROTOCOL='gauzy-api-server'
#AGENT
AGENT_APP_PROTOCOL='gauzy-agent'
REGISTER_URL='https://app.gauzy.co/#/auth/register'
FORGOT_PASSWORD_URL='https://app.gauzy.co/#/auth/request-password'
# I18N Translation Files URL
I18N_FILES_URL=
# MCP Server Configuration
API_TIMEOUT=30000
GAUZY_AUTH_EMAIL=your-email@example.com
GAUZY_AUTH_PASSWORD=your-secure-password
GAUZY_AUTO_LOGIN=false
GAUZY_MCP_DEBUG=false
MCP_APP_ID=co.gauzy.mcp-server
MCP_APP_NAME="Gauzy MCP Server"
NODE_ENV=development
# MCP Transport Configuration
# Options: stdio | http | websocket
MCP_SERVER_MODE="stdio" # Internal server runtime mode
MCP_TRANSPORT="stdio" # Exposed transport (used by TransportFactory)
# HTTP Transport Settings
MCP_AUTH_BASE_URL=http://localhost:3003
MCP_AUTH_PORT=3003
MCP_CORS_CREDENTIALS=true
MCP_CORS_ORIGIN=http://localhost:3000,http://localhost:4200,http://127.0.0.1:3000,http://127.0.0.1:4200
MCP_HTTP_HOST=localhost
MCP_HTTP_PORT=3001
# Session Management
MCP_AUTH_SESSION_SECRET=your-secure-session-secret
MCP_SESSION_COOKIE_NAME=mcp-session-id
MCP_SESSION_ENABLED=true
MCP_SESSION_TTL=1800000
MCP_TRUSTED_PROXIES=
# WebSocket Transport Settings
MCP_WS_ALLOWED_ORIGINS="*" # DEV ONLY; set specific origins in production
MCP_WS_CERT_PATH=path/to/your/certs/cert.pem
MCP_WS_COMPRESSION=true
MCP_WS_HOST=localhost
MCP_WS_KEY_PATH=path/to/your/certs/key.pem
# Default 1MB is safer; increase only if you must handle large messages
MCP_WS_MAX_PAYLOAD=1048576
MCP_WS_PATH="/sse" # Realtime endpoint path (WS server behind /sse by default)
MCP_WS_PER_MESSAGE_DEFLATE=true
MCP_WS_PORT=3002
MCP_WS_SESSION_COOKIE_NAME=mcp-ws-session-id
MCP_WS_SESSION_ENABLED=true
MCP_WS_TLS=false
MCP_WS_TRUSTED_PROXIES=
# OAuth 2.0 Authorization Configuration (Development)
MCP_AUTH_ENABLED=false
MCP_AUTH_RESOURCE_URI=http://localhost:3001/sse
MCP_AUTH_REQUIRED_SCOPES=mcp.read,mcp.write
# JWT validation for development using RS256 with JWKS
MCP_AUTH_JWT_ALGORITHMS=RS256
MCP_AUTH_JWT_AUDIENCE=http://localhost:3001/sse
MCP_AUTH_JWT_ISSUER=http://localhost:3003
MCP_AUTH_JWT_JWKS_URI=http://localhost:3003/.well-known/jwks.json
# If you switch to HS*, use MCP_AUTH_JWT_SECRET and set ALGORITHMS=HS256
# Cache settings
# 5 minutes
MCP_AUTH_TOKEN_CACHE_TTL=300
# 1 hour
MCP_AUTH_METADATA_CACHE_TTL=3600
# Authorization server configuration (mock for development)
MCP_AUTH_SERVERS='[{"issuer":"http://localhost:3003","authorizationEndpoint":"http://localhost:3003/oauth2/authorize","tokenEndpoint":"http://localhost:3003/oauth2/token","grantTypesSupported":["authorization_code","client_credentials","refresh_token"],"responseTypesSupported":["code"],"scopesSupported":["mcp.read","mcp.write","mcp.admin"],"codeChallengeMethodsSupported":["S256"]}]'
# -----------------------------------------------------------------------------
# AI CHAT (embedded AI agent) — @gauzy/plugin-ai-chat
# -----------------------------------------------------------------------------
# Master switch (chat is also hidden automatically when no provider is configured)
GAUZY_AI_CHAT_ENABLED=true
# SSRF egress guard for BYOK provider endpoints.
# A tenant admin can store a custom provider base URL and the server then fetches it (model
# catalogue, dictation, chat completions, document embeddings). By default any loopback, private
# (RFC 1918), link-local or cloud-metadata target is REFUSED, both when the URL is saved and when it
# is used, and redirects are not followed.
# Set this to `true` ONLY on single-tenant / self-hosted installs (and desktop local-server builds)
# whose users enter a LocalAI, Speaches, vLLM, Ollama or whisper.cpp address on localhost or a LAN in
# the AI settings page. Leave it unset on shared/multi-tenant hosting: there it would let any tenant
# reach the operator's internal network. It governs everything a TENANT credential leads to: the base
# URL a tenant entered AND the built-in local default a key-less row for Speaches/LocalAI/whisper.cpp
# falls back to (GHSA-w3mx-m5cr-3gxp). Only an operator's own `*_BASE_URL` value is trusted without it,
# so a zero-config local provider needs either this flag or that variable.
# GAUZY_AI_CHAT_ALLOW_PRIVATE_BASE_URLS=false
# Default provider/model when the tenant has not chosen one in Settings (BYOK).
# Providers are contributed by @gauzy/plugin-ai-provider-* plugins:
# anthropic | openai | openrouter | vercel-gateway | gauzy-ai | gemini | grok | groq | mistral |
# localai | openai-compatible (chat) — plus the voice-only ones listed under AI VOICE below.
GAUZY_AI_CHAT_DEFAULT_PROVIDER=anthropic
GAUZY_AI_CHAT_DEFAULT_MODEL=claude-sonnet-5
# BYOK credentials entered in Settings are encrypted at rest with this base64
# 32-byte key (shared with the core EncryptionService). Generate one with:
# node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
# ENCRYPTION_KEY=
# Server-wide provider API keys (tenant BYOK credentials from Settings take precedence)
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
OPENROUTER_API_KEY=
AI_GATEWAY_API_KEY=
# Optional custom base URLs (proxies / self-hosted compatible endpoints)
# ANTHROPIC_BASE_URL=
# OPENAI_BASE_URL=
# OPENROUTER_BASE_URL=
# AI_GATEWAY_BASE_URL=
# Free tier: set this to make the AI agent work with NO per-tenant setup.
#
# It is resolved LAST — after a tenant's own key from Settings, and after OPENROUTER_API_KEY above —
# and it is the only source restricted to OpenRouter's free (":free") models. That separation is the
# point: if you set OPENROUTER_API_KEY you are bringing your own account and keep full access, while
# this variable is for a shared key that should never be spent on paid models.
#
# Rate limits on free models are low and shared across everyone using the key. When a user is rate
# limited the chat tells them to connect their own OpenRouter account or configure another provider.
# OPENROUTER_PLATFORM_API_KEY=
#
# Optional: pin the free model list instead of fetching it from OpenRouter. Free slugs are retired
# without notice, so this lets you correct drift with a restart rather than a release. Comma
# separated; leave unset to use the live catalogue (cached, with a pinned fallback).
# OPENROUTER_FREE_MODELS=deepseek/deepseek-r1:free,meta-llama/llama-3.3-70b-instruct:free
# Optional: attach the Gauzy MCP server's tools to the chat agent.
# Only point this at an MCP server that honors the per-request bearer token
# (see packages/plugins/ai-chat README — security note).
# Google Gemini + xAI Grok provider keys (BYOK per tenant also supported in Settings -> AI)
# GEMINI_API_KEY=
# XAI_API_KEY=
# Groq + Mistral: OpenAI-compatible chat AND speech-to-text (Whisper / Voxtral) — usable as the
# tenant's voice (dictation) provider as well as for chat.
# GROQ_API_KEY=
# GROQ_BASE_URL=
# MISTRAL_API_KEY=
# MISTRAL_BASE_URL=
# -----------------------------------------------------------------------------
# AI VOICE (dictation / speech-to-text) — voice providers for the AI chat
# -----------------------------------------------------------------------------
# Dictation uses the tenant's chosen voice provider (Settings -> AI Providers -> "Use as default
# voice provider"), and otherwise the first speech-capable provider that has credentials, in this
# order: openai (50), groq (80), mistral (90), speaches (100), localai (101), whisper-cpp (102),
# openai-compatible (103), deepgram (110), elevenlabs (120). Any provider below that is configured
# — even one that cannot chat — makes dictation work.
#
# Cloud speech-to-text only providers (no chat models):
# DEEPGRAM_API_KEY=
# DEEPGRAM_BASE_URL=
# ELEVENLABS_API_KEY=
# ELEVENLABS_BASE_URL=
#
# LOCAL / self-hosted speech (no API key needed — setting the base URL is the whole credential;
# a tenant can also enter the URL in Settings -> AI Providers instead):
# Speaches (faster-whisper-server): docker run -p 8000:8000 ghcr.io/speaches-ai/speaches:latest-cpu
# SPEACHES_BASE_URL=http://localhost:8000/v1
# SPEACHES_API_KEY=
# LocalAI (chat + whisper): docker run -p 8080:8080 localai/localai:latest
# LOCALAI_BASE_URL=http://localhost:8080/v1
# LOCALAI_API_KEY=
# whisper.cpp whisper-server: ./build/bin/whisper-server -m models/ggml-base.en.bin --convert
# WHISPER_CPP_BASE_URL=http://localhost:8080
# WHISPER_CPP_API_KEY=
# Any OpenAI-compatible endpoint (vLLM, LM Studio, Ollama /v1, LiteLLM …) for chat and/or STT:
# OPENAI_COMPATIBLE_BASE_URL=http://localhost:11434/v1
# OPENAI_COMPATIBLE_API_KEY=
# GAUZY_AI_CHAT_MCP_URL=
# Optional: base URL the chat agent's tools use to call this API itself
# (defaults to API_BASE_URL). Useful in k8s to short-circuit via localhost.
# GAUZY_AI_CHAT_SELF_API_URL=
# --------------------------------------------------------------------------------------------------
# Documents (@gauzy/plugin-docs)
# --------------------------------------------------------------------------------------------------
# The Documents hub works out of the box with no configuration: files upload, extract text, and become
# searchable. Everything below is optional tuning. AI features are OFF by default — turn them on only
# after an AI provider is configured (see the AI chat section above; Documents reuses those providers
# and honors per-tenant BYOK keys).
# Master switch for AI classification, summaries and embeddings in Documents. ON by default.
# It is safe to leave on with no AI provider configured: the AI stages are additionally gated on a
# provider having credentials, so with none registered the pipeline skips them and uploads still
# extract text and stay fully searchable (lexical search only). Set to false to keep the AI stages
# off even where a provider IS configured.
# GAUZY_DOCS_AI_ENABLED=true
# Model overrides. Classification falls back to the AI chat default model when unset.
# GAUZY_DOCS_CLASSIFY_MODEL=
# GAUZY_DOCS_EMBEDDING_MODEL=text-embedding-3-small
# GAUZY_DOCS_EMBEDDING_DIMS=1536
# GAUZY_DOCS_EMBED_BATCH_SIZE=64
# GAUZY_DOCS_CLASSIFY_SAMPLE_CHARS=12000
# Text recognition (OCR) for scanned PDFs and image uploads, using the same AI provider (and the same
# per-tenant BYOK keys) as classification — no separate OCR service is involved. Off by default because
# it costs one model call per page. ON by default, but doubly gated — it needs GAUZY_DOCS_AI_ENABLED
# *and* a provider with credentials, so a deployment with no AI configured never transcribes anything.
# While unavailable, a PDF with no text layer and an image upload both fail with a clear "OCR is not
# available" message and land in the review queue.
# OCR'd documents are always flagged for review: a transcription can drop or garble text silently.
# GAUZY_DOCS_OCR_ENABLED=true
# GAUZY_DOCS_OCR_MAX_PAGES=20
# Vector store used for semantic retrieval. 'pgvector' requires PostgreSQL with the vector extension;
# on MySQL/SQLite (or when the extension is missing) retrieval degrades to lexical search automatically.
# GAUZY_DOCS_VECTOR_STORE=pgvector
# GAUZY_DOCS_RETRIEVAL_TOPK_MAX=12
# Chunking (how extracted text is split before embedding).
# GAUZY_DOCS_CHUNK_TOKENS=512
# GAUZY_DOCS_CHUNK_OVERLAP_TOKENS=64
# Re-index every document automatically when the embedding model or dimensions change. Off by default
# because a fleet-wide re-embed costs money — trigger it deliberately from Documents settings instead.
# GAUZY_DOCS_AUTO_REINDEX_ON_MODEL_CHANGE=false
# Upload limits and processing.
# GAUZY_DOCS_MAX_FILE_SIZE=52428800
# GAUZY_DOCS_MAX_EXTRACTED_CHARS=2000000
# GAUZY_DOCS_MAX_BINARY_BYTES=10485760
# GAUZY_DOCS_QUEUE_CONCURRENCY=2
# GAUZY_DOCS_STUCK_THRESHOLD_MINUTES=1440
# Per-organization storage quota in bytes. 0 or unset means unlimited (an organization-level setting
# in Documents settings overrides this).
# GAUZY_DOCS_ORG_QUOTA_BYTES=0
# Minutes between automatic version snapshots while a page is being edited.
# GAUZY_DOCS_VERSION_DEBOUNCE_MINUTES=10
# Structured logging of knowledge searches (query length, result counts, latency — never query text).
# GAUZY_DOCS_RETRIEVAL_LOG_ENABLED=true
# Inbound email capture: documents emailed to a per-organization address land in Documents for review.
# ON by default, but the route accepts NOTHING until a delivery can prove itself. Captured documents
# are never added to AI knowledge automatically — they always go through review first.
#
# Two kinds of capture address:
# PLATFORM docs-<128-bit hex>@$GAUZY_DOCS_INBOUND_DOMAIN — minted automatically for every
# organization on first read. Requires GAUZY_DOCS_INBOUND_DOMAIN; without it there
# is no address to mint and none is invented.
# CUSTOM_DOMAIN <mailbox>@<the tenant's own domain>, registered through the Documents settings UI.
# Inert until the tenant publishes _gauzy-docs.<domain> IN TXT with the value the UI
# shows — a chosen mailbox name is guessable, so ownership must be proven.
#
# A delivery is authenticated by EITHER of two proofs:
# 1. The deployment-wide HMAC below. Signature is hex(HMAC_SHA256(secret, "<timestamp>.<rawBody>"))
# in x-gauzy-docs-signature with x-gauzy-docs-timestamp. Fails closed when unset, and enforces a
# timestamp tolerance, a constant-time compare and single-use replay consumption.
# 2. A per-address relay secret in x-gauzy-docs-address-secret, issued once when a custom-domain
# address is created or rotated. Prefer this for tenant-owned domains: the deployment-wide
# secret can post as ANY tenant, a per-address secret only as one.
# GAUZY_DOCS_INBOUND_EMAIL_ENABLED=true
# GAUZY_DOCS_INBOUND_DOMAIN=
# GAUZY_DOCS_INBOUND_WEBHOOK_SECRET=
# GAUZY_DOCS_INBOUND_MAX_MESSAGE_BYTES=26214400
# BullMQ-backed dispatch for the Documents pipeline. Defaults to ON exactly where a BullMQ root is
# registered — i.e. REDIS_ENABLED=true and SCHEDULER_QUEUE_ENABLED not set to false. Every process
# that loads plugins (API, `yarn seed`, worker) registers that root under the same condition, so
# the default cannot register a @Processor without a connection. Set it explicitly to force either
# direction; with it off the pipeline runs in-process, which stays a fully supported mode.
# GAUZY_DOCS_QUEUE_ENABLED=false
# Whether THIS process also runs the docs-processing consumer, or only enqueues. Defaults to true
# wherever the queue is on. Set false on the API when a dedicated worker deployment exists, so the
# extraction / OCR / embedding work happens only there.
# GAUZY_DOCS_QUEUE_WORKER_ENABLED=false
# Fleet-wide kill switch for the BullMQ root itself (API + worker + seeder). `false` removes the
# root everywhere and puts every queue-backed pipeline back on its in-process fallback.
# SCHEDULER_QUEUE_ENABLED=false
# BILLING (Stripe) — hosted deployments only.
#
# Leave everything here unset for self-hosting: registration behaves exactly as it always has, no
# Stripe call is ever made, no subscription is required, and the billing UI is absent. The presence
# of STRIPE_SECRET_KEY is the only switch that turns any of it on.
#
# Server-side only — never expose these to the browser.
#
# Per-environment expectation:
# demo no key at all. DEMO=true also disables billing outright even if a key is present,
# so a demo deployment cannot reach Stripe by accident.
# staging a sk_test_ key. Test mode; no real card is ever charged.
# prod a sk_live_ key AND STRIPE_LIVE_MODE=true. A live key without that second opt-in is
# refused, so copying the production secret bundle onto staging cannot start taking
# real payments.
STRIPE_SECRET_KEY=
# Required alongside a sk_live_ key. Leave unset everywhere except production.
STRIPE_LIVE_MODE=
# Signing secret for the Stripe webhook endpoint (POST /api/billing/webhook).
STRIPE_WEBHOOK_SECRET=
# Where an unsubscribed visitor is sent to pick a plan. Shared by every Ever product.
EVER_CHECKOUT_URL=https://ever.co/checkout
# Which Ever product this deployment bills for: the `<product>` in the catalog's lookup keys
# (`ever_<product>_<hosting>_<tier>_<interval>`) and in `metadata.ever_product`. Default `gauzy`.
# The Stripe account is shared by every Ever product, so the webhook, the signup paywall, the tenant
# link and the billing pages all count ONLY this product's subscriptions. The Ever Teams deployment
# of this API sets `teams`. Only this product's HOSTED plans count (`ever_<product>_cloud_*`, or
# `ever_hosting` absent/`cloud`): a self-hosted license subscription never does. A value that is not a
# lower-case product key disables billing (logged) and, while BILLING_SIGNUP_PAYWALL is on, refuses
# every signup until it is fixed - the paywall fails closed, never open.
BILLING_PRODUCT=
# Whether signing up (POST /api/auth/register) requires a subscription to BILLING_PRODUCT. Default
# `true` (only `false`/`0`/`no`/`off` turn it off). Has no effect unless billing is on. `false` keeps
# everything else — tenant linking, the billing pages, the webhook — and drops only the paywall; the
# Ever Teams deployment uses that.
BILLING_SIGNUP_PAYWALL=
# Whether the Stripe webhook may WRITE a tenant's billing link. Default `false` (only
# `true`/`1`/`yes`/`on` turn it on). Off, the webhook still runs every check and logs
# `decision: would-link`, and tenants are linked by the buyer's own Checkout Session at onboarding or
# by an admin opening Settings > Billing. Turning it on lets a purchase made under an admin's address
# by somebody else bind that admin's tenant, and lets an existing admin's new purchase bind their OLD
# tenant before they register the new one - leave it off unless you accept both.
BILLING_WEBHOOK_LINKING=