Sync
.envfiles without leaking secrets.
EnvDrift is a CLI tool that automatically syncs your .env file to .env.example while intelligently scrubbing sensitive values. It detects secrets from 30+ providers including AWS, Stripe, GitHub, OpenAI, and database connection strings.
EnvDrift also has a VS Code extension for real-time drift detection!
Features:
- π Status Bar - See drift status at a glance
- β‘ Auto-Check - Automatically detects drift when files change
- π One-Click Sync - Sync directly from VS Code
- π Diff View - Side-by-side comparison
β οΈ Problems Panel - Integrates with VS Code diagnostics
Install: Search "EnvDrift" in VS Code Extensions or run:
code --install-extension sol-21.envdrift-vscode- π Smart Detection - Identifies sensitive values by key name AND value patterns
- π’ Provider Detection - Recognizes secrets from AWS, Stripe, GitHub, PostgreSQL, MongoDB, OpenAI, and 30+ more
- π JSON Output - Machine-readable output for CI/CD tooling
- π Quiet Mode - Suppress all output except errors
- π Multi-file Support - Scan
.env,.env.local,.env.development, etc. - π Diff Command - Visual comparison between files
- ποΈ Watch Mode - Auto-sync on file changes
- ποΈ Interactive Mode - Approve each change individually
- π― Strict Mode - Scrub ALL values for maximum security
- π Dry Run - Preview changes before modifying files
- βοΈ Config File - Project-level
.envdriftrc.jsonfor team consistency - π Ignore List - Keep certain keys unmodified
- π Merge Mode - Add new keys without overwriting existing entries
- π¬ Comment Preservation - Keeps your documentation intact
- π CI/CD Ready - Proper exit codes and minimal output mode
- πͺ Git Hooks - Pre-commit hook to prevent drift
# Use directly with npx (no install needed)
npx envdrift sync
# Or install globally
npm install -g envdrift
# Or add to your project
npm install --save-dev envdrift# Check for drift between .env and .env.example
npx envdrift check
# Sync .env.example with smart scrubbing
npx envdrift sync
# Preview changes without modifying files
npx envdrift sync --dry-run
# Scrub ALL values (paranoid mode)
npx envdrift sync --strict
# Interactive sync - approve each change
npx envdrift sync --interactive
# Watch for changes and auto-sync
npx envdrift sync --watchDetect drift between your .env and .env.example files.
envdrift check
envdrift check --input .env.local --output .env.local.example
envdrift check --ci # CI mode with proper exit codes
envdrift check --json # JSON output for tooling
envdrift check --quiet # Minimal output
envdrift check --all # Check all .env filesOptions:
| Option | Description |
|---|---|
-i, --input <file> |
Input file (default: .env) |
-o, --output <file> |
Output file (default: .env.example) |
--ci |
CI mode - minimal output, exit code 1 on drift |
--json |
Output results as JSON |
-q, --quiet |
Suppress all output except errors |
-a, --all |
Check all .env files (.env, .env.local, etc.) |
Sync and scrub your .env.example file.
envdrift sync
envdrift sync --dry-run # Preview changes
envdrift sync --strict # Scrub all values
envdrift sync --interactive # Approve each change
envdrift sync --watch # Auto-sync on changes
envdrift sync --json # JSON output
envdrift sync --merge --sort # Merge and sort keys
envdrift sync --ignore NODE_ENV DEBUGOptions:
| Option | Description |
|---|---|
-i, --input <file> |
Input file (default: .env) |
-o, --output <file> |
Output file (default: .env.example) |
-d, --dry-run |
Preview changes without modifying files |
-s, --strict |
Scrub ALL values regardless of key name |
--ci |
CI mode - minimal output |
-m, --merge |
Add new keys without removing existing |
--sort |
Sort keys alphabetically |
--ignore <keys...> |
Keys to never scrub |
--no-preserve-comments |
Don't preserve comments |
--json |
Output results as JSON |
-q, --quiet |
Suppress all output except errors |
-I, --interactive |
Interactive mode - approve each change |
-w, --watch |
Watch mode - auto-sync on file changes |
Show visual diff between .env and .env.example.
envdrift diff
envdrift diff --changes-only # Only show differences
envdrift diff --json # JSON outputOptions:
| Option | Description |
|---|---|
-i, --input <file> |
Input file (default: .env) |
-o, --output <file> |
Output file (default: .env.example) |
--json |
Output results as JSON |
-q, --quiet |
Suppress all output except errors |
-c, --changes-only |
Only show changes, hide unchanged keys |
Scan project for all .env files.
envdrift scan
envdrift scan --jsonOptions:
| Option | Description |
|---|---|
--json |
Output results as JSON |
-q, --quiet |
Suppress all output except errors |
Initialize EnvDrift in your project with an interactive wizard.
envdrift init # Interactive wizard
envdrift init --yes # Skip wizard, use defaults
envdrift init --hook # Also setup pre-commit hook
envdrift init --force # Overwrite existing configOptions:
| Option | Description |
|---|---|
-f, --force |
Overwrite existing config file |
--hook |
Setup git pre-commit hook |
-y, --yes |
Skip wizard, use default config |
Create .envdriftrc.json in your project root (or use envdrift init wizard):
{
"input": ".env",
"output": ".env.example",
"strict": false,
"ignore": ["NODE_ENV", "DEBUG", "LOG_LEVEL"],
"alwaysScrub": ["INTERNAL_API_KEY"],
"sensitiveKeywords": ["custom_secret"],
"customPatterns": [
{ "name": "MyService", "pattern": "^myservice_[a-z0-9]{32}$" }
],
"preserveComments": true,
"merge": false,
"sort": false,
"placeholderFormat": "YOUR_{KEY}_HERE"
}| Option | Type | Default | Description |
|---|---|---|---|
input |
string | .env |
Input file path |
output |
string | .env.example |
Output file path |
strict |
boolean | false |
Scrub all values |
ignore |
string[] | [] |
Keys to never scrub |
alwaysScrub |
string[] | [] |
Keys to always scrub |
sensitiveKeywords |
string[] | [] |
Custom sensitive keywords |
customPatterns |
array | [] |
Custom regex patterns for secret detection |
preserveComments |
boolean | true |
Preserve comments |
merge |
boolean | false |
Merge mode |
sort |
boolean | false |
Sort keys alphabetically |
groupByPrefix |
boolean | false |
Group keys by prefix |
placeholderFormat |
string | YOUR_{KEY}_HERE |
Placeholder template |
EnvDrift automatically detects and scrubs secrets from these providers:
| Provider | Pattern |
|---|---|
| AWS | Access Key ID (AKIA...), Secret Access Key |
| Stripe | sk_live_*, sk_test_*, pk_*, rk_*, whsec_* |
| GitHub | ghp_*, gho_*, ghu_*, ghs_*, ghr_*, github_pat_* |
| GitLab | glpat-*, glptt-* |
| OpenAI | sk-... (48 chars) |
| Anthropic | sk-ant-* |
| Clerk | sk_live_*, sk_test_*, pk_live_*, pk_test_* |
| Supabase | JWT tokens starting with eyJ... |
| Twilio | Account SID (AC...), Auth Token |
| SendGrid | SG.*.* |
| Mailgun | key-* |
| Mailchimp | *-us* API keys |
| Slack | xox[baprs]-*, webhook URLs |
| Discord | Webhook URLs |
API Keys (AIza*), OAuth Client IDs |
|
| NPM | npm_* tokens |
| Heroku | UUID-format API keys |
| Databases | PostgreSQL, MySQL, MongoDB, Redis, SQLite connection strings |
| JWT | eyJ*.*.* tokens |
| Private Keys | PEM format (-----BEGIN PRIVATE KEY-----) |
name: EnvDrift Check
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npx envdrift check --ci# Get drift status as JSON
npx envdrift check --json
# Get sync preview as JSON
npx envdrift sync --dry-run --json
# Get diff as JSON
npx envdrift diff --jsonExample JSON output:
{
"synced": false,
"missingInExample": ["NEW_API_KEY"],
"missingInEnv": ["OLD_KEY"],
"envKeyCount": 10,
"exampleKeyCount": 9
}# Setup automatically
npx envdrift init --hook
# Or add manually to .git/hooks/pre-commit
npx envdrift check --ci# Watch for changes and auto-sync
npx envdrift sync --watch# Check for drift
npx envdrift check
# If drift detected, sync
npx envdrift sync
# Or preview first
npx envdrift sync --dry-run# Initialize project with config + pre-commit hook
npx envdrift init --hook# Scrub ALL values, no exceptions
npx envdrift sync --strict# Approve each change individually
npx envdrift sync --interactive# Scan all .env files
npx envdrift scan
# Check all .env files at once
npx envdrift check --all
# Sync specific file
npx envdrift sync -i .env.local -o .env.local.exampleEnvDrift can be used as a library in your Node.js projects:
npm install envdriftimport {
parseEnvContent,
detectDrift,
generateSyncedExample,
detectProviderSecret,
isSensitiveKey,
} from 'envdrift';
// Parse .env file content
const envContent = `
API_KEY=sk_live_abc123
DATABASE_URL=postgres://user:pass@localhost/db
NODE_ENV=development
`;
const entries = parseEnvContent(envContent);
console.log(entries);
// [
// { key: 'API_KEY', value: 'sk_live_abc123', line: 2 },
// { key: 'DATABASE_URL', value: 'postgres://user:pass@localhost/db', line: 3 },
// { key: 'NODE_ENV', value: 'development', line: 4 }
// ]import { parseEnvContent, extractKeys, detectDrift } from 'envdrift';
import fs from 'fs';
const envContent = fs.readFileSync('.env', 'utf-8');
const exampleContent = fs.readFileSync('.env.example', 'utf-8');
const envKeys = extractKeys(parseEnvContent(envContent));
const exampleKeys = extractKeys(parseEnvContent(exampleContent));
const drift = detectDrift(envKeys, exampleKeys);
console.log(drift);
// {
// isSynced: false,
// missingInExample: ['NEW_KEY'],
// missingInLocal: ['REMOVED_KEY'],
// envKeys: [...],
// exampleKeys: [...]
// }import { parseEnvContent, generateSyncedExample } from 'envdrift';
import fs from 'fs';
const envContent = fs.readFileSync('.env', 'utf-8');
const envEntries = parseEnvContent(envContent);
const exampleContent = fs.readFileSync('.env.example', 'utf-8');
const exampleEntries = parseEnvContent(exampleContent);
const result = generateSyncedExample(envEntries, exampleEntries, {
strictMode: false,
ignore: ['NODE_ENV', 'DEBUG'],
preserveComments: true,
});
console.log(result.content); // Scrubbed .env.example content
console.log(result.added); // Keys added
console.log(result.removed); // Keys removed
console.log(result.entries); // Detailed entry info with scrub reasons
// Write to file
fs.writeFileSync('.env.example', result.content);import { detectProviderSecret, isSensitiveKey } from 'envdrift';
// Detect secrets by value pattern
detectProviderSecret('sk_live_abc123xyz');
// Returns: 'Stripe Secret Key'
detectProviderSecret('ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
// Returns: 'GitHub Personal Access Token (Classic)'
detectProviderSecret('postgres://user:pass@localhost/db');
// Returns: 'PostgreSQL Connection String'
detectProviderSecret('hello-world');
// Returns: null (not a known secret pattern)
// Check if key name is sensitive
isSensitiveKey('DATABASE_PASSWORD'); // true
isSensitiveKey('API_KEY'); // true
isSensitiveKey('APP_NAME'); // false
isSensitiveKey('MY_CUSTOM_SECRET', ['custom_secret']); // true (with custom keywords)import { detectProviderSecret } from 'envdrift';
// Use custom patterns
const customPatterns = [
{ name: 'MyService', pattern: '^myservice_[a-z0-9]{32}$' },
{ name: 'InternalToken', pattern: '^internal_token_[A-Z0-9]+$' },
];
detectProviderSecret('myservice_abc123def456ghi789jkl012mno345', customPatterns);
// Returns: 'Custom: MyService'Parse .env file content into structured entries.
Extract just the key names from parsed entries.
Compare two sets of keys and return drift information.
Generate scrubbed .env.example content.
Check if a value matches known secret patterns.
Check if a key name indicates sensitive data.
Convert config file format to sync options.
interface EnvEntry {
key: string;
value: string;
line: number;
comment?: string;
precedingComments?: string[];
}
interface DriftResult {
isSynced: boolean;
missingInExample: string[];
missingInLocal: string[];
envKeys: string[];
exampleKeys: string[];
}
interface SyncOptions {
strictMode?: boolean;
ignore?: string[];
alwaysScrub?: string[];
customSensitiveKeywords?: string[];
customPatterns?: { name: string; pattern: string }[];
preserveComments?: boolean;
merge?: boolean;
sort?: boolean;
placeholderFormat?: string;
}MIT Β© sol-21