Configuration
Everything is an environment variable — no config tables. Conventions, key variables and the plan catalog.
zmlkit keeps all configuration in environment variables — nothing is stored in the database. That makes config auditable, reproducible across environments and impossible to drift between instances. The trade-off: changing config means redeploying (or restarting dev).
The full reference lives in docs/env/web.env.example (web) and docs/env/mobile.env.example (mobile). Env is validated with Zod at boot and fails fast on invalid values.
The per-app convention
zmlkit is multi-app: one deployment can serve several products, each isolated by app_id. Config follows the same split:
- Global variables use plain names:
OPENROUTER_API_KEY,STORAGE_BUCKET,RESEND_API_KEY. - Per-app variables use
APP_<SLUG>_<NAME>, where<SLUG>is the app slug upper-cased with-→_. Appapp_a→APP_APP_A_STRIPE_SECRET_KEY. - A per-app value overrides the global default; if unset, the global is used.
- Apps are listed in
ENABLED_APPS(comma-separated slugs) and must exist in theapptable (seeded).
Key variables
AUTH_SECRET— session/token signing. Generate withopenssl rand -base64 32. Required.DATABASE_URL/DATABASE_PROVIDER— Postgres (default), MySQL, or SQLite for local dev.API_BASE_URL/WEB_BASE_URL— absolute URLs, used for OAuth callbacks, webhooks and SEO (canonical/sitemap).OPENROUTER_API_KEY/KIE_API_KEY— AI providers (global or per-app).WAFFO_MERCHANT_ID/WAFFO_PRIVATE_KEY— Waffo Pancake payment credentials (or per-appAPP_<SLUG>_WAFFO_*);WAFFO_ENVIRONMENT=test|prodselects the mode.APP_<SLUG>_GOOGLE_CLIENT_ID/_SECRET(and GitHub equivalents) — OAuth, per app.STORAGE_*— S3/Cloudflare R2 bucket, keys, endpoint, signed-URL TTL.REDIS_URL— optional; enables distributed rate limiting across instances.SENTRY_DSN/NEXT_PUBLIC_SENTRY_DSN— optional monitoring.
Minimal to boot: AUTH_SECRET + DATABASE_URL. Every capability (AI, payments, storage, email, OAuth) switches on only when its key is present, so you can add them incrementally.
What is not env: the plan catalog
Product catalog data — plans, prices, credits, Stripe price ids, Waffo Pancake product ids — is code: packages/core-domain/src/plans.ts. It is typed, versioned and shared by web and mobile. Waffo product ids can also be supplied as a plan-code map in WAFFO_PRODUCT_IDS.
{ code: 'pro_monthly', kind: 'subscription', interval: 'month',
amount: 1900, currency: 'usd', credits: 3000, creditsValidDays: 31,
providerRefs: {
stripe: { priceId: 'price_...' },
waffo: { productId: 'PROD_...' },
} }Only the secrets (API keys, webhook signing secrets) go in env.
Mobile config
mobile/.env holds only public EXPO_PUBLIC_* values — API base URL, RevenueCat public keys, native OAuth client ids. Server secrets never ship to the mobile app.