npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bun-secure-env

v0.1.0

Published

Production-grade encrypted environment loader with Zod validation, native AES-256-GCM, and profile merging for Bun.

Readme

bun-secure-env

npm-version bun-version license docs

Encrypted .env loader with AES-256-GCM, Zod schema validation, and Bun-native crypto.

Written in idiomatic, strict-mode TypeScript with no external runtime dependencies other than Zod, and no Node.js compatibility layer requirements.


Features

  • Zero Node.js Shims: Uses Bun's native WebCrypto (crypto.subtle) and Bun-native file system APIs.
  • AES-256-GCM Security: Whole-file encryption with unique 12-byte initialization vectors (IV) per encryption call, and explicit 16-byte authentication tag verification.
  • Profile Merging: Supports environment overrides via BUN_ENV (e.g. .env.production.vault overrides .env.vault).
  • Strict Validation: Bundled with Zod for fail-fast validation. Invalid environment variables throw detailed, formatted error messages listing every faulty field.
  • Zero-Footprint Key Memory: Explicitly zeroes out decoded vault key material in memory immediately after usage.
  • Fully Typed: Infers fully typed environment configurations directly from your Zod schema shape.

Installation

bun add bun-secure-env

Quickstart

1. Generate a Vault Key

Generate a 32-byte cryptographically secure base64-encoded key:

import { generateKey } from "bun-secure-env";

const key = generateKey();
console.log(key); // A 44-character base64 string

Save this key securely. Set it in your environment as VAULT_KEY (e.g. export VAULT_KEY="..." or in your hosting provider's configuration page).

2. Encrypt Your .env File

Create a plaintext .env file:

PORT=8080
DB_URL="mongodb://localhost:27017" # Production Database
DEBUG=false

Encrypt it using the setup script:

import { encryptEnv } from "bun-secure-env";

await encryptEnv({
  input: ".env",
  key: process.env.VAULT_KEY!,
});

This creates .env.vault in the same directory containing version metadata and base64-encoded ciphertext. You can safely commit .env.vault to your git repository. Never commit the raw .env file or your VAULT_KEY.

3. Load & Validate at Startup

Load your environment at your application entry point:

import { createEnv, z } from "bun-secure-env";

const env = await createEnv({
  schema: {
    PORT: z.preprocess((val) => Number(val), z.number().int()),
    DB_URL: z.string().url(),
    DEBUG: z.preprocess((val) => val === "true", z.boolean().default(false)),
  },
});

console.log(env.PORT);    // Number: 8080
console.log(env.DB_URL);  // String: mongodb://localhost:27017
console.log(env.DEBUG);   // Boolean: false

Environment Profiles

If you have different configuration values for different environments, you can create profile-specific vaults.

  1. Create a profile-specific env file, e.g. .env.production.
  2. Encrypt it using encryptEnv:
    await encryptEnv({
      input: ".env.production",
      key: process.env.VAULT_KEY!,
    });
    This generates .env.production.vault.
  3. Set your runtime profile in the shell:
    export BUN_ENV=production
  4. When createEnv() is called, it will decrypt and parse the base .env.vault, decrypt and parse .env.production.vault, merge the production keys over the base keys, and validate the merged result.

API Reference

createEnv<T extends ZodRawShape>(options)

Loads and decrypts your vault configurations, merges profiles, and validates schemas.

  • Options:
    • vault (optional): Path to the base vault file. Defaults to ".env.vault".
    • key (optional): The base64 vault key. Defaults to Bun.env.VAULT_KEY.
    • schema: A Zod raw shape definition (e.g. { PORT: z.string() }).
  • Returns: Promise<z.infer<z.ZodObject<T>>> - The decrypted, merged, and validated environment configuration.
  • Throws:
    • VaultKeyError: If VAULT_KEY is missing or invalid base64.
    • VaultNotFoundError: If the base vault file does not exist.
    • ProfileNotFoundError: If BUN_ENV is set but the corresponding profile vault file does not exist.
    • DecryptionError: If decryption fails (corrupted data, bad key, or tampered auth tag).
    • SchemaError: If the decrypted configuration fails to satisfy the Zod schema.

encryptEnv(options)

Encrypts a plaintext .env file into a secure, versioned .env.vault format.

  • Options:
    • input: Path to the plaintext .env file.
    • output (optional): Output file path. Defaults to input + ".vault".
    • key: The base64 vault key.
  • Returns: Promise<void>
  • Throws:
    • VaultKeyError: If the key is invalid base64 or not 32 bytes when decoded.
    • VaultNotFoundError: If the input file does not exist.

generateKey()

Helper function to generate a 32-byte cryptographically secure random key.

  • Returns: string - Base64 encoded key (exactly 44 characters).

Error Handling

All errors thrown by the library extend BunSecureEnvError. This allows you to catch errors specific to your environment setup at startup:

import { BunSecureEnvError, SchemaError } from "bun-secure-env";

try {
  const env = await createEnv({ schema: { PORT: z.string() } });
} catch (error) {
  if (error instanceof SchemaError) {
    console.error("Missing/invalid variables:", error.message);
    console.error(error.issues); // Raw Zod issues
  } else if (error instanceof BunSecureEnvError) {
    console.error("Secure env error:", error.message);
  }
}

Vault File Format

The .env.vault file uses a simple key-value format containing the cryptographic components necessary for AES-256-GCM verification and decryption:

BSENV_VERSION=1
BSENV_ALGO=aes-256-gcm
BSENV_IV=<base64-encoded-12-byte-iv>
BSENV_TAG=<base64-encoded-16-byte-auth-tag>
BSENV_CIPHER=<base64-encoded-ciphertext>

Vault Key Management Guide

The vault key is the master key to your encrypted environment variables. Maintain key security with the following guidelines:

  1. Never Commit Keys: Do not write the VAULT_KEY to any file checked into source control.
  2. Environment Variable Injection: Set VAULT_KEY as an environment variable in your production hosting, CI/CD systems, or local developer shell.
  3. Key Leak Mitigation: The library decodes the vault key to a Uint8Array in memory, imports it into WebCrypto, and immediately fills the byte array with zeroes (keyBytes.fill(0)) to ensure the raw key material is destroyed from memory.
  4. Different Keys per Profile: You can use the same key or different keys for your overrides. If using different keys, make sure to set the correct key in your runtime container or system prior to execution.

Known Behaviors & Bundling Trade-offs

  • Zod Bundling (v0.1): Currently, Zod is bundled directly into the compiled output. While this provides a zero-setup runtime, it may result in Zod being bundled twice if your application also depends on it. We plan to move Zod to a peer dependency in v0.2.
  • Key-Value Parsing: Any line in a .env file that does not contain a = character is silently ignored. Ensure all your environment variables are correctly formatted as KEY=VALUE.

License

MIT