Security Model
How xenvsync protects your secrets at rest and in transit.
Overview
xenvsync uses AES-256-GCM(Galois/Counter Mode), an authenticated encryption algorithm from Go's standard crypto/aes and crypto/cipher packages. No third-party cryptography libraries are used.
Encryption Properties
| Algorithm | AES-256-GCM (authenticated encryption with associated data) |
| Key size | 256 bits (32 bytes), generated via crypto/rand |
| Key encoding | Hex-encoded for safe storage in .xenvsync.key |
| Nonce size | 96 bits (12 bytes), fresh random nonce per encryption |
| Auth tag | 128 bits (16 bytes), appended by GCM — detects any tampering |
| Ciphertext layout | [nonce (12 B) ‖ ciphertext ‖ GCM tag (16 B)] |
| Vault format | Base64 with header/footer markers, 76-char line wrapping |
Key Management
Generation
Keys are generated using Go's crypto/rand.Reader, which sources entropy from the OS CSPRNG (/dev/urandom on Linux, CryptGenRandom on Windows).
Storage
The key is written to .xenvsync.key with file permissions 0600 (owner read/write only). Permissions are validated on every operation.
Isolation
The key is never embedded in the vault output, logged, or printed to stdout. It is automatically added to .gitignore during init.
Sharing
In V1, the symmetric key must be shared out-of-band (secure messaging, password manager, etc.). V2 will introduce asymmetric cryptography for zero-trust team sharing.
Nonce Handling
A fresh 12-byte random nonce is generated for every call to xenvsync push. This means encrypting the same .env file twice produces completely different ciphertext, which is important for:
- Security — prevents ciphertext analysis across multiple versions
- Git diffs— the entire vault changes on each push, so it's clear when secrets were updated
Tamper Detection
GCM provides built-in authentication. If any byte of the vault file is modified (accidentally or maliciously), decryption fails with an authentication error. This protects against:
In-Memory Injection
The xenvsync run command decrypts the vault entirely in memory and passes secrets to the child process via its environment block. The plaintext is never written to a file. This reduces the attack surface compared to writing a .env file, which could be:
- Read by other processes on the same machine
- Persisted in filesystem snapshots or backups
- Accidentally committed to Git
Threat Model
| Threat | Mitigation |
|---|---|
| Secrets committed to Git | .env added to .gitignore on init; vault is encrypted |
| Key file leaked | 0600 permissions; auto-gitignored; permission warnings on load |
| Vault file tampered | GCM authentication tag rejects modified ciphertext |
| Plaintext .env on disk | Use `run` command for in-memory injection instead |
| Weak encryption key | 256-bit key from OS CSPRNG; validated on decode |
Reporting Vulnerabilities
If you discover a security vulnerability, please report it privately via GitHub Security Advisories rather than opening a public issue. We take all reports seriously and will respond promptly.