How Encryption Works
Overview
dotsec uses per-value envelope encryption: each secret is encrypted individually with a data encryption key (DEK), so .sec files are git-mergeable — changing one secret only affects that line.
Two providers are supported:
- Local (default) — age (X25519 + ChaCha20-Poly1305) keypair, no cloud account needed
- AWS KMS — IAM-controlled access, CloudTrail audit logs, enterprise teams
Local encryption (default)
dotsec uses age for key management. Each .sec file has a corresponding keypair.
How it works
- On first use, generate an X25519 keypair → store private key in
.sec.key - Generate a random AES-256 DEK
- Wrap the DEK using age (X25519 + ChaCha20-Poly1305) with the public key
- Encrypt each secret value locally with AES-256-GCM using the DEK
- Store the age-wrapped DEK as
__DOTSEC_KEY__in the.secfile
On decryption, load the private key (from DOTSEC_PRIVATE_KEY env var or .sec.key file), unwrap the DEK, decrypt each ENC[...] value locally.
What the .sec file looks like
Key file
The private key is stored in .sec.key as a plain age identity string:
Key discovery order (checked in this order):
DOTSEC_PRIVATE_KEYenvironment variable<sec-file>.keyfile alongside the.secfile
For CI/CD, use the env var — no file writes needed:
AWS KMS
For enterprise teams needing IAM-controlled access and CloudTrail audit logs.
How it works
dotsec uses AWS KMS envelope encryption:
- Request a data key from KMS (
GenerateDataKeywith AES-256) - KMS returns both a plaintext DEK and a KMS-wrapped copy
- Encrypt each secret value locally with AES-256-GCM using the plaintext DEK
- Store the KMS-wrapped DEK as
__DOTSEC_KEY__in the.secfile - Discard the plaintext DEK
On decryption, KMS unwraps the DEK first (Decrypt), then each ENC[...] value is decrypted locally. The actual secret data never leaves your machine — only the wrapped key touches KMS.
Setup
See Setup → AWS KMS for configuration steps.
What the .sec file looks like
Ciphertext format
Every ENC[...] value contains:
- Commitment — HMAC-SHA256 of the DEK, verified before decryption to detect wrong-key attempts early
- Nonce — random 12 bytes per value (no nonce reuse even if the value is unchanged)
- Padding — plaintext is padded to 64-byte blocks with 0–1 random extra blocks to hide length
Git mergeability
Because each value is encrypted independently, two developers can change different secrets in the same .sec file and merge without conflicts:
Only the lines that were actually modified show up in the diff. The __DOTSEC_KEY__ stays the same as long as the key isn't rotated.
Key rotation
Rotate the DEK without changing any plaintext values:
This decrypts all values with the old DEK, generates a new DEK (local: new random DEK wrapped with the same age key; KMS: new data key from KMS), and re-encrypts everything. The __DOTSEC_KEY__ line is updated.
Use this periodically or after a suspected key compromise. For a full key compromise (private key leaked), generate a new keypair first: