Skip to content

feat: centralized typed configuration with strict startup validation#117

Open
0xDeon wants to merge 1 commit intoTevaLabs:mainfrom
0xDeon:feature/config-typed-layer
Open

feat: centralized typed configuration with strict startup validation#117
0xDeon wants to merge 1 commit intoTevaLabs:mainfrom
0xDeon:feature/config-typed-layer

Conversation

@0xDeon
Copy link

@0xDeon 0xDeon commented Mar 24, 2026

Summary

Closes #93

Introduces a centralized, typed configuration module (src/config/) that replaces all scattered process.env access across the runtime codebase. The module parses environment variables once at startup, validates every field with clear error messages, and fails fast — the server will never partially boot with invalid or missing configuration.

What changed

  • New src/config/validation.ts — Reusable validation primitives (required, sensitiveRequired, port, url, positiveInt, boolean, oneOf) that collect all errors and throw once with actionable messages
  • New src/config/index.ts — Typed config entry point defining strict interfaces for every config section (App, JWT, Database, Soroban, Scheduler, Stellar, Socket), exported as a frozen singleton
  • Refactored 8 runtime files to import from config instead of reading process.env directly
  • Updated .env.example with all variables, defaults, and required/optional annotations
  • Updated README.md Environment Setup section with complete variable reference table

Config sections

Section Key fields Validation
App port, nodeEnv, clientUrl, logLevel Port range, enum values
JWT secret, expiry Required + placeholder detection (rejects your-, CHANGE_ME, etc.)
Database url Required, no default
Soroban contractId, network, rpcUrl, adminSecret, oracleSecret URL format, enum for network; optional (Soroban features silently disable if unset)
Scheduler autoResolveEnabled, autoResolveIntervalSeconds, roundSchedulerEnabled, roundSchedulerMode Boolean parsing, positive int, enum
Stellar network Enum (testnet | mainnet)
Socket clientUrl Derived from app config

Files modified

File Change
src/config/index.ts New — typed config module
src/config/validation.ts New — validation logic
src/index.ts Removed dotenv.config(), inline validateEnv(), uses config.app.*
src/utils/jwt.util.ts Removed getJwtSecret() helper and JWT_EXPIRY constant, uses config.jwt.*
src/utils/logger.ts Uses config.app.logLevel
src/lib/prisma.ts Uses config.app.nodeEnv
src/socket.ts Uses config.socket.clientUrl
src/services/soroban.service.ts Destructures config.soroban
src/services/scheduler.service.ts Uses config.scheduler.*
src/services/round-scheduler.service.ts Uses config.scheduler.*
src/services/stellar.service.ts Uses config.stellar.network
.env.example Full rewrite with all variables documented
README.md Environment Setup section rewritten

Design decisions

  1. Fail-fast with all errors at once — The validator accumulates every issue and throws a single ConfigValidationError listing them all, so developers fix everything in one pass instead of playing whack-a-mole
  2. Sensitive values reject placeholderssensitiveRequired scans for common placeholder patterns (your-, CHANGE_ME, TODO, etc.) to prevent accidental deployments with example secrets
  3. Soroban fields are optional — The existing codebase gracefully disables Soroban when config is missing, so these fields use optional() rather than required() to preserve that behavior
  4. Config is frozenObject.freeze() on the exported singleton prevents accidental mutation at runtime
  5. Test files untouched — Tests legitimately manipulate process.env to exercise services under different configurations; this is correct and expected

Audit checklist

  • All configuration accessed through a single typed module
  • No scattered process.env usage in runtime paths (only test files remain)
  • Invalid or missing config fails immediately with clear errors
  • Sensitive values (JWT_SECRET, DATABASE_URL) have no insecure defaults
  • Types are strict with union literals ("testnet" | "mainnet", "UP_DOWN" | "LEGENDS")
  • .env.example matches actual config requirements
  • README documents setup and required variables
  • npm run build succeeds without errors

Test plan

  • Verify npm run build passes
  • Start server with valid .env — should boot normally
  • Start server with missing JWT_SECRET — should fail with clear error
  • Start server with missing DATABASE_URL — should fail with clear error
  • Start server with JWT_SECRET=your-secret-here — should reject placeholder
  • Start server with PORT=abc — should fail with port validation error
  • Start server with NODE_ENV=invalid — should fail with enum error
  • Verify existing tests still pass (npm test)

Introduces src/config/ with strict TypeScript types for all runtime
configuration (app, JWT, database, Soroban, scheduler, Stellar, socket).
Parses env vars once at startup, validates required fields, rejects
placeholder secrets, and fails fast with actionable error messages.
@0xDeon 0xDeon force-pushed the feature/config-typed-layer branch from 08a78e5 to f3b8265 Compare March 24, 2026 17:15
@0xDeon
Copy link
Author

0xDeon commented Mar 24, 2026

@josephchimebuka Please review

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.

feat: add typed environment-specific configuration management

1 participant