Attacker gets ciphertext, not secret values
Your database stores encrypted blobs in value_encrypted. Without the decrypt key, those values are unreadable.
Security architecture-first
ENVCRYPT is designed so your server stores ciphertext, not secrets. Decryption requires your master password, which never leaves your browser.
Browser-side encryption · No standalone app password
Security note: strong protection depends on a strong master password. Weak passwords reduce resistance to offline guessing.
DATABASE_URL
q5q7f...:fNw8Y...
STRIPE_SECRET_KEY
YjRkY...:V2j3n...
OPENAI_API_KEY
c2Fsd...:x81aa...
NEXT_PUBLIC_APP_URL
c2lnb...:a9s2m...
plaintext is never written to value_encrypted
decryption key is not stored in the database
Breach scenario
Your database stores encrypted blobs in value_encrypted. Without the decrypt key, those values are unreadable.
The key is derived in the browser from your master password and user ID, then kept in memory only.
An attacker would still need to crack your master password against Argon2id before AES-256-GCM decryption becomes possible.
Server can see
Server cannot see
Implementation details
How it works
Sign in with Google or GitHub
Authentication identifies you. It does not decrypt your secrets.
Create your master password
Argon2id runs in your browser and derives your AES key locally.
Save environment variables
Each value is encrypted on-device before your app sends it to the API.
Export when needed
Decryption happens in-browser, then you download a clean .env file.
secure save path
const key = await deriveKey(masterPassword, userId)
const valueEncrypted = await encryptValue(secretValue, key)
await fetch('/api/variables', {
method: 'POST',
body: JSON.stringify({ key: 'DATABASE_URL', valueEncrypted })
})
// API rejects plaintext 'value' fieldsStart secure by default
Keep plaintext out of storage. Keep decryption keys in the browser. Keep operational security simple.
Start with GoogleGoogle and GitHub OAuth supported.