Environment
env.js validation, APP_ENV, and production invariants.
Single source of truth: env.js
env.js loads .env.{APP_ENV} (files: .env.development, .env.staging, .env.production — all gitignored), then zod-validates client and buildTime schemas separately. It is the only file that reads dotenv; EXPO_NO_DOTENV=1 is set on every script and every EAS profile so Expo's own dotenv loading never races it.
Client code imports from @env; app.config.ts imports env.js directly.
Variable groups
- Backend selection:
CONVEX_DEPLOYMENT,CONVEX_URL,CONVEX_SITE_URL(prod uses theappsapi/appsbackend.theraw.com.mycustom domains). - Identity:
APP_ENV,NAME,SCHEME,BUNDLE_ID,PACKAGE(non-prod builds get suffixed bundle ids viawithEnvSuffix). - Integrations:
SHOPIFY_STORE_URL,SHOPIFY_STOREFRONT_ACCESS_TOKEN, Google client IDs (web/iOS/Android),SENTRY_DSNplus the Sentry environment and sample rates thatenv.jsderives fromAPP_ENV. - Misc:
EAS_PROJECT_ID,SUPPORT_WHATSAPP/SUPPORT_EMAIL, notification flags,PRODUCT_QUERY_LIMIT,MMKV_OBFUSCATION_KEY(obfuscation, not encryption).
Production invariants
PRODUCTION_ENV_INVARIANTS in env.js pins, when APP_ENV=production: the bundle/package/scheme, the prod Convex URLs, and the three Google client IDs — any drift throws at config time. A second guard throws if a development env points at prod Convex URLs unless ALLOW_DEV_ON_PROD_CONVEX=1.
Sentry sample rates differ per environment (prod traces 0.2, profiles 0.1).