README
ΒΆ
envforge
π¨ Stop shipping broken environment variables
Envforge is a CLI that:
- Fails your app if env vars are missing
- Finds env vars your code actually uses
- Keeps
.envand.env.examplein sync
Example
envforge audit . --env-file .env.example
USED but NOT DECLARED:
DATABASE_URL
STRIPE_KEY
A smart environment variable manager for developers. Envforge helps you compare, sync, audit, encrypt, and watch your .env files with zero configuration.
The Problem
Managing environment variables across projects is chaotic:
.envfiles drift from.env.example- Hard to know which env vars are actually used in code
- Sharing secrets with teammates is risky
- No way to validate env vars before running your app
Envforge solves all of this with a single CLI.
Installation
Option 1 β Go Install (all platforms)
If you have Go installed:
go install github.com/Santiago1809/envforge/cmd/envforge@latest
The binary will be placed in $GOPATH/bin (usually ~/go/bin).
Make sure that directory is in your PATH.
Note:
go installwill show version "dev" β this is expected because the version is injected at build time by GoReleaser. To get the correct version string, download the binary from GitHub Releases. Also note that theenvforge updatecommand only works correctly when installed via the GitHub Releases binary, not viago install.
Option 2 β Download Binary (Recommended)
Download the latest release for your platform from GitHub Releases:
| Platform | File |
|---|---|
| Windows (64-bit) | envforge_windows_amd64.zip |
| macOS (Apple Silicon) | envforge_darwin_arm64.tar.gz |
| macOS (Intel) | envforge_darwin_amd64.tar.gz |
| Linux (64-bit) | envforge_linux_amd64.tar.gz |
| Linux (ARM) | envforge_linux_arm64.tar.gz |
Platform-Specific Setup
Windows:
- Download and extract
envforge_windows_amd64.zip - Move
envforge.exeto a permanent folder, e.g.C:\Users\YOUR_USER\tools\envforge\ - Add that folder to your PATH:
- Press
Win + Sand search for "environment variables" - Click "Edit the system environment variables"
- Click "Environment Variables..."
- Under "User variables", select Path and click Edit
- Click New and add:
C:\Users\YOUR_USER\tools\envforge - Click OK on all dialogs
- Press
- Open a new terminal and verify:
envforge version
macOS:
- Download and extract the
.tar.gzfor your architecture:
# Apple Silicon (M1/M2/M3)
tar -xzf envforge_darwin_arm64.tar.gz
# Intel
tar -xzf envforge_darwin_amd64.tar.gz
- Move the binary to
/usr/local/bin:
mv envforge /usr/local/bin/envforge
chmod +x /usr/local/bin/envforge
- On first run, macOS may block the binary (Gatekeeper).
If you see a security warning:
- Open System Settings β Privacy & Security
- Scroll down and click "Allow Anyway" next to envforge
- Run
envforge versionagain and click Open on the dialog
- Verify:
envforge version
Linux:
tar -xzf envforge_linux_amd64.tar.gz
sudo mv envforge /usr/local/bin/envforge
chmod +x /usr/local/bin/envforge
envforge version
Quick Start
# Check your env vars are set before running
$ envforge check --from .env.example
All required environment variables are set
# See what's different between .env and .env.example
$ envforge diff
MISSING in .env (1):
+ API_KEY
EXTRA in .env (1):
- MY_LOCAL_VAR
# Audit your code to find used but undeclared vars
$ envforge audit ./src --env-file .env.example
USED but NOT DECLARED (2):
+ DATABASE_URL (src/db.go:15)
+ JWT_SECRET (src/auth.go:8)
DECLARED but NOT USED (1):
- DEBUG_MODE
Output Format
All commands support a global --format flag:
--format text(default): Human-readable colored output.--format json: Machine-readable JSON output for automation.
# Text output
envforge audit . --env-file .env.example
# JSON output
envforge audit . --env-file .env.example --format json
Errors (e.g., file not found) are printed to stderr as plain text regardless of format.
Commands
diff
Compare two .env files. By default compares .env vs .env.example.
# Compare .env and .env.example
envforge diff
# Compare specific files
envforge diff .env.staging .env.production
# Show values (use with caution - may expose secrets)
envforge diff --show-values
# Table output (default)
envforge diff --diff-format table
# GitHub Actions format
envforge diff --diff-format github
# JSON output (structured JSON via global flag)
envforge diff --format json
Flags:
--diff-format: Diff output style:table(default),json,github(only used when global--format=text)--show-values: Show values in diff output--verbose, -v: Show matching keys as well--format: Global output format:text(default) orjson
sync
Sync keys from a source .env file to a target .env.example file, stripping values.
Basic usage:
# Default: sync .env β .env.example
envforge sync
# Non-interactive (auto-confirm)
envforge sync --yes
Multi-stage environments:
# Sync a specific stage
envforge sync --stage development # β .env β .env.development
envforge sync --stage staging # β .env β .env.staging
envforge sync --stage production # β .env β .env.production
# If .env doesn't exist, it's created automatically
envforge sync --stage production # Creates .env if missing
# Explicit source and destination
envforge sync --from .env --to .env.production
# Combine stage with explicit paths (stage is ignored if --from/--to are set)
envforge sync --stage production --from .env --to .env.dev
Typical workflow with stages:
# Before deploying to staging
envforge sync --stage staging
envforge check --from .env.staging
# Before deploying to production
envforge sync --stage production
envforge check --from .env.production
# Local development
envforge sync --stage development
envforge check --from .env.development
Flags:
--stage, -s: Environment stage (development,staging,production). Automatically resolves source/target files.--from, -f: Source.envfile (default:.envor stage-based)--to, -t: Target env file (default: derived from source - for stages uses stage name, otherwise.examplesuffix)--yes, -y: Skip confirmation prompt
Examples:
# Using stages (recommended for multi-env projects)
envforge sync --stage production
# Syncing .env β .env.production
# + NEW_KEY (added)
# Successfully synced 1 new key to .env.production
# If .env doesn't exist, it's created automatically
envforge sync --stage production
# Source file .env does not exist. Creating empty file...
# Successfully synced 0 new key(s) to .env.production
# Explicit files
envforge sync --from .env --to .env.staging --yes
# Legacy mode (no stage)
envforge sync # .env β .env.example
envforge sync --from .env.dev --to .env.dev.example
audit
Scan source code for environment variable usage.
# Audit current directory using .env.example (text output)
envforge audit . --env-file .env.example
# Audit with JSON output
envforge audit . --env-file .env.example --format json
# Audit specific directory
envforge audit ./src --env-file .env.example
# Show all variables (including declared and used)
envforge audit . --env-file .env.example --verbose
# Scan specific languages
envforge audit . --env-file .env.example --lang go,js,py
# Exclude additional directories
envforge audit . --env-file .env.example --exclude coverage,build
Flags:
--env-file, -e: Path to.env.examplefile (default:.env.example)--lang, -l: Languages to scan:go,js,ts,py,sh,java,php,ruby,cs(comma-separated)--exclude, -x: Additional directories to exclude (appends to defaults:testdata, vendor, node_modules, .git, dist, build, bin, .agents, .claude, .skills, skills)--verbose, -v: Show declared and used variables--format: Global flag:text(default) orjson
Supported languages & frameworks:
| Language | Extensions | Frameworks Detected | Usage Patterns |
|---|---|---|---|
| Go | .go |
Gin, Fiber, GORM | os.Getenv, viper.Get |
| JavaScript | .js, .jsx |
Next.js, Vue, Angular, React, Node, Bun | process.env, import.meta.env, Bun.env |
| TypeScript | .ts, .tsx |
Next.js, Vue, Angular, React, Bun | process.env, import.meta.env, Bun.env |
| Python | .py |
FastAPI, Django | os.environ, os.getenv |
| Shell | .sh, .bash |
Bash/Zsh scripts | $VAR, ${VAR} |
| Java | .java |
Spring Boot | System.getenv, @Value, ${} |
| PHP | .php |
Laravel | $_ENV, env(), $_SERVER |
| Ruby | .rb |
Rails | ENV.fetch, ENV[], Rails.credentials |
| C# | .cs |
.NET (ASP.NET Core, MVC) | Environment.GetEnvironmentVariable, IConfiguration |
Envforge uses semantic detection to identify environment variable usage and automatically detects the framework you're using based on config files (e.g., package.json, go.mod, pom.xml, Gemfile).
check
Validate required environment variables are set.
# Check against .env.example (text output)
envforge check --from .env.example
# Check with JSON output
envforge check --from .env.example --format json
# Check specific required keys
envforge check --required DATABASE_URL,API_KEY,JWT_SECRET
# Check with prefix filter
envforge check --from .env.example --prefix AWS_
# Allow empty values
envforge check --from .env.example --allow-empty
Flags:
--required: Comma-separated list of required keys--from, -f: Use keys from.env.examplefile--allow-empty: Allow empty values--prefix: Filter by key prefix (e.g.AWS_)--schema: Path to.env.schemafile (optional)--format: Global flag:text(default) orjson
Schema Validation
Envforge supports type validation for environment variables using an optional .env.schema file.
Schema File Format
Create a .env.schema file next to your .env:
# .env.schema
PORT=int
DATABASE_URL=url
DEBUG=bool
RATE=float
EMAIL=email
APP_ENV=enum:development,staging,production
PATTERN=regex:^[a-z]+$
NAME=string
Supported types:
| Type | Description | Example Value |
|---|---|---|
string |
Text value (default) | hello |
int |
Integer | 8080 |
float |
Decimal number | 3.14 |
bool |
Boolean (true, false, 1, 0, yes, no) |
true |
url |
Valid URL with scheme and host | https://localhost:5432 |
email |
Valid email address | user@example.com |
enum |
One of predefined values | development |
regex |
Must match a regex pattern | ^[a-z]+$ |
Automatic Schema Inference
When running envforge check without a schema file:
- If no
.env.schemaexists in the same directory as your.env, envforge will automatically infer types from your current values - An interactive TUI appears where you can review and adjust the inferred types
- Press
sto save the schema to.env.schema - Press
qto cancel without saving
# First run - no schema exists yet
$ envforge check --from .env.example
The TUI shows:
- VARIABLE: The env var name
- INFERRED: Auto-detected type (gray, hint)
- TYPE: Editable type (green if modified)
- SAMPLE VALUE: Current value from your .env
TUI Controls:
j/korβ/β: Navigateβ/β: Change type (cycles: string β int β float β bool β url β email β string)s: Save schema and continueq/Esc: Cancel (skip validation)
Using Schema Explicitly
# Specify schema path explicitly
envforge check --schema ./custom.schema
# Use with JSON output (TUI is skipped in JSON mode)
envforge check --format json --schema .env.schema
CI/CD with Schema
# GitHub Actions
- name: Check environment variables with schema
run: envforge check --from .env.example --format json
env:
# Schema validation happens automatically if .env.schema exists
encrypt
Encrypt a .env file for safe sharing.
# Encrypt with passphrase
envforge encrypt .env --key "your-secure-passphrase"
# Encrypt using a key file
envforge encrypt .env --key ~/.ssh/id_rsa
# Encrypt and specify output
envforge encrypt .env --key "pass" --out .env.enc
Flags:
--key, -k: Encryption passphrase or key file (required)
decrypt
Decrypt an encrypted .env file.
# Decrypt to stdout
envforge decrypt .env.enc.b64 --key "your-secure-passphrase"
# Decrypt to file
envforge decrypt .env.enc.b64 --key "your-secure-passphrase" --out .env.decrypted
# Decrypt using key file
envforge decrypt .env.enc.b64 --key ~/.ssh/id_rsa --out .env
Flags:
--key, -k: Decryption passphrase or key file (required)--out, -o: Output file (default: stdout)
verify
Verify integrity of an encrypted file without decrypting.
envforge verify .env.enc.b64 --key "your-secure-passphrase"
Flags:
--key, -k: Decryption passphrase or key file (required)
watch
Watch a .env file for changes and optionally execute a command.
# Watch .env file
envforge watch .env
# Watch and execute command on change
envforge watch .env --exec "make restart"
# Custom debounce time (ms)
envforge watch .env --exec "systemctl reload app" --debounce 500
Flags:
--exec: Command to execute on change--debounce: Debounce time in milliseconds (default: 50)
info
Print information about a .env file.
# Text output
envforge info .env
envforge info .env.example
# JSON output
envforge info .env --format json
keygen
Generate a random 32-byte encryption key.
envforge keygen
# Output: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
# Store this key in a password manager!
version
Print version information.
envforge version
# Output:
# envforge version v1.0.2
# commit: abc1234
# date: 2026-03-29T12:00:00Z
update
Update envforge to the latest release.
# Interactive update (asks for confirmation)
envforge update
# Skip confirmation
envforge update --yes
Note: This command only works when envforge is installed from GitHub Releases (i.e., placed in
%LOCALAPPDATA%\envforge\on Windows or/usr/local/bin/envforgeon Unix). It does not work withgo installbecause the version cannot be determined.
Flags:
--yes, -y: Skip confirmation prompt
Windows-specific: The binary is installed to %LOCALAPPDATA%\envforge\. This directory
is always writable without admin privileges.
tui
Open interactive Terminal User Interface.
envforge tui
Features:
-
Overview Tab: Shows all keys from
.envwith masked values- Red: Missing from
.env.example - Yellow: Extra keys not in
.env.example - Green: Present in both
- Red: Missing from
-
Audit Tab: Shows code audit results
- Red: Used but not declared
- Yellow: Declared but not used
-
Health Tab: Health check of required variables
- Green checkmark if set, red X if missing
- File sizes, key counts, last modified dates
Navigation:
Tab/Shift+Tab: Switch tabsβ/βorj/k: Scroll within tabqorCtrl+C: Quit
completion
Generate shell completion scripts.
# Bash
envforge completion bash > /etc/bash_completion.d/envforge
# Zsh
envforge completion zsh > "${fpath[1]}/_envforge"
# Fish
envforge completion fish > ~/.config/fish/completions/envforge.fish
# PowerShell
envforge completion powershell | Out-File -Encoding utf8 $env:TEMP\envforge_completion.ps1
. $env:TEMP\envforge_completion.ps1
Configuration
Envforge can be configured via a config file or environment variables.
Config file location: ~/.config/envforge/config.yaml (or ~/.config/envoy/config.yaml for legacy)
Global flags:
--config, -c: Config file path--no-color: Disable colored output
Example config:
# ~/.config/envforge/config.yaml
audit:
languages: [go, js, py, sh]
exclude:
- testdata
- vendor
- node_modules
- .git
- dist
- build
- bin
- .agents
- .claude
- .skills
- skills
Multi-Stage Environments
Envforge makes it easy to manage multiple environment stages (development, staging, production) using the --stage flag.
File naming convention:
| Stage | Source File | Target File |
|---|---|---|
| development | .env |
.env.development |
| staging | .env |
.env.staging |
| production | .env |
.env.production |
Note: All stages use
.envas the source. If.envdoesn't exist, it will be created automatically.
Usage:
# Sync production environment
envforge sync --stage production
# Sync staging environment
envforge sync --stage staging --yes
# Sync development (default, same as plain `envforge sync`)
envforge sync --stage development
You can also explicitly specify files with --from and --to:
envforge sync --from .env.production --to .env.production.example
envforge sync --from .env.staging --to .env.staging.example --yes
This is useful if you use custom file naming conventions.
Typical workflow with stages:
# Before deploying to staging
envforge sync --stage staging
envforge check --from .env.staging
# Before deploying to production
envforge sync --stage production
envforge check --from .env.production
# Local development
envforge sync --stage development
envforge check --from .env.development
Real-World Workflow
Morning: Start Your Project
# Check all env vars are configured
$ envforge check --from .env.example
All required environment variables are set
# Start your app
$ make dev
During Development: Add a New Env Var
# Add the var to your .env
echo "NEW_FEATURE_FLAG=true" >> .env
# Run audit to see if it's used anywhere
$ envforge audit ./src --env-file .env.example
USED but NOT DECLARED (1):
+ NEW_FEATURE_FLAG (src/features/flags.go:5)
Before Committing: Sync .env.example
# Sync new vars to .env.example (or use --stage for multi-env)
$ envforge sync
# or: envforge sync --stage staging
# or: envforge sync --from .env --to .env.example
Continue? [y/N]: y
Successfully synced to .env.example
Code Review: Run Full Audit
$ envforge audit ./src --env-file .env.example -v
USED but NOT DECLARED (5):
+ DATABASE_URL (src/db.go:15)
+ API_KEY (src/client.go:10)
+ JWT_SECRET (src/auth.go:8)
DECLARED but NOT USED (2):
- DEBUG_MODE
- OLD_FEATURE
DECLARED and USED (8):
= DB_HOST
= DB_PORT
= APP_PORT
CI/CD Integration
GitHub Actions
name: Check Environment
on: [push, pull_request]
jobs:
check-env:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.26'
- name: Install envforge
run: go install github.com/Santiago1809/envforge/cmd/envforge@latest
- name: Check environment variables
run: envforge check --from .env.example
Docker Entrypoint
FROM golang:1.26-alpine AS builder
RUN go install github.com/Santiago1809/envforge/cmd/envforge@latest
FROM alpine:latest
COPY --from=builder /go/bin/envforge /usr/local/bin/envforge
COPY .env.example /app/.env.example
# Validate env vars before starting app
ENTRYPOINT ["envforge", "check", "--from", ".env.example", "&&", "myapp"]
Troubleshooting
"dev" version shown when using go install
This is expected. The version is injected at build time by GoReleaser via ldflags.
go install does not pass these flags. To see the correct version, download from
GitHub Releases.
envforge update doesn't work with go install
The update command relies on the binary being in a known location (%LOCALAPPDATA%\envforge\ on Windows or /usr/local/bin/envforge on Unix) that can be overwritten. go install places the binary in $GOPATH/bin which may not be writable or the update mechanism cannot locate it correctly. Install from GitHub Releases for a proper update experience.
Windows: "Access is denied" during update
On Windows, running processes cannot be overwritten. The update process:
- Downloads the new binary to a temporary location
- Creates a batch script that waits for the current process to exit
- The batch script runs after envforge exits and replaces the binary
Ensure you're using the Windows release binary (not go install) for best compatibility.
Parser errors with .env files
The .env file must follow the format KEY=value on each line.
Lines without = will cause parse errors. Comments should start with #.
Values can contain : without issues (e.g., URLs).
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
go test ./... - Submit a pull request
License
MIT License - see LICENSE for details.