Metadata-Version: 2.4
Name: jsleekr-envault
Version: 0.1.0
Summary: Encrypted environment variable management for teams and projects
Author-email: JSLEEKR <93jslee@gmail.com>
License: MIT
Keywords: env,secrets,encryption,environment,dotenv
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=41.0
Requires-Dist: click>=8.1
Requires-Dist: rich>=13.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Dynamic: license-file

<div align="center">

# 🔐 envault

### Encrypted environment variable management for teams and projects

[![GitHub Stars](https://img.shields.io/github/stars/JSLEEKR/envault?style=for-the-badge&logo=github&color=yellow)](https://github.com/JSLEEKR/envault/stargazers)
[![License](https://img.shields.io/badge/license-MIT-blue?style=for-the-badge)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.10+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
[![Tests](https://img.shields.io/badge/tests-698%20passing-brightgreen?style=for-the-badge)](#testing)

<br/>

**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](#-quick-start) | [Features](#features) | [CLI Commands](#-cli-commands) | [Architecture](#-architecture)

</div>

---

## 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

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

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

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

```bash
envault get API_KEY
envault get DATABASE_URL -e prod
```

### `envault list`

List all variables (without values).

```bash
# List all environments
envault list

# List specific environment
envault list -e prod
```

### `envault delete`

Delete a variable.

```bash
envault delete OLD_API_KEY -e dev
```

### `envault export`

Export variables in various formats.

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

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

```bash
# Compare environments
envault diff dev prod

# Show masked values
envault diff dev prod --values
```

### `envault run`

Run a command with injected environment variables.

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

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

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

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

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

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

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

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

```bash
echo ".envault.json" >> .gitignore
```

### Team Sharing

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

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

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