encryptd
- Rust 1.85+
- TypeScript 5+
- napi-rs
- aes-gcm crate
- PBKDF2-HMAC-SHA256
- pnpm
- Vitest
- GitHub Actions

Rust-powered AES-256-GCM environment file encryption as a native Node.js addon. CLI + library, zero dependencies, cross-platform.
Challenge
Managing .env files across environments (local, staging, production) and teams is a security risk. Committing plaintext secrets to git is dangerous. Existing solutions:
- Require external services (AWS KMS, HashiCorp Vault, Doppler) adds infrastructure complexity
- Use symmetric encryption in pure JS slower, no hardware acceleration, larger attack surface
- Lack a simple CLI + library dual interface
- Don't provide cross-platform native binaries out of the box
Goal: A zero-dependency, cross-platform, AES-256-GCM encrypted .env solution that works as both a CLI tool and a library, with native performance via Rust.



Solution
encryptd A Rust native addon (via napi-rs) that encrypts/decrypts .env files using AES-256-GCM with PBKDF2 key derivation. Published to GitHub Packages, installable via pnpm/npm.
How It Works
| Step | Encryption Process |
|---|---|
| 1 | Generate random 16-byte salt |
| 2 | PBKDF2-HMAC-SHA256 (100,000 iterations) derives 32-byte key |
| 3 | Generate random 96-bit nonce |
| 4 | AES-256-GCM encrypt → produces ciphertext + auth tag |
| 5 | Hex-encode salt, nonce, ciphertext, auth tag → write as JSON |
| Step | Decryption Process |
|---|---|
| 1 | Parse JSON, hex-decode all fields |
| 2 | PBKDF2 derives identical key from passphrase + salt |
| 3 | AES-256-GCM decrypt (auth tag verified automatically) |
| 4 | Output plaintext to stdout |
Each encryption uses a fresh random nonce → same input produces different output every time (semantic security).
CLI Usage
# Encrypt .env → .env.enc
ENV_PASSPHRASE="your-secret" npx encryptd encrypt
# Decrypt .env.enc → stdout (pipe to file or eval)
ENV_PASSPHRASE="your-secret" npx encryptd decrypt
# Custom paths
ENV_PASSPHRASE="s3cr3t" npx encryptd encrypt .env.prod .env.prod.enc
ENV_PASSPHRASE="s3cr3t" npx encryptd decrypt .env.prod.encLibrary Usage
import { encrypt, decrypt } from '@vernonthedev/encryptd'
import { readFileSync, writeFileSync } from 'fs'
// Encrypt
const plaintext = readFileSync('.env', 'utf-8')
const encrypted = await encrypt(plaintext, process.env.ENV_PASSPHRASE!)
writeFileSync('.env.enc', encrypted)
// Decrypt
const encrypted = readFileSync('.env.enc', 'utf-8')
const plaintext = await decrypt(encrypted, process.env.ENV_PASSPHRASE!)
// Use plaintext...Output Format (.env.enc)
{
"salt": "a1b2c3d4e5f6...",
"nonce": "f0e1d2c3b4a5...",
"ciphertext": "9f8e7d6c5b4a...",
"tag": "1a2b3c4d5e6f..."
}All fields hex-encoded. Portable across platforms.
Tech Stack
| Layer | Technology |
|---|---|
| Native Core | Rust 1.85+, aes-gcm, pbkdf2, napi-rs |
| Bindings | napi-rs (zero-copy FFI, TypeScript types auto-generated) |
| CLI | TypeScript, commander.js |
| Build | napi build (cross-compiles 4 targets), tsc |
| Test | Vitest (unit + integration) |
| CI/CD | GitHub Actions (build matrix: macOS ARM64/x64, Linux x64, Windows x64) |
| Registry | GitHub Packages (scoped @vernonthedev/encryptd) |


Platform Support (Prebuilt Binaries)
| Target | Binary | CI Runner |
|---|---|---|
| macOS ARM64 (Apple Silicon) | encryptd-darwin-arm64.node | macos-latest (M1) |
| macOS x64 (Intel) | encryptd-darwin-x64.node | macos-latest |
| Linux x64 GNU | encryptd-linux-x64-gnu.node | ubuntu-latest |
| Windows x64 MSVC | encryptd-win32-x64-msvc.node | windows-latest |
No Rust toolchain required for consumers pnpm add downloads the correct .node binary automatically.
Security Design
| Property | Implementation |
|---|---|
| Algorithm | AES-256-GCM (authenticated encryption) |
| Key Derivation | PBKDF2-HMAC-SHA256, 100,000 iterations |
| Salt | 16 bytes, cryptographically random per encryption |
| Nonce | 96 bits, cryptographically random per encryption |
| Auth Tag | 128-bit, verified on decrypt (tamper-proof) |
| Memory Safety | Rust no buffer overflows, use-after-free |
| Dependencies | Minimal: aes-gcm, pbkdf2, napi-rs, serde |
My Role
Solo Developer / Maintainer
- Designed the encryption scheme (AES-256-GCM + PBKDF2 parameters)
- Built the Rust native addon with napi-rs FFI boundaries, error handling, memory management
- Implemented TypeScript bindings with full type safety (auto-generated
.d.tsvia napi-rs) - Created CLI with
commander.jsargument parsing, env var handling, stdin/stdout streaming - Configured GitHub Actions matrix build for 4 targets with artifact upload to GitHub Packages
- Published to GitHub Packages with Changesets for versioning/changelog
- Wrote comprehensive tests (Vitest) covering encrypt/decrypt roundtrips, wrong passphrase, corrupted files, CLI integration
Installation
pnpm add @vernonthedev/encryptd
# or
npm install @vernonthedev/encryptdRequires
@vernonthedev/encryptdscope access to GitHub Packages, or build from source.
Development
pnpm install
pnpm napi-build # Compile Rust → native binaries (4 targets)
pnpm build # Compile TypeScript
pnpm test # Vitest unit + integration testsLinks
Zero-dep, native-speed .env encryption. Rust core, TypeScript API, works everywhere Node runs.

