Case study 26 / 26
dotenv-doctor
Validates .env files against the committed .env.example — catching the configuration mistakes that only surface after a deploy.
- Status
- Released
- Period
- 2026
- Domain
- tools · security
- Language
- Python
- Last push
- 30 AUG 2026
- License
- MIT
- Source of claims
- README.md, src/dotenv_doctor/checks.py, src/dotenv_doctor/report.py, .github/workflows/ci.yml
A dependency-free Python CLI that treats .env.example as a contract: missing keys, unfilled placeholders, weak secrets, duplicate assignments, stray whitespace and real credentials committed to the example file are all reported — as text, JSON or GitHub annotations.
01/The problem
.env.example is a promise — these are the variables this service needs — and nothing enforces it. The file drifts, and every failure looks the same: a service that boots locally and dies in staging.
02/The system
A dependency-free Python CLI that treats .env.example as a contract: missing keys, unfilled placeholders, weak secrets, duplicate assignments, stray whitespace and real credentials committed to the example file are all reported — as text, JSON or GitHub annotations.
- cli.py to parser.py
- parser.py to checks.py
- checks.py to report.py
03/Implementation
- 01Example-contract checking: keys missing from the real file are errors, undocumented keys are warnings.
- 02Placeholder detection against ~25 template values, matched against the whole value so a real secret containing “changeme” is not flagged.
- 03Weak-secret detection for credential-shaped keys only, combining length with Shannon entropy.
- 04Nine live-credential shapes — AWS, GitHub, Slack, Stripe, OpenAI, Anthropic, Google, PEM private keys, JWTs — reported as errors when found in the example file.
- 05A parser for the awkward cases: export prefixes, inline comments, single vs double quoting with escapes, values containing =, and multi-line quoted values.
- 06Text, JSON and GitHub Actions output; exit codes 0 clean, 1 findings, 2 usage error.
04/Engineering
Four modules, one responsibility each
parser → checks → report → cli. Checks are pure functions over parsed data, so every rule is tested without touching a filesystem, and a new output format can never change a rule.
The intersection, not a shell
The parser implements only what mainstream loaders agree on (python-dotenv, docker-compose, foreman, direnv). Anything ambiguous becomes a parse issue rather than a guess.
False positives are bugs
Entropy is a weak signal used only alongside the key name — PORT=3000 is short and low-entropy, and is correctly left alone.
05/Interface
No product screenshots are published for this project. The visual above is a code-driven representation of how it behaves, built from the repository source — not a screenshot.
06/Tech stack
- Python 3.10+
- Zero runtime dependencies
- pytest
- mypy (strict)
- ruff
- GitHub Actions
07/Result
Verified outcomes
- Released under MIT with CI, a strictly typed codebase, and tests for the parser, every rule and the CLI end to end.
- Usable as a CI gate: --format github annotates findings inline on pull requests.
08/Links
Next case study
DataJewellers →