Polyatic env-diff
All tools

env‑diff

Key-level .env compare & .env.example generator — entirely in your browser.

Your .env the file you actually run with
Reference .env.example what the project expects
— —
Report

Your files never leave the device. The whole compare runs in your browser; the page does load one cookieless pageview counter from stats.dankdev.com, which records the URL and never your files. Values are masked to their first two characters until you choose to reveal them.

Offline later: once you have opened this page with a connection, it reopens and works with none — your browser keeps a copy. Not saved: the very first Polyatic page you ever open (open it again while online), and any page or link you have never opened.

Generate .env.example

Blanks every value in your .env (the left pane) while keeping keys, order, comments and export prefixes — safe to commit.

Use it in your own code

The compare on this page is a published Node module — the same parser, differ and generator — so a CI step can fail a pull request the moment .env.example drifts from .env.

npm install @verifyhash/env-diff

Three lines — the same keys re-ordered and re-quoted, so only the real differences come back

const { diffKeys } = require('@verifyhash/env-diff');
const d = diffKeys('PORT=3000\nAPI_KEY=abc\nDEBUG=1\n', 'DEBUG=0\nPORT="3000"\nAPI_URL=https://x\n');
console.log(`missing ${d.onlyRight}\nextra ${d.onlyLeft}\ndiffering ${d.valueDiffers.map(v => v.key)}\nsame ${d.same}`);

prints

missing API_URL
extra API_KEY
differing DEBUG
same PORT

What the package does not include: this page — the two paste panes, the colour-coded report and the one-click .env.example download. The engine is otherwise the same build, not merely a similar one: the parse.js, diff.js and example.js inside the published 0.1.0 tarball are byte-for-byte the three files this page loads (diffed 2026-09-22). The redaction law travels with it — diffKeys returns key names only, which is why DEBUG prints as differing above without either 1 or 0 ever appearing.

@verifyhash/env-diff on npm · version 0.1.0 · MIT · also listed with our other libraries on Polyatic libs.

What env-diff actually checks

env-diff parses both panes with the same conservative dotenv-subset parser the npm package uses, then compares the parsed key sets — so the report ignores line order, comments and quote style (PORT="3000" and PORT=3000 compare as equal). It classifies every key as missing (in the reference but not your file), extra (in your file but undocumented), differing (same key, different value) or in sync, and separately flags duplicate keys (last assignment wins under dotenv semantics) and lines it will not guess at — ${VAR} interpolation, trailing-backslash continuations and malformed keys are reported as unparsed rather than silently dropped.

Values are treated as secrets. The diff core never emits a value for a differing key; the display masks every value to its first two characters until you flip “Reveal values”, and that reveal lives only in this page session — nothing is written to localStorage, cookies or anywhere else. The honest limit: because interpolated values like REDIS_URL=${HOST}:6379 are kept literal and unexpanded, env-diff can’t tell you whether they would resolve to the same value — it flags them instead.

The parser’s honest limits. The .env “format” has no single standard, so env-diff parses the common dotenv subset and is deliberately conservative — it never guesses. It is not a shell parser: it understands KEY=value, export KEY=value and single- or double-quoted values, and that’s it. There is no multiline support (a line ending in a trailing backslash is reported as an unsupported continuation, never joined), no escape-sequence decoding inside quotes (a double-quoted \n stays the two characters \ and n), and no inline comment stripping — a # is a comment only when it is the first non-blank character of a line, so in KEY=val # note the # note stays part of the value rather than the parser guessing where an unquoted value ends. When keys repeat, diffing uses the effective last-assignment-wins value (matching dotenv runtime semantics) while still surfacing the duplicates separately. Anything else lands in the unparsed list with a plain-language reason — the tool would rather tell you it didn’t understand a line than pretend it did.