Skip to content
You are reading the unreleased documentation. No version is released yet, and these pages describe code that is not in a release.

Environment variables

Every variable the engine reads, from the four example files in the repository. The install stepper writes the production files; the development files are the ones nx serve reads on a laptop. A variable marked optional is commented out in the example and read only when set.

  • PORT. Example: 53000.
  • NODE_ENV. development and test are the only labels that unlock a development behaviour; every other label, and none, reads as production. Example: development.
  • DATABASE_URL. Example: postgresql://postgres:postgres@localhost:55432/merchants_engine_dev.
  • DATABASE_TEST_URL. Example: postgresql://postgres:postgres@localhost:55433/merchants_engine_test.
  • REDIS_HOST. Example: localhost.
  • REDIS_PORT. Example: 56379.
  • REDIS_TEST_HOST. Example: localhost.
  • REDIS_TEST_PORT. Example: 56380.
  • BETTER_AUTH_SECRET. Better Auth owns auth and sessions. BETTER_AUTH_SECRET is high-entropy, >= 32 bytes; rotate it when a person who knew it leaves. Example: change-in-production-min-32-bytes-abcde.
  • BETTER_AUTH_URL. The API’s own externally-reachable base URL (used to build email links). Example: http://localhost:53000.
  • AUTH_COOKIE_DOMAIN. Optional. Cross-subdomain session cookie, production only. Leading-dot root domain so the session is shared across the admin / storefront / api subdomains. The stepper leaves it unset: every install proxies /api on the admin host. Example: .shop.example.
  • LOG_LEVEL. Example: debug.
  • CORS_ORIGIN. CORS - admin dev (53200), admin e2e (53211), storefront dev (53300), storefront e2e (53311). Example: http://localhost:53200,http://localhost:53211,http://localhost:53300,http://localhost:53311.
  • STOREFRONT_PUBLIC_BASE_URL. Public origins. In dev these are the local servers; the stepper writes the deployed origins, and the API refuses to boot in production without them. Example: http://localhost:53300.
  • ADMIN_PUBLIC_BASE_URL. Example: http://localhost:53200.
  • API_PUBLIC_BASE_URL. Example: http://localhost:53000.
  • STOREFRONT_REVALIDATE_URL. Storefront revalidation - backend-to-backend signal that triggers SSR cache invalidation on product / category / CMS write events. Both values must match the storefront’s. The secret is a high-entropy string ≥ 32 bytes. Never log it. Example: http://localhost:53300/api/_internal/revalidate.
  • STOREFRONT_REVALIDATE_SECRET. Example: change-in-production-min-32-bytes-12345.
  • UNSUBSCRIBE_TOKEN_SECRET. Unsubscribe links in outbound mail are signed with this. Example: change-in-production-min-32-bytes-abcdef.
  • MAX_FILE_SIZE. Example: 10485760.
  • ALLOWED_FILE_TYPES. Example: jpg,jpeg,png,gif,pdf,doc,docx.
  • STORAGE_DRIVER. Asset storage - choose driver per environment. local - writes to ./uploads (dev fallback, no S3 needed) s3 - any S3-compatible bucket (set the S3_* vars below) Example: local.
  • S3_ENDPOINT. Optional. Example: https://s3.<region>.example.
  • S3_REGION. Optional.
  • S3_BUCKET. Optional.
  • S3_ACCESS_KEY. Optional.
  • S3_SECRET_KEY. Optional.
  • S3_PUBLIC_URL. Optional.
  • PRIVATE_UPLOADS_DIR. Asset variant pipeline. Local driver - where uploaded originals are written. Must be OUTSIDE the public-served uploads/ root. Defaults to ./uploads-private/. Example: ./uploads-private.
  • SIGNED_URL_SECRET. Secret used by LocalStorageDriver.signUrl() to mint HMAC-signed admin access URLs for private originals. High-entropy ≥ 32 bytes. Example: change-in-production-min-32-bytes-67890.
  • RETAIN_ORIGINALS. Original-deletion gate: true keeps originals for re-processing (dev), false deletes them once every variant is verified (the production default). Example: true.
  • STRIPE_API_KEY. Optional.
  • MAIL_TRANSPORT. Transport selection - console (default, no network) lets local dev + jest + e2e run without a Resend account. resend calls the real API. Example: console.
  • RESEND_API_KEY. Sending-only API key (re_…). REQUIRED when MAIL_TRANSPORT=resend.
  • RESEND_FROM_EMAIL. Envelope-from address. Must be on a Resend-verified domain. Example: noreply@example.test.
  • RESEND_FROM_NAME. Display name combined with RESEND_FROM_EMAIL to build the From: header. Example: My Store.
  • RESEND_REPLY_TO. Default Reply-To header. Per-job overrides take precedence. Example: contact@example.test.
  • CONTACT_FORM_RECIPIENT. Where the storefront contact form is delivered. Example: contact@example.test.
  • RESEND_WEBHOOK_SECRET. Svix signing secret (whsec_…) for inbound webhooks at /api/v1/webhooks/resend. REQUIRED when MAIL_TRANSPORT=resend.
  • MAIL_DEV_REDIRECT_TO. Development override - when set, every email is redirected here. Honoured under NODE_ENV=development and test only; a production boot refuses it.

Sign-up bot protection - Cloudflare Turnstile

Section titled “Sign-up bot protection - Cloudflare Turnstile”
  • TURNSTILE_SECRET. Optional. Leave unset in dev and e2e: the widget stays inert and sign-up stays open.
  • INDEXNOW_KEY. IndexNow key: 8 to 128 lower-case hex characters. The storefront serves the matching key file; the stepper writes it.
  • INDEXNOW_HOST. Optional. Defaults to the storefront host.
  • GSC_SUBMISSION_ENABLED. Google Search Console API (opt-in): Example: false.
  • GSC_SITE_URL. sc-domain:<domain> for a domain property, or https://<domain>/ for a URL-prefix property.
  • GOOGLE_APPLICATION_CREDENTIALS. Absolute path to the service-account JSON key. Never commit the file.
  • BING_API_KEY. Bing Webmaster API (opt-in):
  • BING_SITE_URL. The verified site URL; falls back to STOREFRONT_PUBLIC_BASE_URL.
  • LICENSE_FILE. Path of the Ed25519-signed license file issued for this store. Required in production (the API refuses to boot without one); unset on a developer box the API runs unlicensed and reports missing on GET /v1/admin/license.
  • INVOICE_LOGO_PATH. Optional PNG drawn in the invoice header; without it the header carries the store name. The stepper writes it from the uploaded logo.
  • NODE_ENV. Example: production.
  • PORT. Example: 53000.
  • API_GLOBAL_PREFIX. Example: api.
  • LOG_LEVEL. Example: info.

Database and cache (compose service names)

Section titled “Database and cache (compose service names)”
  • DB_PASSWORD. Example: <generated, 64 hex>.
  • DATABASE_URL. Example: postgresql://merchants_app:<generated>@postgres:5432/merchants_engine?schema=public.
  • REDIS_HOST. Example: redis.
  • REDIS_PORT. Example: 6379.
  • REDIS_PASSWORD. Example: <generated, 64 hex>.
  • BETTER_AUTH_SECRET. Example: <generated, 64 hex>.
  • BETTER_AUTH_URL. The public API origin, used in every callback link inside an email. Example: https://api.shop.example.
  • CORS_ORIGIN. Also Better Auth trustedOrigins. The admin reaches the API through its own origin. Example: https://shop.example,https://admin.shop.example.
  • STOREFRONT_PUBLIC_BASE_URL. Example: https://shop.example.
  • ADMIN_PUBLIC_BASE_URL. Example: https://admin.shop.example.
  • API_PUBLIC_BASE_URL. Example: https://api.shop.example.
  • STOREFRONT_REVALIDATE_URL. Example: https://shop.example/api/_internal/revalidate.
  • STOREFRONT_REVALIDATE_SECRET. Example: <generated, 64 hex; the storefront box needs the same value>.
  • UNSUBSCRIBE_TOKEN_SECRET. Example: <generated, 64 hex>.
  • TURNSTILE_SECRET. Example: <entered and verified against siteverify>.
  • LICENSE_FILE. Mounted read-only by the compose file from the path the stepper copied it to. Example: /app/config/license.json.
  • MAX_FILE_SIZE. Example: 10485760.
  • ALLOWED_FILE_TYPES. Example: jpg,jpeg,png,gif,pdf,doc,docx.
  • STORAGE_DRIVER. Example: s3.
  • S3_ENDPOINT. Example: https://s3.<region>.example.
  • S3_REGION. Example: <region>.
  • S3_BUCKET. Example: <bucket, hyphens, no dots>.
  • S3_ACCESS_KEY. Example: <entered and verified by a put, get and delete>.
  • S3_SECRET_KEY. Example: <entered>.
  • S3_PUBLIC_URL. Example: https://<bucket>.s3.<region>.example.
  • SIGNED_URL_SECRET. Example: <generated, 64 hex>.
  • RETAIN_ORIGINALS. Example: false.
  • INVOICE_LOGO_PATH. Example: /app/config/invoice-logo.png.
  • MAIL_TRANSPORT. Example: resend.
  • RESEND_API_KEY. Example: <entered and verified by a domain lookup and a test message>.
  • RESEND_WEBHOOK_SECRET. Example: <entered>.
  • RESEND_FROM_EMAIL. Example: noreply@shop.example.
  • RESEND_FROM_NAME. Example: My Store.
  • RESEND_REPLY_TO. Example: hello@shop.example.
  • CONTACT_FORM_RECIPIENT. Example: hello@shop.example.
  • MAIL_DEV_REDIRECT_TO. Honoured under NODE_ENV=development only; a production boot refuses a non-empty value.
  • INDEXNOW_KEY.
  • BING_API_KEY.
  • BING_SITE_URL.
  • GSC_SUBMISSION_ENABLED. Example: false.
  • GSC_SITE_URL.
  • GOOGLE_APPLICATION_CREDENTIALS.
  • STRIPE_API_KEY.
  • STOREFRONT_API_URL. API base URL the storefront fetches from. Browser-side proxy rewrites /v1/* to ${STOREFRONT_API_URL}/v1/* (see vite.config.ts). Example: http://localhost:53000/api.
  • STOREFRONT_PUBLIC_BASE_URL. Public base URL the storefront uses for canonical, og:url, hreflang alternates, and JSON-LD URL fields. Production: the deployed origin. Example: http://localhost:53300.
  • STOREFRONT_REVALIDATE_SECRET. Shared secret matched by the API webhook listener. Compared timing-safely on the inbound /api/_internal/revalidate request. Must equal the value of STOREFRONT_REVALIDATE_SECRET on the API side. Rotate it together with the API side. Example: change-in-production-min-32-bytes-12345.
  • REDIS_HOST. Redis client for the inbound /api/_internal/revalidate webhook dedup (the same dedup the API applies to inbound webhooks). The storefront SSR process MUST resolve to a Redis instance in production (the boot guard in apps/storefront/src/server/lib/dedup-store.ts throws when NODE_ENV=production and REDIS_HOST is unset). In local dev these defaults match the dev Redis container on port 56379 per the reserved port table. Leave blank to opt into the in-memory fallback (acceptable for vitest + local single-process work; never for production). Example: localhost.
  • REDIS_PORT. Example: 56379.
  • REDIS_PASSWORD.
  • NODE_ENV. Example: production.
  • PORT. Example: 54300.
  • HOST. Example: 0.0.0.0.
  • STOREFRONT_API_URL. On one box the storefront reaches the engine over the compose network (http://api:53000/api); on its own box it goes out over the public API origin. Example: https://api.shop.example/api.
  • STOREFRONT_PUBLIC_BASE_URL. Public origin used for canonical, og:url, hreflang, JSON-LD URL fields. Example: https://shop.example.
  • STOREFRONT_REVALIDATE_SECRET. Must equal the engine’s STOREFRONT_REVALIDATE_SECRET; a storefront-only box is asked for it and the engine box prints it at the end of its install. Example: <the engine's value>.
  • VITE_TURNSTILE_SITE_KEY. Public; read at build time and passed by the stepper as a build argument. Example: <entered>.

Revalidate dedup store (compose service names)

Section titled “Revalidate dedup store (compose service names)”
  • REDIS_HOST. Example: redis.
  • REDIS_PORT. Example: 6379.
  • REDIS_PASSWORD. Example: <generated, 64 hex>.
Section titled “Public API origin, for links the page builds itself”
  • API_URL. Example: https://api.shop.example/api.