Layer¶
Deterministic, multi-source configuration with validation, provenance tracking, and hot-reloading.
Most applications pull config from several places — a base YAML file, environment variable overrides, a secrets manager in production. Layer gives you a typed pipeline that merges all of them in a defined order, lets you ask where did this value come from? at any time, and validates with different rule sets per environment. The result is a frozen, thread-safe object with no surprises.
Highlights¶
| Feature | Description |
|---|---|
| Multi-source merging | Add providers in priority order — files, env vars, SSM, Vault, or anything custom |
| Provenance tracking | Every field records its full source history; source_of() and explain() make debugging instant |
| Categorical validation | Run different rule sets per environment (prod vs dev) without branching logic |
| Variable interpolation | ${field_name} and ${nested.path} resolved across the full config tree |
| Type coercion | str → int/bool/float/List[T]/Dict[K,V], Optional[T], Union, Literal — from env vars or files |
| Parsers | Pre-coercion transforms per field (strip whitespace, remove formatting characters, normalize paths) |
| Computed fields | Read-only derived properties included in to_dict() and explain() |
| Hot reloading | Live config updates without restart; reloadable=False locks critical fields |
| Secret redaction | secret=True fields are automatically masked in explain() and logs |
| Aliases | Map external key names (e.g. apiKey) to your Python field names |
| Export artifacts | .env templates, Kubernetes ConfigMaps, JSON Schema |
| Freeze & thread safety | freeze() locks the config object for safe concurrent reads |
Quick Example¶
from layer import layerclass, field, ConfigPipeline, require, is_port
from layer.providers import FileProvider, EnvProvider
@layerclass
class AppConfig:
host: str = field(str, default="localhost", description="Service hostname")
port: int = field(int, default=8080, prod=[require, is_port])
db_password: str = field(str, default=None, secret=True)
pipeline = (
ConfigPipeline(AppConfig)
.add_provider(FileProvider("config.yml"))
.add_provider(EnvProvider("APP")) # APP_HOST, APP_PORT, APP_DB_PASSWORD
)
config = pipeline.load()
pipeline.validate(["prod"]).raise_if_invalid()
print(config.host)
print(config.source_of("host")) # "env:APP_HOST" or "config.yml" or "default"
See Getting Started for the full walkthrough.