A comprehensive CLI tool for managing .env files — validation, secret scanning, format conversion, and migration to cloud secret managers.
📚 Documentation | 🌐 Website
I built this after accidentally pushing AWS credentials to GitHub in a test file during an Airflow refactor (20 DAGs, 300+ Scrapy spiders). The key was revoked immediately, other services went down, and I had to explain the incident to my development head. That conversation was more painful than any billing alert.
Three years later, I'm still paranoid about secrets management. This tool is the safety net I wish I'd had.
All features are production-ready and working in v0.1.0!
- ✅
init- Interactive project setup with templates for Python, Node.js, Rust, Go, PHP - ✅
add- Option to add variables to your .env file , custom, blueprint, services - ✅
validate- Comprehensive validation (checks for placeholders, weak secrets, misconfigurations) - ✅
scan- Secret detection using pattern matching and entropy analysis - ✅
diff- Compare.envand.env.example, show missing/extra variables - ✅
convert- Transform to 14+ formats (JSON, YAML, Docker, Kubernetes, AWS, GCP, Azure, GitHub Actions, and more) - ✅
sync- Keep.envand.env.examplein sync (bidirectional)
- ✅
migrate- Direct migration to secret managers (GitHub Actions, AWS Secrets Manager, Doppler, Infisical) - ✅
doctor- Diagnose common setup issues - ✅
template- Generate config files from templates with variable substitution - ✅
backup- Create AES-256-GCM encrypted backups - ✅
restore- Restore from encrypted backups
Build with all features:
cargo build --features full
# or
cargo build --all-featurescurl -sSL https://raw.githubusercontent.com/urwithajit9/evnx/main/install.sh | bashPrerequisites: Install Rust first.
# Clone the repository
git clone https://github.com/urwithajit9/evnx.git
cd evnx
# Build and install with core features
cargo install --path .
# Or build with all features
cargo install --path . --features full
# Verify installation
evnx --versionNote: Add %USERPROFILE%\.cargo\bin to your PATH if not already done during Rust installation.
Tested on: Windows 10/11 with PowerShell 5.1+
# Install with core features only
cargo install evnx
# Install with all features
cargo install evnx --features fullevnx --version
evnx --help# 1. Initialize a new project
evnx init
# 2. Validate your configuration
evnx validate --strict
# 3. Scan for accidentally committed secrets
evnx scan
# 4. Compare files
evnx diff --show-values
# 5. Convert to different formats
evnx convert --to json > config.json
evnx convert --to github-actions
evnx convert --to kubernetes > secret.yaml
# 6. Keep files in sync
evnx sync --direction forward- Getting Started Guide - Complete walkthrough with examples
- Use Cases - Real-world scenarios
- CI/CD Integration - GitLab, GitHub Actions, Jenkins
- Architecture - System design and internals
- Contributing - How to contribute
Interactive project setup - Generates .env.example with sensible defaults.
evnx init # Interactive modeSupported stacks: Python, Node.js, Rust, Go, PHP Supported services: PostgreSQL, Redis, MongoDB, MySQL, RabbitMQ, Elasticsearch, AWS S3, Stripe, SendGrid, OpenAI, and more
Comprehensive validation - Catches misconfigurations before deployment.
evnx validate # Pretty output
evnx validate --strict # Fail on warnings
evnx validate --format json # JSON output
evnx validate --format github-actions # GitHub annotationsDetects:
- ❌ Missing required variables
- ❌ Placeholder values (
YOUR_KEY_HERE,CHANGE_ME) - ❌ Boolean string trap (
DEBUG="False"is truthy!) - ❌ Weak
SECRET_KEY(too short, common patterns) - ❌
localhostin production - ❌ Suspicious port numbers
Secret detection - Find accidentally committed credentials.
evnx scan # Scan current directory
evnx scan --path src/ # Specific directory
evnx scan --format sarif # SARIF for GitHub Security
evnx scan --exit-zero # Don't fail CIDetects 8+ secret types:
- AWS Access Keys (
AKIA...) - Stripe API Keys (live & test)
- GitHub Personal Access Tokens
- OpenAI API Keys
- Anthropic API Keys
- Private Keys (RSA, EC, OpenSSH)
- High-entropy strings (potential secrets)
- Generic API keys
SARIF output integrates with GitHub Security tab!
File comparison - See what's different between environments.
evnx diff # Compare .env and .env.example
evnx diff --show-values # Show actual values
evnx diff --reverse # Swap comparison
evnx diff --format json # JSON outputFormat conversion - Transform to 14+ output formats.
evnx convert --to json # Generic JSON
evnx convert --to yaml # Generic YAML
evnx convert --to shell # Shell export script
evnx convert --to docker-compose # Docker Compose format
evnx convert --to kubernetes # Kubernetes Secret YAML
evnx convert --to terraform # Terraform .tfvars
evnx convert --to github-actions # GitHub Actions format
evnx convert --to aws-secrets # AWS Secrets Manager
evnx convert --to gcp-secrets # GCP Secret Manager
evnx convert --to azure-keyvault # Azure Key Vault
evnx convert --to heroku # Heroku Config Vars
evnx convert --to vercel # Vercel Environment Variables
evnx convert --to railway # Railway JSON
evnx convert --to doppler # Doppler formatAdvanced options:
evnx convert --to json \
--output secrets.json \ # Write to file
--include "AWS_*" \ # Filter variables
--exclude "*_LOCAL" \ # Exclude patterns
--prefix "APP_" \ # Add prefix
--transform uppercase \ # Transform keys
--base64 # Base64-encode valuesReal-world example - Deploy to AWS:
evnx convert --to aws-secrets | \
aws secretsmanager create-secret \
--name prod/myapp/config \
--secret-string file:///dev/stdinBidirectional sync - Keep .env and .env.example aligned.
# Forward: .env → .env.example (document what you have)
evnx sync --direction forward --placeholder
# Reverse: .env.example → .env (generate from template)
evnx sync --direction reverseUse cases:
- Generate
.envfrom.env.examplein CI/CD - Update
.env.examplewhen adding new variables - Maintain documentation
Cloud migration - Move secrets directly to secret managers.
# GitHub Actions Secrets
evnx migrate \
--from env-file \
--to github-actions \
--repo owner/repo \
--github-token $GITHUB_TOKEN
# AWS Secrets Manager
evnx migrate \
--to aws-secrets-manager \
--secret-name prod/myapp/config
# Doppler
evnx migrate \
--to doppler \
--dry-run # Preview changes firstFeatures:
- ✅ Conflict detection (skip or overwrite)
- ✅ Dry-run mode
- ✅ Progress tracking
- ✅ Encrypted uploads (GitHub uses libsodium)
Health check - Diagnose common issues.
evnx doctor # Check current directory
evnx doctor --path /path/to/projectChecks:
- ✅
.envexists and has secure permissions - ✅
.envis in.gitignore - ✅
.env.exampleexists and is tracked by Git - ✅ Project structure detection (Python, Node.js, Rust, Docker)
Template generation - Dynamic config file creation.
evnx template \
--input config.template.yml \
--output config.yml \
--env .envSupports filters:
# config.template.yml
database:
host: {{DB_HOST}}
port: {{DB_PORT|int}}
ssl: {{DB_SSL|bool}}
name: {{DB_NAME|upper}}Encrypted backups - AES-256-GCM encryption with Argon2 key derivation.
# Create backup
evnx backup .env --output .env.backup
# Restore
evnx restore .env.backup --output .envSecurity:
- AES-256-GCM encryption
- Argon2 password hashing
- No secrets in plaintext
evnx currently does not support array-like or list-like values in .env files. This affects Django and other frameworks that use python-dotenv's extended syntax.
Will fail:
# Arrays with brackets
CORS_ALLOWED=["https://example.com", "https://admin.example.com"]
# Multiline values
DATABASE_HOSTS="""
host1.example.com
host2.example.com
"""
# JSON values
CONFIG={"key": "value", "nested": {"data": 123}}Workaround:
# Use comma-separated strings instead
CORS_ALLOWED=https://example.com,https://admin.example.com
# Or use base64-encoded JSON
CONFIG_JSON=eyJrZXkiOiJ2YWx1ZSJ9
# Parse in your application code
# Python example:
import os
import json
cors_allowed = os.getenv("CORS_ALLOWED", "").split(",")
config = json.loads(base64.b64decode(os.getenv("CONFIG_JSON")))Why this limitation?
evnx follows the strict .env format specification which defines values as simple strings. Django's python-dotenv uses extended parsing that's not compatible with the standard format used by most other tools.
Tracking: We're considering adding a --lenient or --extended flag for compatibility. Follow Issue #XX for updates.
Affects:
- Django projects using complex ALLOWED_HOSTS or CORS settings
- Projects with JSON/YAML embedded in .env values
- Multiline string values
Does NOT affect:
# These work fine
DATABASE_URL=postgres://localhost/db
API_KEYS=key1,key2,key3 # Simple comma-separated
ALLOWED_HOSTS=example.com admin.example.com # Space-separated
DEBUG=True
PORT=3000- File permissions checking is limited on Windows (no Unix permissions)
- Path handling uses backslashes (handled internally)
- Some terminal color codes may not display correctly in older CMD (use PowerShell or Windows Terminal)
These are tracked and will be improved in future releases.
name: Validate Environment
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install evnx
run: |
curl -sSL https://raw.githubusercontent.com/urwithajit9/evnx/main/install.sh | bash
- name: Validate configuration
run: evnx validate --strict --format github-actions
- name: Scan for secrets
run: evnx scan --format sarif > scan-results.sarif
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: scan-results.sarifvalidate-env:
stage: validate
image: alpine:latest
before_script:
- apk add --no-cache curl bash
- curl -sSL https://install.dotenv.space | bash
script:
- evnx validate --strict --format json
- evnx scan --format sarif > scan.sarif
artifacts:
reports:
sast: scan.sarif# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: dotenv-validate
name: Validate .env files
entry: evnx validate --strict
language: system
pass_filenames: false
- id: dotenv-scan
name: Scan for secrets
entry: evnx scan --exit-zero
language: system
pass_filenames: falseStore preferences in .evnx.toml:
[defaults]
env_file = ".env"
example_file = ".env.example"
verbose = false
[validate]
strict = true
auto_fix = false
format = "pretty"
[scan]
ignore_placeholders = true
exclude_patterns = ["*.example", "*.sample", "*.template"]
format = "pretty"
[convert]
default_format = "json"
base64 = false
[aliases]
gh = "github-actions"
k8s = "kubernetes"
tf = "terraform"# Clone repository
git clone https://github.com/urwithajit9/evnx.git
cd evnx
# Build (core features only)
cargo build
# Build with all features
cargo build --all-features
# Run tests
cargo test
# Run with features
cargo run --features migrate -- migrate --help
cargo run --features backup -- backup --help
cargo run --all-features -- --help
# Lint and format
cargo clippy --all-features -- -D warnings
cargo fmt# Cargo.toml features
[features]
default = []
migrate = ["reqwest", "base64", "indicatif"]
backup = ["aes-gcm", "argon2", "rand"]
full = ["migrate", "backup"]Why feature flags?
- Smaller binary size for basic usage
- Optional dependencies (reqwest, crypto libraries)
- Faster compilation during development
Contributions welcome! See CONTRIBUTING.md.
Areas where help is appreciated:
- Additional format converters
- Secret pattern improvements
- Windows support enhancements
- Extended
.envformat support (arrays, multiline values) - Documentation improvements
- Integration examples
- Translation (i18n)
MIT License - see LICENSE
Built by Ajit Kumar after learning the hard way about secrets management.
Inspired by:
- Countless developers who've accidentally committed secrets
- The pain of production incidents caused by misconfiguration
- The desire for better developer tooling
Related Projects:
- python-dotenv - Python implementation
- dotenvy - Rust dotenv parser
- direnv - Environment switcher
- git-secrets - AWS secret scanning
If this tool saved you from a secrets incident or made your life easier, please:
- ⭐ Star the repository
- 🐦 Tweet about it
- 📝 Write a blog post
- 💬 Tell your teammates
Your support helps improve the tool for everyone!
Made with 🦀 Rust and ❤️ by developers who've been there