> ## Documentation Index
> Fetch the complete documentation index at: https://encryptd.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# How encryptd Encrypts and Decrypts .env Files Securely

> A technical walkthrough of how encryptd uses AES-256-GCM, PBKDF2 key derivation, and a Rust native addon to encrypt and decrypt .env files.

encryptd uses a two-layer cryptographic approach to protect your `.env` files. First, it derives a strong 256-bit key from your passphrase using PBKDF2-HMAC-SHA256, so the raw passphrase never touches the cipher directly. Then it encrypts the file content with AES-256-GCM, an authenticated encryption algorithm that simultaneously protects confidentiality and detects any tampering. The entire implementation runs in a Rust native addon compiled for each supported platform.

## Encryption Flow

When you run `encryptd encrypt`, the following steps execute inside the Rust native addon:

<Steps>
  <Step title="Generate a random salt">
    The Rust runtime calls `OsRng.fill_bytes` to generate **16 bytes of cryptographically random salt**. This salt is unique to every encryption operation, meaning the same `.env` file encrypted twice will produce completely different output.
  </Step>

  <Step title="Derive a 256-bit key with PBKDF2">
    Your passphrase and the fresh salt are fed into **PBKDF2-HMAC-SHA256** with **100,000 iterations**, producing a 32-byte (256-bit) key. The high iteration count makes brute-force attacks against the passphrase computationally expensive.
  </Step>

  <Step title="Generate a random nonce">
    `Aes256Gcm::generate_nonce` produces a **12-byte (96-bit) nonce** using `OsRng`. The nonce ensures that even if you encrypt the same plaintext with the same key, the ciphertext will differ each time.
  </Step>

  <Step title="Encrypt with AES-256-GCM">
    The plaintext is encrypted using the derived key and nonce. AES-256-GCM outputs the **ciphertext** alongside a **16-byte authentication tag** appended to the end of the raw output. The Rust code separates the last 16 bytes as the tag.
  </Step>

  <Step title="Hex-encode and write as JSON">
    All binary fields salt, nonce (stored as `iv`), ciphertext, and auth tag are hex-encoded and written to a JSON payload on disk.
  </Step>
</Steps>

### Output: the `.env.enc` file

After encryption, your encrypted environment file looks like this:

```json theme={null}
{
  "version": 1,
  "salt": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "iv": "0123456789abcdef01234567",
  "content": "deadbeefcafebabe...",
  "tag": "cafebabe0123456789abcdef01234567"
}
```

Every field in the payload has a defined role:

| Field     | Size                    | Description                         |
| --------- | ----------------------- | ----------------------------------- |
| `version` | —                       | Schema version (currently `1`)      |
| `salt`    | 16 bytes (32 hex chars) | Random salt used for key derivation |
| `iv`      | 12 bytes (24 hex chars) | Random nonce used by AES-256-GCM    |
| `content` | variable                | Hex-encoded ciphertext              |
| `tag`     | 16 bytes (32 hex chars) | GCM authentication tag              |

## Decryption Flow

When you run `encryptd decrypt`, the process is the exact reverse:

<Steps>
  <Step title="Parse the JSON payload">
    encryptd reads the `.env.enc` file and parses the JSON structure, extracting `salt`, `iv`, `content`, and `tag`.
  </Step>

  <Step title="Hex-decode all fields">
    Each hex string is decoded back to raw bytes. Any malformed hex causes an immediate error with a descriptive message.
  </Step>

  <Step title="Re-derive the same key">
    PBKDF2-HMAC-SHA256 runs again with your passphrase and the stored salt. Because the salt is identical to the one used during encryption, the derived key is identical no key material is ever stored on disk.
  </Step>

  <Step title="Decrypt and verify the auth tag">
    AES-256-GCM decrypts the ciphertext and **automatically verifies the 16-byte authentication tag** in a single operation. If the file has been tampered with, or if you provide the wrong passphrase, decryption fails immediately with an error no partial output is ever returned.
  </Step>

  <Step title="Output UTF-8 plaintext">
    The decrypted bytes are validated as UTF-8 and returned as the plaintext content of your original `.env` file.
  </Step>
</Steps>

## Why the Same Input Produces Different Output

Each call to `encrypt_env` independently generates a fresh 16-byte salt and a fresh 12-byte nonce from the operating system's cryptographically secure random number generator (`OsRng`). Because the PBKDF2 key derivation includes the salt, and because AES-256-GCM uses the nonce as an input to the cipher, encrypting the same `.env` twice even with the same passphrase will always produce a distinct `salt`, `iv`, `content`, and `tag`. This is intentional: it prevents an attacker from detecting whether two encrypted files have the same plaintext.

## The Rust Native Addon

encryptd's cryptographic core is written in Rust and compiled to a platform-specific `.node` binary. The compiled addon is loaded by the Node.js package at runtime you never execute JavaScript crypto. Every sensitive operation runs in native Rust code with no JavaScript layer in the cryptographic path.
