Security
Security model, threat model, and encryption details for EnvCat.
Security Overview
EnvCat is designed with a local-first, zero-trust security model. Your secrets stay on your infrastructure and are encrypted at multiple layers.
Core security principles:
- Local-first: No cloud dependencies, no third-party trust
- Encryption at rest: Bundle values encrypted in SQLite
- End-to-end encryption: Server-to-CLI encryption via sealed boxes
- Ephemeral approvals: Single-use requests with TTL
- No plaintext storage: Server never stores plaintext secrets
Encryption Layers
EnvCat uses two layers of encryption:
Layer 1: At-Rest Encryption (Bundle Storage)
Purpose: Protect bundle values in SQLite database.
Technology: libsodium secretbox (XSalsa20 + Poly1305 MAC)
How it works:
- Bundle items stored in SQLite
- Values encrypted with server-side key (
BUNDLE_KMS_KEY) - Decryption happens in-memory only
- Encrypted values include authentication tag (tamper-proof)
Key storage: Environment variable BUNDLE_KMS_KEY (32-byte base64)
Security properties:
- Authenticated encryption (detects tampering)
- Nonce-based (unique nonce per encryption)
- Server-side key required to decrypt
Trade-off: Server has access to decryption key. This enables server-side re-encryption for CLI but means the server is trusted.
Layer 2: End-to-End Encryption (CLI Communication)
Purpose: Ensure only the CLI can decrypt secrets, never the server in plaintext.
Technology: libsodium sealed boxes (X25519 + XSalsa20-Poly1305)
How it works:
- CLI generates ephemeral keypair (per request)
- CLI sends public key to server with request
- Server loads bundle, decrypts at-rest, encrypts to CLI public key
- Server stores ciphertext in Redis
- CLI retrieves ciphertext, decrypts with private key
- Private key destroyed after use
Security properties:
- Only CLI can decrypt (server never sees plaintext)
- Ephemeral keypairs (new keypair per request)
- Forward secrecy (old requests can't be decrypted)
- Anonymous encryption (server can't prove who requested)
Crypto primitives:
X25519- Elliptic curve Diffie-HellmanXSalsa20-Poly1305- Authenticated encryption- Sealed box - One-way encryption (encrypt-only, decrypt requires private key)
Threat Model
What EnvCat Protects Against
✅ Accidental git leaks
- Secrets stored encrypted, not in
.envfiles - Approval flow prevents copy-paste mistakes
- CLI warns if writing
.envin git repo
✅ Database compromise
- Bundle values encrypted at rest
- Attacker needs
BUNDLE_KMS_KEYto decrypt - No plaintext secrets in database
✅ Network eavesdropping (local)
- End-to-end encryption (CLI to server)
- Ephemeral requests (single-use, TTL)
- No long-lived tokens
✅ Unauthorized access to bundles
- Approval workflow (user must approve in browser)
- TTL expiration (requests expire after 5 min)
- Single-use (requests deleted after retrieval)
✅ Insider threat (limited)
- Server operator can't see plaintext in E2E flow
- Database admin can't read encrypted values without key
- Redis only stores ephemeral ciphertext
What EnvCat Does NOT Protect Against
❌ Compromised server
- Server has
BUNDLE_KMS_KEYand can decrypt at-rest bundles - Mitigations: Use envelope encryption (future), hardware security module (future)
❌ Compromised client (CLI)
- Malware on client can steal private key from memory
- Mitigations: Use secure hardware (future), attestation (future)
❌ Process environment visibility
- Variables visible in
/proc/<pid>/environ(Linux) - Child processes inherit environment
- Mitigations: Use ephemeral shells, unset after use
❌ Logging and crash dumps
- Variables may leak into logs or core dumps
- Mitigations: Never log values (server-side), disable core dumps (client-side)
❌ Physical access
- Attacker with physical access can extract keys
- Mitigations: Disk encryption, secure boot
❌ Social engineering
- User approves malicious request in browser
- Mitigations: User education, audit logs (future)
Security Best Practices
Server-Side
1. Generate Strong Encryption Key
⚠️ CRITICAL: Change BUNDLE_KMS_KEY from default in production.
# Generate secure 32-byte key
openssl rand -base64 32
# Set in docker-compose.yml or .env
BUNDLE_KMS_KEY=<generated-key>
2. Protect Environment Variables
# Secure docker-compose.yml
chmod 600 docker-compose.yml
# Or use .env file
echo "BUNDLE_KMS_KEY=..." > .env
chmod 600 .env
3. Enable HTTPS (Production)
Local dev uses HTTP. Production should use HTTPS:
# docker-compose.yml (production)
web:
environment:
- NODE_ENV=production
- HTTPS=true
4. Restrict Network Access
# docker-compose.yml
services:
api:
ports: [] # Don't expose Flask API to host
redis:
ports: [] # Don't expose Redis to host
5. Regular Backups
Backup SQLite database:
docker compose exec api cp /data/app.db /data/backup.db
docker compose cp api:/data/backup.db ./backup-$(date +%Y%m%d).db
Encrypt backups:
gpg --symmetric --cipher-algo AES256 backup.db
Client-Side (CLI)
1. Secure .env Files
# Always use restricted permissions
constants request --write .env
ls -l .env
# -rw------- 1 user user (0600 perms)
2. Add .env to .gitignore
echo ".env" >> .gitignore
git add .gitignore
3. Unset Variables After Use
# Load secrets
eval "$(constants request)"
# Use secrets
./run-command
# Unset when done
unset API_KEY DB_URL
4. Use Ephemeral Shells
# Start subshell
bash -c 'eval "$(constants request)" && ./run-command'
# Secrets gone when subshell exits
5. Disable Core Dumps
# Prevent secrets leaking into core dumps
ulimit -c 0
Operational
1. Rotate Secrets Regularly
# Update bundle item
curl -X POST "http://localhost:8888/api/v1/bundles/$BUNDLE_ID/items" \
-d '{"key":"API_KEY","value":"new_key_123"}'
2. Monitor Access (Future: M10)
Audit logs will track:
- Who requested secrets
- Which bundles were accessed
- When approvals occurred
3. Least Privilege
- Create separate bundles per environment (dev, staging, prod)
- Separate bundles per service/app
- Don't share production bundles widely
4. Review Bundles
Periodically review:
- Which bundles exist
- Who has access
- Are secrets still needed
Compliance Considerations
Data Residency
Local-first = Data stays on your infrastructure.
- GDPR: Data in EU stays in EU
- SOC 2: Control over data location
- PCI DSS: Secrets on compliant infrastructure
Encryption Standards
- At-rest: XSalsa20-Poly1305 (libsodium)
- In-transit: HTTPS (TLS 1.3 recommended)
- Key length: 256-bit keys
Audit Requirements
Current (M7): Basic logging (Flask access logs).
Future (M10): Comprehensive audit logs:
- User actions (create, read, update, delete)
- Approval events (who approved what)
- Access patterns (which bundles accessed when)
Cryptography Details
Libraries
- libsodium: Audited cryptography library
- Python:
PyNaCl(wrapper around libsodium) - Go:
golang.org/x/crypto/nacl(implementation)
- Python:
- Algorithm suite: NaCl (Networking and Cryptography Library)
Encryption Modes
Secretbox (At-Rest):
- Cipher: XSalsa20 (stream cipher)
- MAC: Poly1305 (authentication)
- Nonce: 24 bytes (random, unique per encryption)
- Key: 32 bytes (from
BUNDLE_KMS_KEY)
Sealed Box (End-to-End):
- Key exchange: X25519 (Curve25519 Diffie-Hellman)
- Cipher: XSalsa20-Poly1305
- Ephemeral keypair: Generated per request
- Public key: 32 bytes
- Private key: 32 bytes (memory-only)
Key Management
Current:
- Server-side symmetric key (env var)
- Ephemeral CLI keypairs (in-memory)
Future (M8+):
- Envelope encryption (per-bundle data keys + master key)
- HSM support (hardware security module)
- Cloud KMS integration (AWS KMS, GCP KMS, Azure Key Vault)
Reporting Security Issues
Found a security vulnerability?
DO NOT open a public issue.
Email: security@EnvCat (future)
For now: Open a private security advisory on GitHub:
- Go to repository → Security → Advisories → New draft
What to include:
- Description of vulnerability
- Steps to reproduce
- Impact assessment
- Suggested fix (if any)
Response timeline:
- Acknowledgment: Within 48 hours
- Initial assessment: Within 7 days
- Fix timeline: Depends on severity
Responsible disclosure: We'll credit you in the security advisory and release notes (unless you prefer to remain anonymous).
TODO: Security Sign-off - Confirm with security owner that crypto terminology ("sealed boxes", "secretbox") and implementation details match current codebase and security approvals.
Additional Security Resources
- Threat Model - Detailed threat analysis
- At-Rest Encryption - Deep dive into bundle encryption (coming soon)
- Architecture: Data Flow - See how data flows through the system
- SECURITY_NOTES.md - See internal docs/SECURITY_NOTES.md for security notes
External Resources: