Skip to content
Local-first · no network · no telemetry

Catch environment drift
before it ships.

A consistency checker that reconciles the environment variables your code uses against the ones your .env files define — and flags every mismatch.

one tool · six languages

npm install -g @arunskg/envdoctor

One tool, six languages

Published as a standalone native port for each ecosystem — no Node required, no wrappers. Pick yours.

Node reference implementation

View on npm →
Install
npm install -g @arunskg/envdoctor
Detects

process.env.X · import.meta.env.X

Every native port is at full feature parity with the Node reference — all ten detectors (missing, unused, duplicates, public-prefix secret leaks, weak secrets, typos, environment diffs, type mismatches, schema validation), Docker Compose / Kubernetes / GitHub Actions scanning, the scan/diff/sync/init/fix subcommands, and a --json mode — with byte-identical output across languages.

Every port shares the same CLI: envdoctor scan --dir . and exits 1 on errors.

See exactly what drifted

One command reconciles every source, then prints a readable audit. Values are never shown.

What it checks

A suite of detectors runs over one normalized model of every place a variable can appear.

Missing / undefined

A variable is referenced in source code, Compose, Kubernetes, or Actions but never defined in any .env file.

Error

Unused variables

Defined in a .env file but never referenced anywhere in your code or config — dead entries that drift out of sync.

Warning

Duplicates

The same key defined twice in one file, or across files sharing one environment label — a classic source of “which value wins?”.

Error

Type mismatch

Incompatible inferred types across environments, or a value that fails its own inferred type — e.g. PORT expected integer, found string.

Error

Public-prefix leak

A secret-looking variable uses a public framework prefix (NEXT_PUBLIC_*, VITE_*) and would ship straight into a client bundle.

Error

Local-first & private

Everything runs on your machine. No network calls, no telemetry, and variable values are never printed or written to any artifact.

By design

Getting started

Give envdoctor a directory, and it reconciles your code against your environment files.

.env
NODE_ENV=development
NODE_ENV=production   # duplicate
DATABASE_URL=postgres://localhost/app
PORT=three-thousand   # not an integer
DEBUG_MODE=true       # never used
src/index.ts
const db   = process.env.DATABASE_URL;
const port = process.env.PORT;
const flag = process.env.NEW_FEATURE_FLAG;
//                       ^ not defined
//                         in any .env file
1

Bootstrap your project

envdoctor init creates a config, a generated .env.example, and ENVIRONMENT.md docs — never overwriting existing files.

2

Scan for issues

envdoctor scan --dir . runs every detector and prints the audit above. Exits 0 when clean, 1 when errors are found.

3

Tighten for CI

envdoctor scan --strict promotes warnings to errors too, so unused vars and other warnings also fail the build.

4

Drop it into your pipeline

Because it exits non-zero on errors, a single line in CI blocks any merge that introduces environment drift — no plugin, no service, no config server.

ci.yml
# fail the build on any drift
- name: Check environment consistency
  run: envdoctor scan --strict

Why local-first

Environment variables are where secrets live. A checker that reads them should never be a new place they can leak.

No network

Everything runs on your machine. envdoctor makes zero outbound requests — nothing to review in a proxy log, nothing to allowlist.

No telemetry

No usage pings, no analytics, no phone-home. What you scan stays entirely with you.

Values never printed

Reports reference variables by name and location only. Secret values are never printed to the terminal or written into generated files.

CI-friendly by default

A clean exit code contract — 0 for pass, 1 for errors — means it drops into any pipeline without a wrapper.