Command line tool
One table for .env, .env.example and the environment
that is actually deployed. Values are never printed. They are reduced to a keyed digest and
compared by that, so the table can go in a pull request. It exits nonzero when the three
disagree, which is what makes it useful in a pre-deploy hook.
| key | .env | .env.example | deployed | verdict |
|---|
This block is the tool's real stdout, captured when this page was built. Nothing in it was typed by hand.
| verdict | means | why it matters |
|---|
vercel env ls cannot tell youvercel env ls prints names and the word Encrypted. It never prints a value. So in
that mode presence can be compared and equality cannot, and those keys are reported as
present but not comparable rather than as agreement. The
same fixture read with vercel env pull, which does return values, resolves them.
Below is the same set of keys under both modes.
| key | presence only (env ls) | values readable (env pull) |
|---|
A tool that reported those keys as matching would be green on a production secret that had drifted, which is the failure it exists to catch.
A row carries the key name, whether the value is set, how long it is, and eight hexadecimal characters of an HMAC-SHA256 keyed with a salt that is regenerated on every run. Two rows with the same digest had the same value during that run. The digest means nothing in the next run and nothing in anybody else's.
The salt matters more than it looks. A plain SHA-256 of a short value is not a mask:
true, 1, postgres and a few thousand other plausible
settings can be hashed and looked up in under a second, so a table of unsalted digests hands over
the values it claims to hide. --salt pins the salt for reproducible output, and the
help text says what that costs.
Equality is decided on the full digest, never on the eight characters shown. Eight hex characters are 32 bits, and a few thousand keys make a collision likely enough to meet in practice. The test suite carries a pair of different values that produce the same visible prefix, and asserts the tool still calls them different.
npx envdiff-three-way # .env vs .env.example vs vercel env ls
envdiff --remote-source pull # compare values, not just presence
envdiff --no-remote # the local two-way diff, no Vercel needed
envdiff --remote-cmd "flyctl secrets list" # any provider that can print dotenv
envdiff --format markdown >> "$GITHUB_STEP_SUMMARY"
Exit codes: 0 the three columns agree, 1 drift was found,
2 the comparison could not be made. The third one is the point. A checker that cannot
reach the deployed environment and exits 0 has told a deploy script that everything is fine.