Skip to content

feat(config): add Zod-based startup env validation | ENG-193#294

Merged
LuD1161 merged 2 commits intomainfrom
LuD1161/env-var-validation
Feb 16, 2026
Merged

feat(config): add Zod-based startup env validation | ENG-193#294
LuD1161 merged 2 commits intomainfrom
LuD1161/env-var-validation

Conversation

@LuD1161
Copy link
Contributor

@LuD1161 LuD1161 commented Feb 16, 2026

Summary

  • Add Zod-based environment variable validation at startup for backend, worker, and frontend services
  • Misconfiguration is caught immediately with clear, actionable error messages instead of silent failures or cryptic runtime errors
  • Shared schemas in packages/shared/src/env.ts avoid duplication across services
  • Existing ad-hoc checks preserved as defense-in-depth

Key design decisions

  • SKIP_INGEST_SERVICES support: When true, DATABASE_URL and LOG_KAFKA_BROKERS become optional (backend)
  • Boolean coercion: Explicit stringToBoolean helper avoids JS footgun where Boolean("false") === true
  • AUTH_PROVIDER normalization: transform+pipe+catch pattern trims, lowercases, and defaults unknown values to 'local'
  • Frontend VITE_API_URL: Defaults to 'http://localhost:3211' preserving test compatibility
  • Worker MINIO_ENDPOINT: Defaults to 'localhost' matching current behavior

Files changed

New files (10):

  • packages/shared/src/env.ts — shared Zod schemas + helpers
  • backend/src/config/env.schema.ts — backend env schema with conditional validation
  • backend/src/config/env.validate.ts — validation function for NestJS ConfigModule
  • worker/src/config/env.schema.ts — worker env schema
  • worker/src/config/env.validate.ts — worker validation function
  • frontend/src/config/env.schema.ts — frontend env schema
  • 4 test files with 45 tests total

Modified files (5):

  • packages/shared/src/index.ts — export env module
  • backend/src/app.module.ts — add validate: validateBackendEnv to ConfigModule
  • worker/src/temporal/workers/dev.worker.ts — add validation call after dotenv
  • frontend/src/config/env.ts — replace manual fallback pattern with Zod validation
  • test/setup.ts — provide test defaults for SECRET_STORE_MASTER_KEY and SKIP_INGEST_SERVICES

Manual Verification (SHIPSEC_INSTANCE=1)

Tested by creating .instances/instance-1/backend.env with each config and running SHIPSEC_INSTANCE=1 bun src/main.ts from backend/:

# Scenario Expected Result Output
1 Remove DATABASE_URL from env Fail with clear error PASS DATABASE_URL is required (set SKIP_INGEST_SERVICES=true or ENABLE_INGEST_SERVICES=false to skip)
2 SKIP_INGEST_SERVICES=true without DATABASE_URL Should start PASS Validation passed, NestJS bootstrapped
3 SECRET_STORE_MASTER_KEY="short" Fail with length error PASS SECRET_STORE_MASTER_KEY must be exactly 32 characters
4 AUTH_PROVIDER=clerk without CLERK_SECRET_KEY Fail PASS Both CLERK_SECRET_KEY and CLERK_PUBLISHABLE_KEY reported missing
5 Valid .env with all required fields Start normally PASS Validation passed, NestJS bootstrapped

Test plan

  • 45 new tests pass across 4 test files (shared: 19, backend: 10, worker: 7, frontend: 9)
  • Full test suite passes (no new failures introduced — pre-existing naabu flaky test unaffected)
  • ESLint + Prettier pass on all new/modified files
  • Manual: Remove DATABASE_URL from backend/.env → should fail with clear error
  • Manual: Set SKIP_INGEST_SERVICES=true without DATABASE_URL → should start
  • Manual: Set SECRET_STORE_MASTER_KEY to "short" → should fail with length error
  • Manual: Set AUTH_PROVIDER=clerk without CLERK_SECRET_KEY → should fail
  • Manual: Normal startup with valid .env → should work as before

Add environment variable validation at startup for backend, worker, and
frontend services using Zod schemas. Misconfiguration is now caught
immediately with clear, actionable error messages instead of causing
silent failures or cryptic runtime errors.

Shared schemas (packages/shared/src/env.ts):
- databaseUrlSchema, temporalConfigSchema, minioConfigSchema
- secretStoreKeySchema (exactly 32 chars), kafkaBrokersSchema
- stringToBoolean helper (avoids JS coercion footgun)
- formatEnvErrors for readable console output

Backend (backend/src/config/env.schema.ts):
- Conditional: DATABASE_URL/LOG_KAFKA_BROKERS optional when
  SKIP_INGEST_SERVICES=true
- AUTH_PROVIDER normalized via transform+pipe+catch pattern
- Clerk keys required conditionally when AUTH_PROVIDER=clerk

Worker (worker/src/config/env.schema.ts):
- Required: DATABASE_URL, SECRET_STORE_MASTER_KEY, LOG_KAFKA_BROKERS
- MinIO and Temporal configs with sensible defaults

Frontend (frontend/src/config/env.schema.ts):
- VITE_API_URL defaults to localhost:3211 (test compatibility)
- All VITE_* vars optional with defaults
- Clerk key required conditionally

Test setup provides SECRET_STORE_MASTER_KEY and SKIP_INGEST_SERVICES
defaults so AppModule import doesn't fail in integration tests.

Existing ad-hoc checks in database.module.ts and dev.worker.ts are
preserved as defense-in-depth.

Signed-off-by: Aseem Shrey <LuD1161@users.noreply.github.com>
@LuD1161 LuD1161 changed the title feat(config): add Zod-based startup env validation feat(config): add Zod-based startup env validation | ENG-193 Feb 16, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2634a55fd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…p forcing SKIP_INGEST_SERVICES in test setup

Address PR review feedback:
- superRefine now checks both ENABLE_INGEST_SERVICES and SKIP_INGEST_SERVICES,
  matching the runtime guard in node-io.module.ts / trace.module.ts
- test/setup.ts provides dummy DATABASE_URL and LOG_KAFKA_BROKERS instead of
  setting SKIP_INGEST_SERVICES=true, so integration tests exercise real code paths

Signed-off-by: Aseem Shrey <LuD1161@users.noreply.github.com>
@LuD1161 LuD1161 merged commit 42e0831 into main Feb 16, 2026
3 checks passed
@LuD1161 LuD1161 deleted the LuD1161/env-var-validation branch February 16, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments