Skip to content

Environment variables

The one rule: the extension bundle is public

Section titled “The one rule: the extension bundle is public”

Everything the extension app reads at build time (any WXT_- or VITE_-prefixed variable) is compiled into the shipped extension and readable by anyone who downloads it from the store. Treat extension env as configuration, never secrets:

  • OK in extension env: Firebase web config, hosting URLs, feature flags.
  • NEVER in extension env: Stripe secret keys, service-account JSON, webhook signing secrets, any API key that grants data access. Those live only in the backend (Secret Manager / function env).

Created from .env.example by the setup wizard. All are build-time.

variable module purpose
WXT_GOOGLE_OAUTH_CLIENT_ID auth Google OAuth client ID (Web application type; redirect https://<ext-id>.chromiumapp.org/). Set → sign-in uses the chrome.identity web-auth-flow (+ getAuthToken fast path on Chrome). Empty → offscreen signInWithPopup fallback.
VITE_FIREBASE_HOSTING_URL auth Firebase Hosting URL used by the offscreen sign-in fallback (https://<project>.firebaseapp.com).
WXT_ANONYMOUS_AUTH auth true = anonymous-first: every install gets a guest uid immediately; sign-in upgrades it in place (uid preserved). Default false.
WXT_API_URL billing, gate, error-reporting Deployed Cloud Functions base URL (https://us-central1-<project>.cloudfunctions.net/api). Checkout/portal, gate events, and error reports all post under it.
VITE_PREMIUM billing true shows premium UI (pricing, portal, status). Plans/copy live in site.config.ts; amounts and trials are server-side.
WXT_DEMO_SURFACES demo-newtab, demo-devtools true builds the optional demo entrypoints (branded new tab + devtools panel). Default false so the standard build never takes over the user’s new tab.

The Firebase web config itself is not env; paste it into apps/extension/utils/firebase.ts (the TODO marker).

Loaded from apps/extension/; later files override earlier ones:

  1. .env: base values (untracked; created from .env.example)
  2. .env.local: personal overrides (untracked)
  3. .env.[mode]: per-mode, e.g. .env.development (trackable)
  4. .env.[mode].local: personal per-mode overrides (untracked)

For browser-specific values, prefer branching on import.meta.env.BROWSER (or per-browser manifest fields in wxt.config.ts) over separate .env.chrome/.env.firefox files.

  • WXT_*: preferred for new variables (exposed on import.meta.env).
  • VITE_*: also exposed; parts of the kit still use it.
  • Unprefixed variables are not available to app code; use that deliberately for build-machine-only values.

Keep .env.example exhaustive, and keep each module’s module.json env list in sync; that’s what lets the pruner remove template entries with their module.

Secrets, set once per project:

Terminal window
firebase functions:secrets:set STRIPE_SECRET_KEY # sk_…
firebase functions:secrets:set STRIPE_WEBHOOK_SECRET # whsec_… (pnpm stripe:webhook sets this for you)

Non-secret knobs, plain env on the function:

variable purpose
BILLING_SUCCESS_URL checkout success return page (https)
BILLING_CANCEL_URL checkout cancel return page (https)
BILLING_PORTAL_RETURN_URL customer-portal return URL
BILLING_TRIAL_DAYS card-free trial length; 0 = none. Display copy in site.config.ts → pricing.trialDays should match; the server stays the authority
BILLING_AUTOMATIC_TAX "true" enables Stripe Tax on checkout
BILLING_ALLOW_PROMO_CODES "false" hides the promo-code field (default on)

Credit-based billing needs no env of its own: credit amounts live in Stripe price metadata (credits on packs, monthly_credits on the metered plan; pnpm seed:stripe sets them), and site.config.ts → pricing.plans decides which model is sold. See the credits model.

For local emulator runs, the same two secrets go in backend/functions/.secret.local (gitignored); see the payments guide.