An open API service indexing awesome lists of open source software.

https://github.com/labrynx/envctl

Your .env files, local-first and under your control.
https://github.com/labrynx/envctl

cli config-management configuration developer-tools devops dotenv env environment environment-management local-first python symlink typer

Last synced: 6 days ago
JSON representation

Your .env files, local-first and under your control.

Awesome Lists containing this project

README

          

# envctl

**Your `.env.local` files drift between machines, hide missing variables, and break when you least expect it. envctl fixes that.**

[![CI](https://github.com/labrynx/envctl/actions/workflows/ci.yml/badge.svg)](https://github.com/labrynx/envctl/actions/workflows/ci.yml)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/labrynx/envctl/blob/main/LICENSE)

---

## Why this exists

Most projects handle environment variables in a messy way:

- `.env.local` files 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.yaml` and 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

```bash
pip install envctl
````

Or from source:

```bash
git clone https://github.com/labrynx/envctl
cd envctl
pip install -e .
```

---

## Quickstart

```bash
envctl config init
envctl init
envctl fill
envctl check
envctl run -- python app.py
```

What this does:

* `config init` creates your local envctl config
* `init` connects the current repository to envctl
* `fill` asks only for missing values
* `check` validates the environment before you run anything
* `run` injects 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

```bash
# 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`, or `export`

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:

```bash
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

```bash
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:

```bash
docker run --env-file <(envctl export --format dotenv) ...
```

---

## CI mode

```bash
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

```bash
envctl check
envctl inspect
envctl explain DATABASE_URL
envctl status
envctl doctor

envctl add DATABASE_URL
envctl set PORT 4000
envctl unset PORT

envctl run --
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.local` files 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 `.env` file
* 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:

```json
{
"encryption": {
"enabled": true
}
}
```

Then migrate existing vault files once:

```bash
envctl vault encrypt
```

This creates a local key file at:

```text
~/.envctl/vault/master.key
```

That key is stored with restrictive permissions.

After encryption is enabled:

* `vault edit` works transparently
* `vault check` reports 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:

```bash
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

* [Quickstart](https://github.com/labrynx/envctl/blob/main/docs/getting-started/quickstart.md)
* [Mental model](https://github.com/labrynx/envctl/blob/main/docs/getting-started/mental-model.md)
* [Commands reference](https://github.com/labrynx/envctl/blob/main/docs/reference/commands.md)
* [Profiles reference](https://github.com/labrynx/envctl/blob/main/docs/reference/profiles.md)
* [Vault reference](https://github.com/labrynx/envctl/blob/main/docs/reference/vault.md)
* [Encryption reference](https://github.com/labrynx/envctl/blob/main/docs/reference/encryption.md)
* [Config reference](https://github.com/labrynx/envctl/blob/main/docs/reference/config.md)
* [CI workflow](https://github.com/labrynx/envctl/blob/main/docs/workflows/ci.md)
* [Team workflow](https://github.com/labrynx/envctl/blob/main/docs/workflows/team.md)
* [Security](https://github.com/labrynx/envctl/blob/main/docs/reference/security.md)
* [Internal architecture](https://github.com/labrynx/envctl/blob/main/docs/internals/architecture.md)