Skip to main content

Encrypted environment variable management for teams and projects

Project description

๐Ÿ” envault

Encrypted environment variable management for teams and projects

GitHub Stars License Python Tests


Stop leaking secrets in .env files. Encrypt them at rest, share securely, and inject at runtime.

AES-256-GCM Encryption + Multi-Environment Support + Zero Infrastructure

Quick Start | Features | CLI Commands | Architecture


Why This Exists

Every team has the same problem: .env files with plaintext secrets committed to repos, shared over Slack, or lost when someone leaves. Cloud secrets managers like HashiCorp Vault or AWS Secrets Manager require infrastructure, setup, and ongoing maintenance. For most projects, that is overkill.

envault sits in the sweet spot -- AES-256-GCM encryption with scrypt key derivation, multi-environment support (dev/staging/prod in one file), and zero infrastructure. It is just a JSON file you can commit to your repo. The secrets inside are encrypted. Share the password or key out of band, and your whole team has access.

  • Encrypted at rest -- AES-256-GCM with scrypt key derivation means secrets are safe even if the vault file is committed
  • Multi-environment -- manage dev, staging, and prod secrets in a single file with environment inheritance
  • Export anywhere -- dotenv, shell, Docker, JSON, YAML, Kubernetes Secrets with one command
  • Zero infrastructure -- no server, no cloud service, no accounts, just a JSON file and a password

Stop sharing plaintext .env files. Start encrypting them.


Features

Category Feature Description
Encryption AES-256-GCM Military-grade authenticated encryption
Encryption scrypt Key Derivation Password-based key derivation with configurable parameters
Encryption Key-based Auth Base64-encoded 256-bit keys for team sharing
Encryption Password Rotation Re-encrypt all secrets with a new password
Encryption Key Rotation Re-encrypt all secrets with a new key
Environments Multi-Environment dev, staging, prod in one vault file
Environments Environment Inheritance staging inherits from default, prod overrides
Environments Environment Diffing Compare variables across environments
Environments Environment Copying Clone entire environments
Variables Set/Get/Delete Full CRUD operations with metadata
Variables Bulk Set Set multiple variables at once
Variables Rename Rename variables without re-encryption
Variables Search Case-insensitive name search across environments
Variables Tags Categorize variables with multiple tags
Variables Groups Organize by prefix (DB_, API_, AWS_)
Variables Interpolation ${DB_USER}:${DB_PASS} template syntax with defaults
Variables Validation Enforce URL, email, port, pattern, length constraints
Variables Expiry/TTL Time-based variable expiration with warnings
Variables History Full change audit trail (set, delete, rename, rotate)
Export dotenv Standard .env file format
Export Shell export VAR=value statements
Export Docker Docker --env-file compatible
Export JSON Structured JSON output
Export YAML YAML-formatted output
Export Kubernetes Secret K8s Secret manifest with base64 encoding
Import .env Files Parse and import standard dotenv files
Import JSON Import from JSON files
Runtime Process Injection Run commands with injected environment variables
Security Access Control Role-based (admin/editor/viewer) with environment/variable restrictions
Security Secret Detection Detect weak secrets and default values
Security gitignore Generation Auto-generate gitignore entries for vault files
Security Secure Display Multiple masking styles for terminal output
Reliability Atomic Saves Temp file + rename to prevent corruption
Reliability File Locking Prevent concurrent modifications with stale lock detection
Reliability Backup/Restore Timestamped backups with verification
Reliability Snapshots Point-in-time state comparison
Reliability Migration Vault file version upgrade support
Configuration .envaultrc Project-level configuration file
Configuration pyproject.toml [tool.envault] section support
Configuration .env.example Smart placeholder generation (no secrets exposed)

๐Ÿš€ Quick Start

# 1. Install envault
pip install -e .

# 2. Initialize a vault with password
envault init

# 3. Set your first secret
envault set DATABASE_URL "postgres://localhost/mydb" -e dev

Your secrets are now encrypted at rest in .envault.json.


๐Ÿ“‹ CLI Commands

envault init

Initialize a new encrypted vault.

# Password-based (interactive prompt)
envault init

# Key-based (prints key to save securely)
envault init --key

# Custom path
envault init --path secrets/vault.json

envault set

Set a variable in the vault.

# Basic set
envault set API_KEY "sk-secret-key"

# With environment, description, and tags
envault set DATABASE_URL "postgres://prod-host/db" \
  -e prod \
  -d "Production database" \
  -t database -t critical

envault get

Get a decrypted variable value.

envault get API_KEY
envault get DATABASE_URL -e prod

envault list

List all variables (without values).

# List all environments
envault list

# List specific environment
envault list -e prod

envault delete

Delete a variable.

envault delete OLD_API_KEY -e dev

envault export

Export variables in various formats.

# Export as .env file
envault export -e dev -f dotenv -o .env

# Export as shell statements
eval "$(envault export -e dev -f shell)"

# Export as Kubernetes Secret
envault export -e prod -f k8s-secret -o k8s-secret.yaml

# Export as Docker env-file
envault export -e dev -f docker -o docker.env

# Export as JSON
envault export -e dev -f json

# Export as YAML
envault export -e dev -f yaml

envault import

Import variables from a file.

# Import from .env file
envault import .env -e dev

# Import into specific environment
envault import legacy-config.env -e staging

envault diff

Compare two environments.

# Compare environments
envault diff dev prod

# Show masked values
envault diff dev prod --values

envault run

Run a command with injected environment variables.

# Run application with dev secrets
envault run -e dev -- python app.py

# Run with prod secrets
envault run -e prod -- node server.js

envault keygen

Generate a new encryption key.

envault keygen
# Output: base64-encoded 256-bit key

๐Ÿ—๏ธ Architecture

envault/
โ”œโ”€โ”€ cli.py           # Click-based CLI with Rich output
โ”œโ”€โ”€ vault.py         # Core vault engine (Vault, VaultEntry, Environment)
โ”œโ”€โ”€ crypto.py        # AES-256-GCM encryption with scrypt key derivation
โ”œโ”€โ”€ access.py        # Role-based access control (admin/editor/viewer)
โ”œโ”€โ”€ history.py       # Variable change history tracking
โ”œโ”€โ”€ inheritance.py   # Environment inheritance with layered resolution
โ”œโ”€โ”€ template.py      # Variable interpolation (${VAR:-default} syntax)
โ”œโ”€โ”€ validator.py     # Variable validation rules (URL, email, port, etc.)
โ”œโ”€โ”€ expiry.py        # TTL-based variable expiration
โ”œโ”€โ”€ groups.py        # Variable groups by prefix
โ”œโ”€โ”€ diff.py          # Environment comparison
โ”œโ”€โ”€ display.py       # Secure terminal display with masking
โ”œโ”€โ”€ exporter.py      # Multi-format export (dotenv/shell/docker/json/yaml/k8s)
โ”œโ”€โ”€ importer.py      # Import from .env, JSON, shell
โ”œโ”€โ”€ backup.py        # Timestamped backup and restore
โ”œโ”€โ”€ snapshot.py      # Point-in-time vault state snapshots
โ”œโ”€โ”€ lock.py          # File-based vault locking
โ”œโ”€โ”€ migration.py     # Vault file version migration
โ”œโ”€โ”€ security.py      # Secret strength auditing and gitignore generation
โ”œโ”€โ”€ example.py       # .env.example generation with smart placeholders
โ””โ”€โ”€ config.py        # Project config (.envaultrc, pyproject.toml)

Data Flow

                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚  CLI (Click) โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ–ผ            โ–ผ            โ–ผ
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚   set    โ”‚ โ”‚   get    โ”‚ โ”‚  export  โ”‚
        โ”‚  import  โ”‚ โ”‚   list   โ”‚ โ”‚   run    โ”‚
        โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
             โ”‚            โ”‚            โ”‚
             โ–ผ            โ–ผ            โ–ผ
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚          Vault Engine           โ”‚
        โ”‚   (encrypt/decrypt/store)       โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ–ผ           โ–ผ           โ–ผ
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚  Crypto  โ”‚ โ”‚ History  โ”‚ โ”‚  Access  โ”‚
   โ”‚ AES-256  โ”‚ โ”‚  Audit   โ”‚ โ”‚ Control  โ”‚
   โ”‚  scrypt  โ”‚ โ”‚   Log    โ”‚ โ”‚  RBAC    โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚
                     โ–ผ
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚  .envault.jsonโ”‚
              โ”‚  (encrypted)  โ”‚
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Encryption Details

Password Mode:
  password โ”€โ”€โ–ถ scrypt(N=16384, r=8, p=1) โ”€โ”€โ–ถ 256-bit key โ”€โ”€โ–ถ AES-256-GCM
                    + 32-byte random salt        + 12-byte random nonce

Key Mode:
  base64 key โ”€โ”€โ–ถ decode โ”€โ”€โ–ถ 256-bit key โ”€โ”€โ–ถ AES-256-GCM
                                              + 12-byte random nonce

Each variable is independently encrypted with its own nonce, so identical values produce different ciphertexts.


๐Ÿ“ก API Reference

Vault Operations

from envault.vault import Vault
from envault.crypto import generate_key

# Create with password
vault = Vault(path="secrets.json", password="my-password")

# Create with key
key = generate_key()
vault = Vault(path="secrets.json", key=key)

# Set variables
vault.set("DB_URL", "postgres://...", "prod", description="Main DB", tags=["database"])
vault.bulk_set({"API_KEY": "sk-...", "SECRET": "val"}, "dev")

# Get variables
value = vault.get("DB_URL", "prod")
all_vars = vault.get_all("prod")

# Search and filter
results = vault.search("DB", environment="prod")
db_vars = vault.filter_by_tag("database", "prod")

# Environment management
vault.copy_environment("dev", "staging")
envs = vault.list_environments()

# Rotation
vault.rotate_password("new-password")
vault.rotate_key(generate_key())

# Save and load
vault.save()
vault.load()

Environment Inheritance

from envault.inheritance import InheritanceConfig, resolve_variables

config = InheritanceConfig()
config.set_parent("staging", "default")
config.set_parent("prod", "staging")

# Resolves: default -> staging -> prod (later overrides earlier)
resolved = resolve_variables(vault, "prod", config)

Variable Interpolation

from envault.template import interpolate

variables = {"DB_USER": "admin", "DB_PASS": "secret", "DB_HOST": "localhost"}
result = interpolate("postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/mydb", variables)
# => "postgres://admin:secret@localhost/mydb"

Access Control

from envault.access import AccessControl, AccessEntry, Role, Permission

ac = AccessControl()
ac.add_entry(AccessEntry(identity="dev-team", role=Role.EDITOR, environments=["dev", "staging"]))
ac.add_entry(AccessEntry(identity="ops-team", role=Role.ADMIN))

ac.check_permission("dev-team", Permission.WRITE, environment="dev")  # OK
ac.check_permission("dev-team", Permission.WRITE, environment="prod")  # Raises AccessDeniedError

Validation

from envault.validator import ValidationSchema, ValidationRule, RuleType

schema = ValidationSchema()
schema.add_rule(ValidationRule(variable="DATABASE_URL", rule_type=RuleType.URL))
schema.add_rule(ValidationRule(variable="PORT", rule_type=RuleType.PORT))
schema.add_rule(ValidationRule(variable="EMAIL", rule_type=RuleType.EMAIL))

errors = schema.validate({"DATABASE_URL": "not-a-url", "PORT": "99999"})

Export Formats

from envault.exporter import export_to_format, ExportFormat, inject_to_process

# Export as dotenv
content = export_to_format(variables, ExportFormat.DOTENV)

# Export as Kubernetes Secret
content = export_to_format(variables, ExportFormat.K8S_SECRET, name="app-secrets")

# Run subprocess with injected vars
exit_code = inject_to_process(variables, ["python", "app.py"])

๐Ÿ”ง How It Works

  1. Initialization -- envault init creates a .envault.json file encrypted with your password or key
  2. Encryption -- Each variable value is independently encrypted using AES-256-GCM with a unique nonce
  3. Key Derivation -- Passwords are run through scrypt (N=16384, r=8, p=1) with a random 32-byte salt
  4. Storage -- The vault file is a JSON document with encrypted payloads (ciphertext + nonce + salt)
  5. Atomic Writes -- Saves write to a temp file first, then atomically rename to prevent corruption
  6. Multi-Environment -- Variables are organized by environment name, each independently accessible
  7. Inheritance -- Child environments inherit from parents; prod can override staging which inherits default
  8. Export -- Decrypted variables are formatted into the target format (dotenv, shell, docker, k8s, etc.)
  9. Injection -- envault run decrypts variables and passes them as environment to a subprocess

โ“ Troubleshooting

"Decryption failed: incorrect password or corrupted data"

  • Verify you are using the same password/key that was used to create the vault
  • If using environment variables, check ENVAULT_KEY or ENVAULT_PASSWORD is set correctly

Vault File in Git

The .envault.json file is safe to commit -- all values are encrypted. But add it to .gitignore if you prefer:

echo ".envault.json" >> .gitignore

Team Sharing

# Option 1: Password (share out of band)
envault init  # Enter password
# Share password via secure channel

# Option 2: Key (share the key)
envault init --key
# Output: base64-key-here
# Share key via secure channel, set ENVAULT_KEY env var

Migration Between Password and Key

Currently, you need to export and re-import:

envault export -e dev -f json -o backup.json
envault init --key --path new-vault.json
envault import backup.json -e dev --path new-vault.json

๐Ÿงช Testing

# Install dev dependencies
pip install -e ".[dev]"

# Run all 698 tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=envault --cov-report=term-missing

# Run specific module tests
pytest tests/test_vault.py -v
pytest tests/test_crypto.py -v
pytest tests/test_access.py -v

License

MIT

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

jsleekr_envault-0.1.0.tar.gz (78.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jsleekr_envault-0.1.0-py3-none-any.whl (49.8 kB view details)

Uploaded Python 3

File details

Details for the file jsleekr_envault-0.1.0.tar.gz.

File metadata

  • Download URL: jsleekr_envault-0.1.0.tar.gz
  • Upload date:
  • Size: 78.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for jsleekr_envault-0.1.0.tar.gz
Algorithm Hash digest
SHA256 66b743a51d37315c4db72ac14714ea67886c611372be528e3aeebdde010e2d5e
MD5 13707812b92fd0f1c180c6ab0cddb596
BLAKE2b-256 0751929f876b9c47c766e0bdad6ffdc6e8d2176e2ff1c78571abcf97bf2029b1

See more details on using hashes here.

File details

Details for the file jsleekr_envault-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for jsleekr_envault-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 53a0d081cb5352de49aa0d158a07a110504f51ae84bd8d4c6dd1e854321ca268
MD5 8cbbd6571d37f274c98c032dc1ac155c
BLAKE2b-256 50121ca91109ae92f928fe1771129ffbde967dd4c1746a052bf0ed26f83f4389

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page