EnvDoctor Documentation
Type-check your environment before your users do. Every feature explained with examples.
Overview
EnvDoctor is a local-first developer utility that verifies whether your application's configuration is complete, valid, safe, and consistent before it reaches runtime.
Discover
Finds env vars your code actually reads, not just what's in .env files.
Validate
Checks types, ranges, formats, requiredness, and allowed values.
Compare
Detects drift between .env, .env.example, source code, and contracts.
Explain
Every finding includes file location, cause, and a suggested fix.
Quick Start
Three commands to go from zero to confidence:
# Step 1: Generate a contract from your codebase
npx envdoctor init
# Step 2: Validate your current environment
npx envdoctor check
# Step 3: Open the web dashboard
npx envdoctor web
Installation
Run without installing
npx envdoctor check
Install globally
npm install -g envdoctor
Install as a dev dependency
npm install -D envdoctor
Then add scripts to your package.json:
{
"scripts": {
"env:check": "envdoctor check",
"env:init": "envdoctor init",
"env:web": "envdoctor web"
}
}
envdoctor init
Scans your codebase and generates an envdoctor.yml environment contract.
Usage
npx envdoctor init [root]
What it does
| Step | Description |
|---|---|
| 1. Project detection | Finds your package.json, detects framework (Next.js, Express, etc.) |
| 2. Source scanning | Reads all .ts, .tsx, .js, .jsx files for process.env.X and import.meta.env.X |
| 3. Type inference | Infers types from .env.example values (integer, boolean, URL, etc.) |
| 4. Secret detection | Flags variables with names like *_SECRET, *_KEY, *_TOKEN |
| 5. Contract generation | Writes envdoctor.yml with all discovered variables |
Example output
Discovered 5 environment variables from source code.
5 high-confidence variables:
+ PORT (required)
+ DATABASE_URL (required)
+ API_KEY (required, secret)
+ ENABLE_CACHE (required)
+ NODE_ENV (required)
Contract written to ./envdoctor.yml
envdoctor check
Validates the current environment against the contract and produces a diagnostic report.
Usage
npx envdoctor check [root] [options]
Options
| Flag | Description |
|---|---|
--ci | CI mode: compact output, stable exit codes |
--format json | Machine-readable JSON output |
--format compact | Single-line summary format |
--no-color | Disable colored output |
What it checks
| Rule | What it catches | Default |
|---|---|---|
| ENV001 | Required variable is missing | Blocker |
| ENV002 | Variable declared but never used in code | Warning |
| ENV003 | Value does not match declared type | Blocker |
| ENV004 | Value outside permitted range | Blocker |
| ENV005 | Used in code but not in contract | Warning |
| ENV006 | .env.example is incomplete | Warning |
| ENV007 | Possible misspelling detected | Warning |
| ENV008 | Required variable has empty value | Blocker |
| ENV009 | Public variable contains secret | Blocker |
| CFG001 | Inconsistent values across files | Warning |
Exit codes
| Code | Meaning |
|---|---|
0 | All checks passed |
1 | Blockers found (or warnings if configured) |
envdoctor explain
Shows a detailed explanation of a specific diagnostic rule.
Usage
npx envdoctor explain ENV003
Example output
✖ ENV003 BLOCKER
PORT: expected integer, got "no********er"
Variable
PORT
Location
src/server.ts:5
How to fix
ensure PORT is a valid integer
envdoctor web
Launches a local web dashboard showing the latest check results.
Usage
npx envdoctor web [root] [-p port]
Options
| Flag | Description | Default |
|---|---|---|
-p, --port | Port to serve on | 3777 |
Dashboard features
Stat Cards
Total, blockers, warnings, info at a glance.
Status Banner
Green/yellow/red banner with summary.
Expandable Cards
Click any diagnostic to see the fix.
Animated
Staggered fade-in animations on load.
envdoctor docs
Opens this documentation in your browser.
npx envdoctor docs
Environment Contract
The contract (envdoctor.yml) is the single source of truth for your application's configuration requirements.
Full example
version: 1
service: web
variables:
DATABASE_URL:
type: url
required: true
secret: true
schemes: [postgres, postgresql]
PORT:
type: integer
required: false
default: 3000
min: 1
max: 65535
NODE_ENV:
type: enum
required: true
values: [development, test, staging, production]
ENABLE_CACHE:
type: boolean
required: false
default: false
API_KEY:
type: string
required: true
secret: true
minLength: 20
Supported types
| Type | Validates | Example |
|---|---|---|
string | Any text | hello |
integer | Whole numbers | 3000 |
number | Decimals allowed | 3.14 |
boolean | true/false/yes/no/1/0 | true |
url | Valid URL format | https://example.com |
json | Valid JSON | {"key":"val"} |
enum | One of allowed values | development |
port | 1-65535 | 3000 |
Variable options
| Option | Type | Description |
|---|---|---|
type | string | Data type (see above) |
required | boolean | Whether the variable must be set |
secret | boolean | Mark as sensitive (redacted in output) |
public | boolean | Mark as client-facing (warns if secret-like) |
default | any | Default value if not set |
values | string[] | Allowed values (for enum type) |
schemes | string[] | Allowed URL schemes (for url type) |
min | number | Minimum value |
max | number | Maximum value |
pattern | string | Regex pattern to match |
minLength | number | Minimum string length |
Rules and Diagnostics
Every diagnostic has a stable ID, severity, message, and fix suggestion.
| Rule | Category | Description |
|---|---|---|
ENV001 | Requiredness | Required variable is missing from the environment |
ENV002 | Unused | Declared in contract but never referenced in source code |
ENV003 | Type | Value cannot be parsed as the declared type |
ENV004 | Range | Value outside min/max, not in enum, or fails pattern |
ENV005 | Undeclared | Used in code but missing from the contract |
ENV006 | Example | Variable missing from .env.example |
ENV007 | Spelling | Possible misspelling relative to another declaration |
ENV008 | Empty | Required variable exists but has empty value |
ENV009 | Exposure | Public variable appears to contain a credential |
CFG001 | Consistency | Same variable has different values in .env vs .env.example |
Severity and Policy
Every rule has a default severity. You can override severity per-rule in the contract.
Severity levels
| Level | Effect |
|---|---|
| blocker | Fails the check (exit code 1) |
| warning | Reported but does not fail by default |
| info | Informational only |
Custom policy
policy:
fail_on: blocker
overrides:
SEC001: warning
ENV002: ignore
Secret Redaction
EnvDoctor never prints secret values in full. All output masks sensitive data.
Privacy guarantees
Local-first
Nothing leaves your machine during a normal scan.
No telemetry
Telemetry is opt-in only. Off by default.
No raw cache
Cached data uses fingerprints, not values.
CI safe
Secrets are masked in CI logs automatically.
CI Integration
Add EnvDoctor to your CI pipeline with one line.
GitHub Actions
- name: Check environment contract
run: npx envdoctor check --ci
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
NODE_ENV: production
GitLab CI
env-check:
script:
- npx envdoctor check --ci
Monorepo Support
For monorepos, create a separate contract per service.
packages/
web/
envdoctor.yml
src/
api/
envdoctor.yml
src/
Run checks per service:
npx envdoctor check packages/web
npx envdoctor check packages/api
Troubleshooting
"No variables found"
EnvDoctor looks for process.env.X and import.meta.env.X. If you use a custom config loader, you may need to add the contract manually.
"CLI not found"
Run npm run build before using the web command from a local install.
False positives
Use the policy.overrides section in your contract to suppress or downgrade specific rules.
policy:
overrides:
ENV002: ignore
ENV007: warning