Configuration, owned

Every environment variable has an owner, a reason to exist, and a lifecycle you can query.

A secret manager decides where a value lives. A validator decides whether it is well-formed. Neither says who owns it, why it exists, or when it expires.

env-cap treats configuration as a capability-owned contract: each part of your code declares the variables it owns, and a build step turns those declarations into a project-wide ownership and lifecycle report.

Give every team a clear owner and expiry date for each environment variable, alongside the validator and secret manager you already use.

Pre-1.0 · v0.7.1 · MIT

Quick start
npm install @maverickcer/env-cap
npx env-cap init

The runtime validates your variables at startup. The build step produces the ownership report. Nothing replaces dotenv, Zod, envalid, t3-env or your secret manager.

Why it exists

Secret managers and validators answer different questions.

A secret manager decides where a value lives. A validator decides whether a value is well-formed. Neither answers why a variable exists and which capability depends on it, who owns it and who to page when it must rotate, or whether it is still needed.

Where the answers live today: a wiki page, a Slack thread, someone's memory, a leftover from a feature that moved on — no executable source of truth.

env-cap makes the answer part of the code: each capability declares the environment it owns, and a build-time pass turns every declaration into a project-wide ownership and lifecycle view.

How it works

Runtime and build-time stay separate on purpose.

Runtime receives only what the application requires: no dependencies and no filesystem access. Build tooling creates the visibility needed to understand and maintain dependencies, as an explicit development or CI step, never at startup.

A capability's schema file branches into a runtime path for validated application access and a build path for generated documentation and visibility artifacts.

The build package parses schema files as TypeScript ASTs and never imports or executes them. That static-analysis boundary stops unfamiliar code from running as a side effect of generating documentation, manifests and dependency insight.

See it

Not a mockup: real output from this repository.

Run against examples/application in this repo:

npx env-cap --location ... --evidence ...
Wrote manifest: src/generated/env.manifest.ts
Discovered 1 contract(s).
Wrote evidence: docs/env.evidence.json

[generate-docs] wrote docs/ENVIRONMENT.md (1 contract(s)), .env.example, and docs/OWNERSHIP.md (0 abandoned, 2 unconsumed owned variable(s)).

1 variable(s)/contract(s) expiring soon or already expired:
- STRIPE_KEY in env: 2026-09-01 (expired Nd ago)

2 unconsumed owned variable(s):
- DATABASE_URL in app
- STRIPE_KEY in app

Source: examples/application

Not "CI failed": a rotation deadline that already passed, and two variables a capability declares owning but nothing in the codebase reads. Both are real, provable facts about this fixture. N is the number of days since 2026-09-01, counted from the day you run it.

Guarantees

What is guaranteed, and where it stops.

Each claim states its scope and links to the document that defines it, so you can check the boundary before you rely on it.

The runtime has no dependencies and no filesystem access.Scope: True of the runtime entry points, which are held to a size budget in CI. The build entry is separate: it bundles a TypeScript 6 compiler (about 24 MB unpacked), runs only in development and CI, and a build step fails if a runtime entry can reach it.Read the adoption guide ↗
The build tool never executes your schema files.Scope: Schema files are parsed as TypeScript ASTs and never imported or evaluated; only literal values are resolved, and anything dynamic is reported as unresolved. It does not sandbox the rest of your build or your own code.Read the security model ↗
The runtime never persists, retrieves, rotates or logs secrets.Scope: Values live in memory and contracts redact themselves when logged or serialized. Storing, rotating and auditing secrets stay your infrastructure's job, and it cannot stop a validator you write from logging.Read the security model ↗

Why not just dotenv, Zod, envalid, or t3-env?

You keep all of them.

env-cap doesn't replace your loader or your validator. It adds the layer none of them cover. Plug an existing Validator<T> or Processor<T> function straight into a contract, or use env-cap's lightweight helpers when a validation library is more than the variable needs.

Where common approaches stop and env-cap begins
Tool What it solves What it doesn't
dotenv Loads values from a .env file into process.env Says nothing about who owns a value, whether it's still used, or when it expires
Zod / envalid / t3-env Validates that a value has the right shape Same gap: a well-typed variable can still be an orphaned, unowned, or expired one
A secret manager Decides where a value physically lives and how it's rotated Says nothing about which capability in your code actually depends on it

Adoption

From one schema to capability-owned contracts.

Most applications start with a single, centralized schema, and that is a fine place to stay if it's working. As ownership boundaries start to matter, contracts move closer to the capability that consumes them, with no change to the runtime model.

A progressive path, not a rewrite

  1. Start with one centralized schema and run npx env-cap init.
  2. Generate the manifest, documentation and ownership report in CI.
  3. As ownership boundaries matter, move each contract next to the capability that consumes it. The runtime model does not change.

You probably don't need it when

Your application is small, one team owns every configuration decision, environment variables are few and stable, and you already have enough visibility into your dependencies. A validation library alone, or plain dotenv, may be all you need.

For decision-makers

A one-page brief for your manager

env-cap gives every configuration setting an owner, a reason to exist and an expiry date, and shows what is unused or overdue.

It answers what this is, why it matters, what it costs, what could go wrong and what is being asked, in plain language, on one printable page.

Try it

Trial it on one application.

Read the README for a first contract, or run an example to see the report it produces.

env-cap is pre-1.0. Per VERSIONING.md, a minor release may still include a breaking change to the Stable tier before 1.0, so pin accordingly and read the changelog on every minor upgrade.