Skip to content

Environment contract is drifting: lib/env.ts schema omits variables read elsewhere and assertEnv is never invoked #9

Description

@ibrahimmosouf-png

Labels / Complexity: docs,dx · High — 13## Problem

lib/env.ts is meant to be the single source of truth for environment variables (RULES, validateEnv, assertEnv), but the schema and the codebase have drifted apart:

  • assertEnv is defined in lib/env.ts and never called anywhere in the repo, so no build or boot path fails fast on missing configuration.

  • Variables read by the code are absent from the schema: NEXT_PUBLIC_BASE_URL in app/sitemap.ts and app/robots.ts; NEXT_PUBLIC_ERROR_TRACKING_ENABLED, NEXT_PUBLIC_ENVIRONMENT, NEXT_PUBLIC_APP_VERSION, and NEXT_PUBLIC_ERROR_SAMPLE_RATE in lib/errorTracking.ts; NEXT_PUBLIC_STELLAR_EXPLORER_URL in lib/stellar/explorer.ts; CLOUDINARY_API_KEY and CLOUDINARY_API_SECRET in app/api/cloudinary/delete.ts.

  • The template misleads: REDIS_URL is required in the schema but absent from .env.example, and lib/api/client.ts defaults to http://localhost:5000/api while .env.example sets NEXT_PUBLIC_API_URL=http://localhost:3001, so a fresh clone without .env.local talks to the wrong port.

  • Server-only secrets are unvalidated: CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET are read at runtime with no schema check.

  • Misconfiguration fails at runtime, not at boot: a missing or mistyped variable surfaces as a cryptic request error instead of a build failure with the variable name.

  • Onboarding is adversarial: new contributors copy .env.example and inherit the port mismatch and missing entries.

  • The drift will recur: nothing checks that every process.env.* read is declared.

Root cause

// lib/env.ts
export function assertEnv(): void {
  // ...
  if (!valid) {
    throw new Error('[env] Build failed - required environment variables are missing or invalid: ...');
  }
}
// never imported or called by next.config.js, any server boot path, or any route

Why this is architecturally hard

  1. The fix is a reconciliation across three surfaces (schema, .env.example, and every process.env.* read), not a one-liner; each variable needs a deliberate required and validate decision, and NEXT_PUBLIC_* versus server-only placement matters for client bundles.
  2. Wiring assertEnv into a boot path must not break next build for contributors who run without optional keys; the schema already separates required from optional, so the invocation point (e.g. next.config.js or an instrumentation hook) must respect that distinction.
  3. A regression test that statically scans the repo for process.env.X reads and asserts each is declared is the only durable guard against re-drift, and it must tolerate build-time vars like NODE_ENV.

Proposed design

Reconcile the schema with the actual reads, complete .env.example, wire assertEnv into a build/server boot path that throws on missing required variables, and add the static scan test.

Acceptance criteria

  • Every process.env.* read in app/, components/, hooks/, lib/, store/, features/, utils/, and middleware.ts is declared in RULES in lib/env.ts.
  • .env.example matches the schema: REDIS_URL, CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET, NEXT_PUBLIC_BASE_URL, and the error-tracking variables are present, and the API URL default and example agree.
  • assertEnv runs at build or server boot and throws with the variable name on a missing required value.
  • A test fails when a new process.env.X read is added without a schema entry.
  • npm run type-check, npm run lint, and npm run build pass.

Out of scope

Adding new environment variables beyond reconciling what the code already reads.

Getting started

  • Read lib/env.ts, .env.example, lib/api/client.ts, lib/errorTracking.ts, app/sitemap.ts, and lib/stellar/explorer.ts.
  • List the drift with grep -rhn "process.env\.[A-Z_]*" --include="*.ts" --include="*.tsx" app components hooks lib store features utils middleware.ts.
  • Verify with npm run type-check and npm run build.

Good first files to read: lib/env.ts, .env.example, lib/api/client.ts.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaigndocsDocumentation and configuration templatesdxDeveloper experience

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions