Your .env.local works... until it doesn't.
Different machines behave differently.
Onboarding breaks.
CI fails in ways you can't reproduce.
envctl stops that drift.
envctl keeps environments consistent.
What envctl does
envctl keeps environments consistent.
It gives you:
- shared requirements in the repo
- local values outside Git
- explicit runtime behavior instead of guesswork
It is not a secret manager. It is not a dotenv loader. It is not a shell trick.
It is a local-first way to stop .env drift across development, teammates, and CI.
The problem
You have probably seen some version of this already:
- it works on your machine, but not on your teammate's
- CI fails because a variable is missing or shaped differently
- onboarding depends on tribal knowledge
.env.localgets copied around until nobody trusts it- nobody can clearly say which variables are actually required
That is not just a secret-storage problem.
It is an environment-consistency problem.
How envctl fixes it
envctl makes environments explicit instead of implicit.
It separates the environment into clear responsibilities:
- contract: what the project requires
- vault: what each machine stores locally
- profiles: which local value set is active
- resolution: what is actually true at runtime
- projection: how that resolved environment is handed to tools
That means:
- the repo defines shared requirements
- each machine keeps real values local
- the runtime environment is explicit and checkable
No hidden source of truth.
No guessing which value won.
Quickstart
Install the CLI:
python3 -m pip install envctl
Then the shortest useful flow is:
envctl config init
envctl init
envctl fill
envctl check
envctl run -- python app.py
What happens:
config initcreates your user-levelenvctlconfiginitprepares the repository forenvctland attempts to install managed Git hooksfillasks only for missing required valuescheckvalidates the resolved environmentrunexecutes with the resolved environment injected directly
If another tool really needs a file on disk, use sync.
Otherwise, run is usually the cleanest path.
Local Git protection
envctl can keep its own secret guard wired into Git without becoming a generic hooks manager.
The managed workflow is:
envctl hooks status
envctl hooks install
envctl hooks repair
envctl hooks remove
Those commands manage only envctl's own pre-commit and pre-push wrappers, both of which run envctl guard secrets.
Why it is different
envctl is not mainly competing with cloud secret tools or dotenv loaders.
Its primary job is different:
- cloud secret tools focus on secret distribution
- dotenv loaders and shell tooling focus on injection
envctlfocuses on environment coherence
That is why the core value is not “where do secrets live?”.
The core value is:
- what does this project require?
- what does this machine actually have?
- what environment will the app really receive?
A typical workflow
# add a new shared requirement
envctl add API_KEY sk-example
git add .envctl.yaml
git commit -m "require API_KEY"
# another developer pulls
envctl check
envctl fill
envctl run -- python app.py
The contract changes in Git. Real values stay local. The runtime environment stays explicit.
When envctl is a good fit
- your
.env.localkeeps drifting - onboarding is fragile
- local and CI behavior diverge too easily
- one machine needs multiple local contexts
- you want a local-first workflow without turning generated files into the source of truth
When it is probably overkill
- you have one tiny project with a static env file
- onboarding is trivial and unlikely to change
- the team already solves environment consistency elsewhere and does not need another layer
Security
- no secrets in the contract
- local values stay on the machine
- sensitive output is masked
- encryption at rest is optional
- managed Git hooks can run
guard secretsautomatically before commit and push
envctl assumes the local machine is trusted. It is designed to keep environment handling explicit and safer, not to replace a full remote secrets platform.
Documentation
- Docs home
- Getting started
- Quickstart
- Concepts
- Commands reference
- Configuration reference
- Observability reference
- Distribution reference
- Troubleshooting
- Compatibility
Development
The repository uses uv for dependency management and reproducible environments.
uv sync --dev
make validate
This ensures that local development and CI use the same locked dependency graph defined in uv.lock.
If you are editing documentation locally:
uv sync --extra docs
make docs-check
The validation flow includes linting, formatting, type checking, security checks, tests with coverage, and architectural constraints.
See CONTRIBUTING.md for details.
If you have ever said:
"it works on my machine"
then envctl is probably solving a problem you already have.
Metadata
Release files for envctl 2.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| envctl-2.5.1.tar.gz | 4.2 MB | With provenance | Details for envctl-2.5.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| envctl-2.5.1-py3-none-any.whl | Python 3 | none | any | Details for envctl-2.5.1-py3-none-any.whl |
Total release size: 4.4 MB
Release files / envctl-2.5.1.tar.gz
| Download URL | envctl-2.5.1.tar.gz |
|---|---|
| Size | 4.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
377cd5d796f93ed9259efa848e570844fee06b34d5ca5c87986936ea668fbc8e
|
|
BLAKE2b-256 checksum How to use checksums |
6b7e3859a1b67db2b499e911de5b16279baef0ac4bcd46d67a64f0a797835e8f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 14, 2026.
Transparency logRelease files / envctl-2.5.1-py3-none-any.whl
| Download URL | envctl-2.5.1-py3-none-any.whl |
|---|---|
| Size | 195.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
12ad96ba37408174fa6b93423f5730fb3bc9acd398ae558eaa8a48f3279b7dc7
|
|
BLAKE2b-256 checksum How to use checksums |
3652bc048820ef083c5453ce96bc38f3b9146d8cde073c4c1dd939085da70028
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 14, 2026.
Transparency logSecurity for envctl 2.5.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for envctl-2.5.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 14, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.