Docs/configurationv1.0.0-rc.1

Configuration System Overview

The @tagna/udiot configuration system provides a layered, type-safe approach to managing application settings. Values are assembled from multiple sources in a defined precedence order, every value carries provenance metadata, and schema violations are surfaced before your application starts.

Key Concepts

Layered configuration — values flow through discrete layers. A higher layer always wins over a lower one, but the system records every layer that touched a key so you can audit the final value.

Schema-first design — you declare what your configuration looks like using TypeScript classes decorated with @AIConfig and @AIEnv. The framework derives environment variable names, types, validation rules, and secret-provider bindings automatically from those decorations.

Convention-based discovery — the ConfigConventionSystem scans a config/ directory for files matching *.config.ts and loads them automatically. .env, .env.{environment}, .env.local, and .env.{environment}.local files are loaded in order so local overrides never need to be committed.

Drift detection — after loading, the system can report keys that exist in the environment but have no corresponding schema field, as well as required keys that are absent. This catches configuration drift before it causes runtime errors.

Provenance tracing — every resolved value is stamped with the layer name, source identifier, and timestamp at which it was written. Sensitive values are redacted in provenance output so trace logs are safe to share.

Precedence Order (highest to lowest)

# Layer Source
1 overrides Programmatic runtime overrides passed to EnvironmentConfigManager
2 secrets Values fetched from the configured SecretProvider
3 environment process.env variables matched by the env key on each field
4 manifest YAML/JSON config file discovered in the config/ directory
5 schema-defaults Default values declared in @AIEnv({ default: … }) or as class property initialisers

Architecture Summary

┌─────────────────────────────────────────────────────┐
│                    Your Application                  │
└──────────────────────┬──────────────────────────────┘
                       │ builds
                       ▼
         ┌─────────────────────────┐
         │   ConfigSchema(s)        │  ← @AIConfig / @AIEnv decorators
         └────────────┬────────────┘
                      │ passed to
                      ▼
       ┌──────────────────────────────┐
       │  EnvironmentConfigManager    │
       │  ┌────────────────────────┐  │
       │  │  schema-defaults layer │  │
       │  │  environment layer     │  │
       │  │  manifest layer        │  │
       │  │  secrets layer         │  │
       │  │  overrides layer       │  │
       │  └────────────────────────┘  │
       └──────────────┬───────────────┘
                      │ returns
                      ▼
        ┌─────────────────────────────┐
        │       ConfigLoadResult       │
        │  values / layers / provenance│
        │  missingRequired / violations│
        └─────────────────────────────┘

Pitfalls

  • The manifest layer (YAML/JSON file) sits below secrets and environment variables. Do not store secrets in manifest files — they belong in secret providers.
  • Convention-based .env loading uses a simple line parser. It does not support multi-line values or variable interpolation. For complex values use a config file or environment variable.
  • Re-running buildConfigSchema on the same class after the module is loaded a second time (e.g., with hot-reload) will produce a fresh schema but will not automatically invalidate a previously constructed EnvironmentConfigManager. Construct a new manager instance after a schema reload.
From the canonical UDIOT public documentation.Try it in the playground ↗