Local environment control plane for contract-driven development workflows
Project description
envctl
Your .env.local files drift between machines, hide missing variables, and break when you least expect it. envctl fixes that.
Why this exists
Most projects handle environment variables in a messy way:
.env.localfiles are undocumented- values get copied between machines
- something works locally until it suddenly does not
- CI and local setups behave differently
- nobody is fully sure which variables are required
It works — until it breaks.
envctl brings structure to this without turning environment setup into a second project.
What is envctl?
envctl is a local-first environment control plane built around a contract-first model.
It separates three things that usually get mixed together:
- what the project needs → defined in
.envctl.schema.yamland committed to the repository - what you have locally → stored in a private local vault, outside git
- what actually runs → a validated environment resolved when needed
That gives you:
- clear, documented variables
- no secrets in git
- fewer setup mistakes
- more predictable local and team workflows
- explicit validation before execution
Install
pip install envctl
Or from source:
git clone https://github.com/labrynx/envctl
cd envctl
pip install -e .
Quickstart
envctl config init
envctl init
envctl fill
envctl check
envctl run -- python app.py
What this does:
config initcreates your local envctl configinitconnects the current repository to envctlfillasks only for missing valuescheckvalidates the environment before you run anythingruninjects a clean resolved environment into the child process
Why not just .env.local?
Because it does not scale cleanly.
.env.local |
direnv | Doppler / Infisical | envctl | |
|---|---|---|---|---|
| Documents variables | ❌ | ❌ | Partial | ✅ |
| Validates values | ❌ | ❌ | ❌ | ✅ |
| Keeps secrets out of git | ⚠️ | ✅ | ✅ cloud | ✅ local |
| Supports multiple environments | manual files | manual files | ✅ | ✅ profiles |
| Works without cloud | ✅ | ✅ | ❌ | ✅ |
envctl is not a cloud secrets manager.
It is a way to make environment handling explicit, predictable, and local-first.
your repository defines what is needed, your machine provides the values, and envctl resolves the final environment.
A typical workflow
# one developer adds a new requirement
envctl add API_KEY sk-example
git add .envctl.schema.yaml
git commit -m "require API_KEY"
# another developer pulls the change
envctl check
envctl fill
envctl run -- python app.py
The contract is shared in git. The values stay local. The runtime environment is rebuilt consistently when needed.
How it works
At a high level:
- contract → defines which variables exist and how they should look
- vault → stores the real local values
- profile → selects which local value set to use (
local,dev,staging, ...) - resolution → builds the final validated environment
- projection → applies it through
run,sync, orexport
Think of it like this:
the repository defines the rules, your machine provides the values, and envctl builds the environment you actually run.
Profiles
Instead of juggling multiple .env files:
envctl --profile dev fill
envctl --profile staging check
envctl --profile staging run -- python app.py
Each profile is explicit and independent. No hidden inheritance, no magic fallback between profiles.
Docker note
envctl run -- docker run ...
envctl injects variables into the Docker client process.
To pass them into the container, you still need one of these:
-e--env--env-file
A common pattern is:
docker run --env-file <(envctl export --format dotenv) ...
CI mode
ENVCTL_RUNTIME_MODE=ci envctl check
In CI:
- validation still works
- mutating commands are blocked
That keeps automation predictable and avoids accidental local-style writes in CI environments.
Common commands
envctl check
envctl inspect
envctl explain DATABASE_URL
envctl status
envctl doctor
envctl add DATABASE_URL <value>
envctl set PORT 4000
envctl unset PORT
envctl run -- <command>
envctl sync
envctl export
envctl profile list
envctl profile create staging
envctl vault check
envctl vault show
envctl vault encrypt
envctl vault decrypt
When envctl is a good fit
envctl is a strong fit if:
.env.localfiles drift between machines- onboarding is fragile
- CI and local environments do not behave the same way
- you work with multiple environments
- you want a local-first workflow without depending on a hosted service
When envctl is not the right tool
envctl may be unnecessary if:
- you only have one static
.envfile - the project is very small and has no real setup complexity
- you already rely fully on a centralized secrets platform and do not want local-first handling
Security model
- the contract contains no secrets
- secrets stay on your machine
- sensitive values are masked in normal output
- vault files use restrictive permissions
- optional encryption at rest is available for vault files
Vault encryption at rest
If you enable encryption, envctl stores vault files in an encrypted, self-identifying format instead of plaintext.
Enable it in your config:
{
"encryption": {
"enabled": true
}
}
Then migrate existing vault files once:
envctl vault encrypt
This creates a local key file at:
~/.envctl/vault/master.key
That key is stored with restrictive permissions.
After encryption is enabled:
vault editworks transparentlyvault checkreports whether the file is plaintext, encrypted, using the wrong key, or corrupted- decrypt failures are explicit instead of looking like generic parse errors
To migrate back to plaintext:
envctl vault decrypt
Then disable encryption in config.
Important limitation
Encryption at rest helps protect vault files on disk.
It does not protect against a fully compromised machine or a compromised user session.
envctl assumes a trusted machine. If your machine is compromised, your secrets are compromised too.
Back up your master.key carefully.
If you lose it, encrypted vault data cannot be recovered.
Documentation
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file envctl-2.4.0.tar.gz.
File metadata
- Download URL: envctl-2.4.0.tar.gz
- Upload date:
- Size: 82.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cd6c2f3b0bc092fbc2a678910bd9cea2bee12757ccb27c379e681481d5a8484
|
|
| MD5 |
b51bb96baa50338b52411403bca7ff9f
|
|
| BLAKE2b-256 |
80ca0fe45a5d1743717ee042c08de5c133b0ecaea7d5e0caa7971c19b5494d27
|
File details
Details for the file envctl-2.4.0-py3-none-any.whl.
File metadata
- Download URL: envctl-2.4.0-py3-none-any.whl
- Upload date:
- Size: 132.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5160c973edbe49219a16e2669475bd7ee9416a112f0e855b5ae360585c62398a
|
|
| MD5 |
9a3260a36a34138713bc7ee4c55c6622
|
|
| BLAKE2b-256 |
3bc61e2d292853d524ae280f6ab9b7624b20791ed34475fc3ada15f0191f3ecb
|