Edit one key in an env file without disturbing anything else, and keep secrets out of your terminal while you do it.
$ envctl set .env DATABASE_URL 'postgres://localhost/app'
$ envctl get DATABASE_URL
postgres://localhost/app
$ envctl --redact list --values
DATABASE_URL=<redacted>
DEBUG=trueOrder, comments, and spacing survive every edit. Writes go through a temp file and a rename, so a crash never leaves half a file behind. A single C11 binary with no runtime dependencies.
curl -fsSL https://raw.githubusercontent.com/kjanat/envctl/master/install.sh | bashinstall.sh picks the release asset for your OS and architecture, checks it
against SHA256SUMS, and installs to ~/.local/bin. Override with
ENVCTL_INSTALL_DIR and ENVCTL_VERSION.
Prefer to pick the asset yourself? GitHub Releases carries linux-amd64,
linux-arm64, darwin-amd64, darwin-arm64, windows-amd64, and
windows-arm64, each with a build provenance attestation:
gh release download -R kjanat/envctl \
--pattern envctl-linux-amd64 --output ~/.local/bin/envctl
chmod +x ~/.local/bin/envctl
gh attestation verify ~/.local/bin/envctl --repo kjanat/envctlFrom source, with any C11 compiler:
git clone https://github.com/kjanat/envctl.git && cd envctl && make && make installmake install places the binary, the man page, and the bash, zsh, and fish
completion scripts under PREFIX (~/.local by default). make test runs the
suite.
envctl set [file] <KEY> [VALUE] create or replace a key
envctl get [file] <KEY> print the active value
envctl disable [file] <KEY> comment a key out, keep its value
envctl enable [file] <KEY> uncomment a key
envctl delete [file] <KEY> remove a key entirely
envctl list [file] print active keys
envctl redact [file] filter stdin to stdout, masking secrets
envctl env print the environment, always redacted
envctl completions <shell> completion script for bash/zsh/fish/pwsh
envctl module pwsh PowerShell cmdlets wrapping the binary
./.env is assumed when you leave the file out. Without a command word,
envctl <KEY> is a get and envctl <KEY> <VALUE> is a set. ls and rm are
aliases for list and delete.
Values that look like secrets are masked when a coding agent is driving your
terminal, and whenever you ask with --redact:
$ envctl env
# envctl v0.5.0 (redacted)
PATH=/usr/bin:/bin
API_TOKEN=<redacted>Pipes stay raw, so TOKEN=$(envctl get TOKEN) keeps working. envctl redact
runs the same masking over any text you pipe through it, which is how build logs
and agent output stop leaking:
npm run build 2>&1 | envctl redactThis is presentation hygiene, not a security boundary. See SECURITY.md.
| Page | Contents |
|---|---|
| Commands | Every command and flag, file format, exit codes, guarantees |
| Redaction | When masking is on, what counts as a secret, filter mode |
| Shell integration | Completions for four shells, PowerShell cmdlets |
| Recipes | Rotate a secret, mask CI output, compare two env files, and friends |
man envctl carries the full reference offline, and envctl <cmd> --help
prints one command with only the flags it accepts.
src/cli.c holds one table of commands and flags. Parser validation, --help,
the man page, the completion scripts, and the PowerShell module all come out of
it, so a flag cannot exist without the documentation knowing about it. The man
page is regenerated by make man, and make test fails when the checked-in
copy no longer matches.
Development and release workflow: CONTRIBUTING.md.
Threat model and vulnerability reporting: SECURITY.md.